Skip to content

Commit 42344af

Browse files
committed
Correct handling of if/match, and make explicit computation of
common supertypes. This was breaking with the change to regions because of the (now incorrect) assumpton that our inference code makes, which is that if a <: b succeeds, there is no need to compute the LUB/GLB.
1 parent 9e6d5e1 commit 42344af

File tree

12 files changed

+265
-120
lines changed

12 files changed

+265
-120
lines changed

src/librustc/middle/typeck/check/_match.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ pub fn check_match(fcx: @mut FnCtxt,
3030
arms: &[ast::arm]) {
3131
let tcx = fcx.ccx.tcx;
3232

33-
let pattern_ty = fcx.infcx().next_ty_var();
34-
check_expr_has_type(fcx, discrim, pattern_ty);
33+
let discrim_ty = fcx.infcx().next_ty_var();
34+
check_expr_has_type(fcx, discrim, discrim_ty);
3535

3636
// Typecheck the patterns first, so that we get types for all the
3737
// bindings.
@@ -41,13 +41,20 @@ pub fn check_match(fcx: @mut FnCtxt,
4141
map: pat_id_map(tcx.def_map, arm.pats[0]),
4242
};
4343

44-
for arm.pats.iter().advance |p| { check_pat(&pcx, *p, pattern_ty);}
44+
for arm.pats.iter().advance |p| { check_pat(&pcx, *p, discrim_ty);}
4545
}
4646

47+
// The result of the match is the common supertype of all the
48+
// arms. Start out the value as bottom, since it's the, well,
49+
// bottom the type lattice, and we'll be moving up the lattice as
50+
// we process each arm. (Note that any match with 0 arms is matching
51+
// on any empty type and is therefore unreachable; should the flow
52+
// of execution reach it, we will fail, so bottom is an appropriate
53+
// type in that case)
54+
let mut result_ty = ty::mk_bot();
55+
4756
// Now typecheck the blocks.
48-
let mut result_ty = fcx.infcx().next_ty_var();
49-
let mut arm_non_bot = false;
50-
let mut saw_err = false;
57+
let mut saw_err = ty::type_is_error(discrim_ty);
5158
for arms.iter().advance |arm| {
5259
let mut guard_err = false;
5360
let mut guard_bot = false;
@@ -74,18 +81,22 @@ pub fn check_match(fcx: @mut FnCtxt,
7481
else if guard_bot {
7582
fcx.write_bot(arm.body.node.id);
7683
}
77-
else if !ty::type_is_bot(bty) {
78-
arm_non_bot = true; // If the match *may* evaluate to a non-_|_
79-
// expr, the whole thing is non-_|_
80-
}
81-
demand::suptype(fcx, arm.body.span, result_ty, bty);
84+
85+
result_ty =
86+
infer::common_supertype(
87+
fcx.infcx(),
88+
infer::MatchExpression(expr.span),
89+
true, // result_ty is "expected" here
90+
result_ty,
91+
bty);
8292
}
93+
8394
if saw_err {
8495
result_ty = ty::mk_err();
85-
}
86-
else if !arm_non_bot {
96+
} else if ty::type_is_bot(discrim_ty) {
8797
result_ty = ty::mk_bot();
8898
}
99+
89100
fcx.write_ty(expr.id, result_ty);
90101
}
91102

