mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 06:51:58 +00:00
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:
parent
63a292fd86
commit
db00362313
@ -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)),
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
@ -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,
|
||||
|
@ -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|
|
||||
|
@ -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 }
|
||||
|
@ -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 }
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
15
src/test/compile-fail/issue-4736.rs
Normal file
15
src/test/compile-fail/issue-4736.rs
Normal 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`
|
||||
}
|
@ -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() { }
|
||||
|
Loading…
Reference in New Issue
Block a user