Make typechecker compositional

The typechecker previously passed around a boolean return flag to
indicate whether it saw something with type _|_ (that is, something
it knows at compile-time will definitely diverge) and also had some
manual checks for the `ty_err` pseudo-type that represents a previous
type error. This was because the typing rules implemented by the
typechecker didn't properly propagate _|_ and ty_err. I fixed it.

This also required changing expected error messages in a few tests,
as now we're printing out fewer derived errors -- in fact, at this
point we should print out no derived errors, so report any that
you see (ones that include "[type error]") as bugs.
This commit is contained in:
Tim Chevalier 2013-03-20 12:55:18 -07:00
parent 63a292fd86
commit db00362313
13 changed files with 819 additions and 500 deletions

View File

@ -28,6 +28,7 @@ use util::ppaux::{expr_repr, ty_to_str};
use core::libc::c_uint;
use syntax::{ast, ast_util, codemap, ast_map};
use util::ppaux::ty_to_str;
pub fn const_lit(cx: @CrateContext, e: @ast::expr, lit: ast::lit)
-> ValueRef {
@ -45,7 +46,8 @@ pub fn const_lit(cx: @CrateContext, e: @ast::expr, lit: ast::lit)
C_integral(T_uint_ty(cx, t), i as u64, False)
}
_ => cx.sess.span_bug(lit.span,
~"integer literal doesn't have a type")
fmt!("integer literal has type %s (expected int or uint)",
ty_to_str(cx.tcx, lit_int_ty)))
}
}
ast::lit_float(fs, t) => C_floating(/*bad*/copy *fs, T_float_ty(cx, t)),

View File