@@ -647,3 +658,4 @@ pub fn check_pointer_pat(pcx: &pat_ctxt,
647658

648659
#[deriving(Eq)]
649660
enum PointerKind { Managed, Send, Borrowed }
661+

src/librustc/middle/typeck/check/mod.rs

Lines changed: 39 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,7 @@ impl FnCtxt {
848848

849849
pub fn mk_subty(&self,
850850
a_is_expected: bool,
851-
origin: infer::SubtypeOrigin,
851+
origin: infer::TypeOrigin,
852852
sub: ty::t,
853853
sup: ty::t)
854854
-> Result<(), ty::type_err> {
@@ -886,7 +886,7 @@ impl FnCtxt {
886886

887887
pub fn mk_eqty(&self,
888888
a_is_expected: bool,
889-
origin: infer::SubtypeOrigin,
889+
origin: infer::TypeOrigin,
890890
sub: ty::t,
891891
sup: ty::t)
892892
-> Result<(), ty::type_err> {
@@ -1436,27 +1436,42 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
14361436
// A generic function for checking the then and else in an if
14371437
// or if-check
14381438
fn check_then_else(fcx: @mut FnCtxt,
1439-
thn: &ast::blk,
1440-
elsopt: Option<@ast::expr>,
1439+
cond_expr: @ast::expr,
1440+
then_blk: &ast::blk,
1441+
opt_else_expr: Option<@ast::expr>,
14411442
id: ast::node_id,
1442-
_sp: span) {
1443-
let if_t =
1444-
match elsopt {
1445-
Some(els) => {
1446-
let if_t = fcx.infcx().next_ty_var();
1447-
check_block(fcx, thn);
1448-
let thn_t = fcx.node_ty(thn.node.id);
1449-
demand::suptype(fcx, thn.span, if_t, thn_t);
1450-
check_expr_has_type(fcx, els, if_t);
1451-
if_t
1452-
}
1453-
None => {
1454-
check_block_no_value(fcx, thn);
1455-
ty::mk_nil()
1456-
}
1457-
};
1443+
sp: span,
1444+
expected: Option<ty::t>) {
1445+
check_expr_has_type(fcx, cond_expr, ty::mk_bool());
1446+
1447+
let branches_ty = match opt_else_expr {
1448+
Some(else_expr) => {
1449+
check_block_with_expected(fcx, then_blk, expected);
1450+
let then_ty = fcx.node_ty(then_blk.node.id);
1451+
check_expr_with_opt_hint(fcx, else_expr, expected);
1452+
let else_ty = fcx.expr_ty(else_expr);
1453+
infer::common_supertype(fcx.infcx(),
1454+
infer::IfExpression(sp),
1455+
true,
1456+
then_ty,
1457+
else_ty)
1458+
}
1459+
None => {
1460+
check_block_no_value(fcx, then_blk);
1461+
ty::mk_nil()
1462+
}
1463+
};
14581464

1459-
fcx.write_ty(id, if_t);
1465+
let cond_ty = fcx.expr_ty(cond_expr);
1466+
let if_ty = if ty::type_is_error(cond_ty) {
1467+
ty::mk_err()
1468+
} else if ty::type_is_bot(cond_ty) {
1469+
ty::mk_bot()
1470+
} else {
1471+
branches_ty
1472+
};
1473+
1474+
fcx.write_ty(id, if_ty);
14601475
}
14611476

14621477
fn lookup_op_method(fcx: @mut FnCtxt,
@@ -2501,25 +2516,9 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
25012516
fcx.write_nil(id);
25022517
}
25032518
}
2504-
ast::expr_if(cond, ref thn, elsopt) => {
2505-
check_expr_has_type(fcx, cond, ty::mk_bool());
2506-
check_then_else(fcx, thn, elsopt, id, expr.span);
2507-
let cond_ty = fcx.expr_ty(cond);
2508-
let then_ty = fcx.node_ty(thn.node.id);
2509-
let else_is_bot = elsopt.map_default(false, |els| {
2510-
ty::type_is_bot(fcx.expr_ty(*els))});
2511-
if ty::type_is_error(cond_ty) || ty::type_is_error(then_ty) {
2512-
fcx.write_error(id);
2513-
}
2514-
else if elsopt.map_default(false, |els| {
2515-
ty::type_is_error(fcx.expr_ty(*els)) }) {
2516-
fcx.write_error(id);
2517-
}
2518-
else if ty::type_is_bot(cond_ty) ||
2519-
(ty::type_is_bot(then_ty) && else_is_bot) {
2520-
fcx.write_bot(id);
2521-
}
2522-
// Other cases were handled by check_then_else
2519+
ast::expr_if(cond, ref then_blk, opt_else_expr) => {
2520+
check_then_else(fcx, cond, then_blk, opt_else_expr,
2521+
id, expr.span, expected);
25232522
}
25242523
ast::expr_while(cond, ref body) => {
25252524
check_expr_has_type(fcx, cond, ty::mk_bool());
@@ -2547,30 +2546,6 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
25472546
}
25482547
ast::expr_match(discrim, ref arms) => {
25492548
_match::check_match(fcx, expr, discrim, *arms);
2550-
let discrim_ty = fcx.expr_ty(discrim);
2551-
let arm_tys = arms.map(|a| fcx.node_ty(a.body.node.id));
2552-
if ty::type_is_error(discrim_ty) ||
2553-
arm_tys.iter().any_(|t| ty::type_is_error(*t)) {
2554-
fcx.write_error(id);
2555-
}
2556-
// keep in mind that `all` returns true in the empty vec case,
2557-
// which is what we want
2558-
else if ty::type_is_bot(discrim_ty) ||
2559-
arm_tys.iter().all(|t| ty::type_is_bot(*t)) {
2560-
fcx.write_bot(id);
2561-
}
2562-
else {
2563-
// Find the first non-_|_ arm.
2564-
// We know there's at least one because we already checked
2565-
// for n=0 as well as all arms being _|_ in the previous
2566-
// `if`.
2567-
for arm_tys.iter().advance |arm_ty| {
2568-
if !ty::type_is_bot(*arm_ty) {
2569-
fcx.write_ty(id, *arm_ty);
2570-
break;
2571-
}
2572-
}
2573-
}
25742549
}
25752550
ast::expr_fn_block(ref decl, ref body) => {
25762551
check_expr_fn(fcx, expr, None,

src/librustc/middle/typeck/infer/combine.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ use middle::typeck::infer::sub::Sub;
6565
use middle::typeck::infer::to_str::InferStr;
6666
use middle::typeck::infer::unify::{InferCtxtMethods};
6767
use middle::typeck::infer::{InferCtxt, cres, ures};
68-
use middle::typeck::infer::{SubtypeOrigin, SubtypeTrace};
68+
use middle::typeck::infer::{TypeOrigin, TypeTrace};
6969
use util::common::indent;
7070

7171
use std::result::{iter_vec2, map_vec2};
@@ -80,7 +80,7 @@ pub trait Combine {
8080
fn infcx(&self) -> @mut InferCtxt;
8181
fn tag(&self) -> ~str;
8282
fn a_is_expected(&self) -> bool;
83-
fn trace(&self) -> SubtypeTrace;
83+
fn trace(&self) -> TypeTrace;
8484

8585
fn sub(&self) -> Sub;
8686
fn lub(&self) -> Lub;
@@ -122,7 +122,7 @@ pub trait Combine {
122122
pub struct CombineFields {
123123
infcx: @mut InferCtxt,
124124
a_is_expected: bool,
125-
trace: SubtypeTrace,
125+
trace: TypeTrace,
126126
}
127127

128128
pub fn expected_found<C:Combine,T>(

src/librustc/middle/typeck/infer/error_reporting.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ rise to a patricular error.
2222
The basis of the system are the "origin" types. An "origin" is the
2323
reason that a constraint or inference variable arose. There are
2424
different "origin" enums for different kinds of constraints/variables
25-
(e.g., `SubtypeOrigin`, `RegionVariableOrigin`). An origin always has
25+
(e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
2626
a span, but also more information so that we can generate a meaningful
2727
error message.
2828
@@ -40,7 +40,7 @@ store and later report what gave rise to the conflicting constraints.
4040
# Subtype Trace
4141
4242
Determing whether `T1 <: T2` often involves a number of subtypes and
43-
subconstraints along the way. A "SubtypeTrace" is an extended version
43+
subconstraints along the way. A "TypeTrace" is an extended version
4444
of an origin that traces the types and other values that were being
4545
compared. It is not necessarily comprehensive (in fact, at the time of
4646
this writing it only tracks the root values being compared) but I'd
@@ -64,8 +64,8 @@ use middle::ty;
6464
use middle::ty::Region;
6565
use middle::typeck::infer;
6666
use middle::typeck::infer::InferCtxt;
67-
use middle::typeck::infer::SubtypeTrace;
68-
use middle::typeck::infer::SubtypeOrigin;
67+
use middle::typeck::infer::TypeTrace;
68+
use middle::typeck::infer::TypeOrigin;
6969
use middle::typeck::infer::SubregionOrigin;
7070
use middle::typeck::infer::RegionVariableOrigin;
7171
use middle::typeck::infer::Types;
@@ -108,8 +108,8 @@ impl InferCtxt {
108108
}
109109
}
110110

111-
fn report_and_explain_type_error(@mut self,
112-
trace: SubtypeTrace,
111+
pub fn report_and_explain_type_error(@mut self,
112+
trace: TypeTrace,
113113
terr: &ty::type_err) {
114114
let tcx = self.tcx;
115115

@@ -125,7 +125,9 @@ impl InferCtxt {
125125
infer::MethodCompatCheck(_) => "method not compatible with trait",
126126
infer::ExprAssignable(_) => "mismatched types",
127127
infer::RelateTraitRefs(_) => "mismatched traits",
128-
infer::RelateSelfType(_) => "mismatched types"
128+
infer::RelateSelfType(_) => "mismatched types",
129+
infer::MatchExpression(_) => "match arms have incompatible types",
130+
infer::IfExpression(_) => "if and else have incompatible types",
129131
};
130132

131133
self.tcx.sess.span_err(
@@ -179,7 +181,7 @@ impl InferCtxt {
179181
sup: Region) {
180182
match origin {
181183
infer::Subtype(trace) => {
182-
let terr = ty::terr_regions_does_not_outlive(sub, sup);
184+
let terr = ty::terr_regions_does_not_outlive(sup, sub);
183185
self.report_and_explain_type_error(trace, &terr);
184186
}
185187
infer::Reborrow(span) => {

src/librustc/middle/typeck/infer/glb.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use middle::typeck::infer::lub::Lub;
1818
use middle::typeck::infer::sub::Sub;
1919
use middle::typeck::infer::to_str::InferStr;
2020
use middle::typeck::infer::{cres, InferCtxt};
21-
use middle::typeck::infer::{SubtypeTrace, Subtype};
21+
use middle::typeck::infer::{TypeTrace, Subtype};
2222
use middle::typeck::infer::fold_regions_in_sig;
2323
use middle::typeck::isr_alist;
2424
use syntax::ast;
@@ -38,7 +38,7 @@ impl Combine for Glb {
3838
fn infcx(&self) -> @mut InferCtxt { self.infcx }
3939
fn tag(&self) -> ~str { ~"glb" }
4040
fn a_is_expected(&self) -> bool { self.a_is_expected }
41-
fn trace(&self) -> SubtypeTrace { self.trace }
41+
fn trace(&self) -> TypeTrace { self.trace }
4242

4343
fn sub(&self) -> Sub { Sub(**self) }
4444
fn lub(&self) -> Lub { Lub(**self) }

src/librustc/middle/typeck/infer/lub.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use middle::typeck::infer::sub::Sub;
1919
use middle::typeck::infer::to_str::InferStr;
2020
use middle::typeck::infer::{cres, InferCtxt};
2121
use middle::typeck::infer::fold_regions_in_sig;
22-
use middle::typeck::infer::{SubtypeTrace, Subtype};
22+
use middle::typeck::infer::{TypeTrace, Subtype};
2323
use middle::typeck::isr_alist;
2424
use util::common::indent;
2525
use util::ppaux::mt_to_str;
@@ -45,7 +45,7 @@ impl Combine for Lub {
4545
fn infcx(&self) -> @mut InferCtxt { self.infcx }
4646
fn tag(&self) -> ~str { ~"lub" }
4747
fn a_is_expected(&self) -> bool { self.a_is_expected }
48-
fn trace(&self) -> SubtypeTrace { self.trace }
48+
fn trace(&self) -> TypeTrace { self.trace }
4949

5050
fn sub(&self) -> Sub { Sub(**self) }
5151
fn lub(&self) -> Lub { Lub(**self) }

0 commit comments

Comments
 (0)