@ -310,6 +310,7 @@ enum tbox_flag {
needs_infer = 4,
has_regions = 8,
has_ty_err = 16,
has_ty_bot = 32,
// a meta-flag: subst may be required if the type has parameters, a self
// type, or references bound regions
@ -355,9 +356,6 @@ pub pure fn type_needs_infer(t: t) -> bool {
pub pure fn type_has_regions(t: t) -> bool {
tbox_has_flag(get(t), has_regions)
}
pub pure fn type_contains_err(t: t) -> bool {
tbox_has_flag(get(t), has_ty_err)
}
pub pure fn type_def_id(t: t) -> Option<ast::def_id> { get(t).o_def_id }
pub pure fn type_id(t: t) -> uint { get(t).id }
@ -892,9 +890,17 @@ fn mk_t_with_id(cx: ctxt, +st: sty, o_def_id: Option<ast::def_id>) -> t {
flags |= rflags(r);
flags |= get(mt.ty).flags;
}
&ty_nil | &ty_bot | &ty_bool | &ty_int(_) | &ty_float(_) | &ty_uint(_) |
&ty_nil | &ty_bool | &ty_int(_) | &ty_float(_) | &ty_uint(_) |
&ty_estr(_) | &ty_type | &ty_opaque_closure_ptr(_) |
&ty_opaque_box => (),
// You might think that we could just return ty_err for
// any type containing ty_err as a component, and get
// rid of the has_ty_err flag -- likewise for ty_bot (with
// the exception of function types that return bot).
// But doing so caused sporadic memory corruption, and
// neither I (tjc) nor nmatsakis could figure out why,
// so we're doing it this way.
&ty_bot => flags |= has_ty_bot as uint,
&ty_err => flags |= has_ty_err as uint,
&ty_param(_) => flags |= has_params as uint,
&ty_infer(_) => flags |= needs_infer as uint,
@ -914,12 +920,16 @@ fn mk_t_with_id(cx: ctxt, +st: sty, o_def_id: Option<ast::def_id>) -> t {
&ty_tup(ref ts) => for ts.each |tt| { flags |= get(*tt).flags; },
&ty_bare_fn(ref f) => {
for f.sig.inputs.each |a| { flags |= get(a.ty).flags; }
flags |= get(f.sig.output).flags;
flags |= get(f.sig.output).flags;
// T -> _|_ is *not* _|_ !
flags &= !(has_ty_bot as uint);
}
&ty_closure(ref f) => {
flags |= rflags(f.region);
for f.sig.inputs.each |a| { flags |= get(a.ty).flags; }
flags |= get(f.sig.output).flags;
// T -> _|_ is *not* _|_ !
flags &= !(has_ty_bot as uint);
}
}
@ -1465,7 +1475,13 @@ pub fn subst_substs(cx: ctxt, sup: &substs, sub: &substs) -> substs {
pub fn type_is_nil(ty: t) -> bool { get(ty).sty == ty_nil }
pub fn type_is_bot(ty: t) -> bool { get(ty).sty == ty_bot }
pub fn type_is_bot(ty: t) -> bool {
(get(ty).flags & (has_ty_bot as uint)) != 0
}
pub fn type_is_error(ty: t) -> bool {
(get(ty).flags & (has_ty_err as uint)) != 0
}
pub fn type_is_ty_var(ty: t) -> bool {
match get(ty).sty {

View File

@ -28,12 +28,11 @@ use syntax::print::pprust;
pub fn check_match(fcx: @mut FnCtxt,
expr: @ast::expr,
discrim: @ast::expr,
arms: &[ast::arm]) -> bool {
arms: &[ast::arm]) {
let tcx = fcx.ccx.tcx;
let mut bot;
let pattern_ty = fcx.infcx().next_ty_var();
bot = check_expr_has_type(fcx, discrim, pattern_ty);
check_expr_has_type(fcx, discrim, pattern_ty);
// Typecheck the patterns first, so that we get types for all the
// bindings.
@ -51,19 +50,46 @@ pub fn check_match(fcx: @mut FnCtxt,
// Now typecheck the blocks.
let mut result_ty = fcx.infcx().next_ty_var();
let mut arm_non_bot = false;
let mut saw_err = false;
for arms.each |arm| {
let mut guard_err = false;
let mut guard_bot = false;
match arm.guard {
Some(e) => { check_expr_has_type(fcx, e, ty::mk_bool(tcx)); },
Some(e) => {
check_expr_has_type(fcx, e, ty::mk_bool(tcx));
let e_ty = fcx.expr_ty(e);
if ty::type_is_error(e_ty) {
guard_err = true;
}
else if ty::type_is_bot(e_ty) {
guard_bot = true;
}
},
None => ()
}
if !check_block(fcx, &arm.body) { arm_non_bot = true; }
check_block(fcx, &arm.body);
let bty = fcx.node_ty(arm.body.node.id);
saw_err = saw_err || ty::type_is_error(bty);
if guard_err {
fcx.write_error(arm.body.node.id);
saw_err = true;
}
else if guard_bot {
fcx.write_bot(arm.body.node.id);
}
else if !ty::type_is_bot(bty) {
arm_non_bot = true; // If the match *may* evaluate to a non-_|_
// expr, the whole thing is non-_|_
}
demand::suptype(fcx, arm.body.span, result_ty, bty);
}
bot |= !arm_non_bot;
if !arm_non_bot { result_ty = ty::mk_bot(tcx); }
if saw_err {
result_ty = ty::mk_err(tcx);
}
else if !arm_non_bot {
result_ty = ty::mk_bot(tcx);
}
fcx.write_ty(expr.id, result_ty);
return bot;
}
pub struct pat_ctxt {

View File

@ -194,6 +194,10 @@ pub enum TransformTypeFlag {
pub impl LookupContext/&self {
fn do_lookup(&self, self_ty: ty::t) -> Option<method_map_entry> {
let mut self_ty = structurally_resolved_type(self.fcx,
self.self_expr.span,
self_ty);
debug!("do_lookup(self_ty=%s, expr=%s, self_expr=%s)",
self.ty_to_str(self_ty),
expr_repr(self.tcx(), self.expr),

File diff suppressed because it is too large Load Diff

View File

@ -150,7 +150,7 @@ pub fn visit_local(l: @ast::local, &&rcx: @mut Rcx, v: rvt) {
// Note: we do this here rather than in visit_pat because we do
// not wish to constrain the regions in *patterns* in quite the
// same way. `visit_node()` guarantees that the region encloses
// the node in question, which ultimately constraints the regions
// the node in question, which ultimately constrains the regions
// in patterns to enclose the match expression as a whole. But we
// want them to enclose the *arm*. However, regions in patterns
// must either derive from the discriminant or a ref pattern: in
@ -616,7 +616,7 @@ pub mod guarantor {
// expressions, both of which always yield a region variable, so
// mk_subr should never fail.
let rptr_ty = rcx.resolve_node_type(id);
if !ty::type_contains_err(rptr_ty) {
if !ty::type_is_error(rptr_ty) {
debug!("rptr_ty=%s", ty_to_str(rcx.fcx.ccx.tcx, rptr_ty));
let r = ty::ty_region(rptr_ty);
infallibly_mk_subr(rcx, true, span, r, bound);
@ -890,7 +890,7 @@ pub mod guarantor {
}
ast::pat_region(p) => {
let rptr_ty = rcx.resolve_node_type(pat.id);
if !ty::type_contains_err(rptr_ty) {
if !ty::type_is_error(rptr_ty) {
let r = ty::ty_region(rptr_ty);
link_ref_bindings_in_pat(rcx, p, Some(r));
}
@ -899,7 +899,7 @@ pub mod guarantor {
ast::pat_range(*) => {}
ast::pat_vec(ref before, ref slice, ref after) => {
let vec_ty = rcx.resolve_node_type(pat.id);
if !ty::type_contains_err(vec_ty) {
if !ty::type_is_error(vec_ty) {
let vstore = ty::ty_vstore(vec_ty);
let guarantor1 = match vstore {
ty::vstore_fixed(_) | ty::vstore_uniq => guarantor,

View File

@ -712,7 +712,7 @@ pub impl InferCtxt {
let actual_ty = self.resolve_type_vars_if_possible(actual_ty);
// Don't report an error if actual type is ty_err.
if ty::type_contains_err(actual_ty) {
if ty::type_is_error(actual_ty) {
return;
}
let error_str = err.map_default(~"", |t_err|

View File

@ -25,4 +25,11 @@ pub enum e {
pub fn nominal() -> e { e_val }
pub pure fn nominal_eq(e1: e, e2: e) -> bool { true }
impl Eq for e {
pure fn eq(&self, other: &e) -> bool { nominal_eq(*self, *other) }
pure fn ne(&self, other: &e) -> bool { !nominal_eq(*self, *other) }
}
pub fn f() -> int { 10 }

View File

@ -22,6 +22,13 @@ pub enum e {
e_val
}
impl Eq for e {
pure fn eq(&self, other: &e) -> bool { !nominal_neq(*self, *other) }
pure fn ne(&self, other: &e) -> bool { nominal_neq(*self, *other) }
}
pub fn nominal() -> e { e_val }
pub pure fn nominal_neq(e1: e, e2: e) -> bool { false }
pub fn f() -> int { 20 }

View File

@ -8,7 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: mismatched types
struct clam {
x: @int,
y: @int,
@ -21,12 +20,12 @@ struct fish {
fn main() {
let a: clam = clam{x: @1, y: @2};
let b: clam = clam{x: @10, y: @20};
let z: int = a.x + b.y;
let z: int = a.x + b.y; //~ ERROR binary operation + cannot be applied to type `@int`
debug!(z);
fail_unless!((z == 21));
let forty: fish = fish{a: @40};
let two: fish = fish{a: @2};
let answer: int = forty.a + two.a;
let answer: int = forty.a + two.a; //~ ERROR binary operation + cannot be applied to type `@int`
debug!(answer);
fail_unless!((answer == 42));
}

View File

@ -11,12 +11,12 @@
// xfail-fast
// aux-build:crateresolve5-1.rs
// aux-build:crateresolve5-2.rs
// error-pattern:mismatched types
extern mod cr5_1 (name = "crateresolve5", vers = "0.1");
extern mod cr5_2 (name = "crateresolve5", vers = "0.2");
fn main() {
// Nominal types from two multiple versions of a crate are different types
fail_unless!(cr5_1::nominal() == cr5_2::nominal());
fail_unless!(cr5_1::nominal() == cr5_2::nominal()); //~ ERROR mismatched types: expected
}

View File

@ -0,0 +1,15 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct NonCopyable(());
fn main() {
let z = NonCopyable{ p: () }; //~ ERROR structure has no field named `p`
}

View File

@ -12,10 +12,9 @@
extern mod std;
// error-pattern: mismatched types
enum bar { t1((), Option<~[int]>), t2, }
fn foo(t: bar) -> int { match t { t1(_, Some(x)) => { return x * 3; } _ => { fail!(); } } }
// n.b. my change changes this error message, but I think it's right -- tjc
fn foo(t: bar) -> int { match t { t1(_, Some(x)) => { return x * 3; } _ => { fail!(); } } } //~ ERROR binary operation * cannot be applied to
fn main() { }