Skip to content

Commit 46eb724

Browse files
committed
Propagate coercions through match expressions.
1 parent 22a9f25 commit 46eb724

File tree

4 files changed

+81
-38
lines changed

4 files changed

+81
-38
lines changed

src/librustc_typeck/check/_match.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use middle::infer::{mod, resolve};
1313
use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding, pat_is_const};
1414
use middle::subst::{Subst, Substs};
1515
use middle::ty::{mod, Ty};
16-
use check::{check_expr, check_expr_has_type, demand, FnCtxt};
16+
use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
17+
use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
1718
use check::{instantiate_path, structurally_resolved_type, valid_range_bounds};
1819
use require_same_types;
1920
use util::nodemap::FnvHashMap;
@@ -233,10 +234,11 @@ pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
233234
}
234235
}
235236

236-
pub fn check_match(fcx: &FnCtxt,
237-
expr: &ast::Expr,
238-
discrim: &ast::Expr,
239-
arms: &[ast::Arm]) {
237+
pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
238+
expr: &ast::Expr,
239+
discrim: &ast::Expr,
240+
arms: &[ast::Arm],
241+
expected: Expectation<'tcx>) {
240242
let tcx = fcx.ccx.tcx;
241243

242244
let discrim_ty = fcx.infcx().next_ty_var();
@@ -263,9 +265,23 @@ pub fn check_match(fcx: &FnCtxt,
263265
// on any empty type and is therefore unreachable; should the flow
264266
// of execution reach it, we will panic, so bottom is an appropriate
265267
// type in that case)
268+
let expected = expected.adjust_for_branches(fcx);
266269
let result_ty = arms.iter().fold(fcx.infcx().next_diverging_ty_var(), |result_ty, arm| {
267-
check_expr(fcx, &*arm.body);
268-
let bty = fcx.node_ty(arm.body.id);
270+
let bty = match expected {
271+
// We don't coerce to `()` so that if the match expression is a
272+
// statement it's branches can have any consistent type. That allows
273+
// us to give better error messages (pointing to a usually better
274+
// arm for inconsistent arms or to the whole match when a `()` type
275+
// is required).
276+
Expectation::ExpectHasType(ety) if ety != ty::mk_nil(fcx.tcx()) => {
277+
check_expr_coercable_to_type(fcx, &*arm.body, ety);
278+
ety
279+
}
280+
_ => {
281+
check_expr_with_expectation(fcx, &*arm.body, expected);
282+
fcx.node_ty(arm.body.id)
283+
}
284+
};
269285

270286
if let Some(ref e) = arm.guard {
271287
check_expr_has_type(fcx, &**e, ty::mk_bool());

src/librustc_typeck/check/mod.rs

+35-29
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,38 @@ enum Expectation<'tcx> {
179179

180180
impl<'tcx> Copy for Expectation<'tcx> {}
181181

182+
impl<'tcx> Expectation<'tcx> {
183+
// Disregard "castable to" expectations because they
184+
// can lead us astray. Consider for example `if cond
185+
// {22} else {c} as u8` -- if we propagate the
186+
// "castable to u8" constraint to 22, it will pick the
187+
// type 22u8, which is overly constrained (c might not
188+
// be a u8). In effect, the problem is that the
189+
// "castable to" expectation is not the tightest thing
190+
// we can say, so we want to drop it in this case.
191+
// The tightest thing we can say is "must unify with
192+
// else branch". Note that in the case of a "has type"
193+
// constraint, this limitation does not hold.
194+
195+
// If the expected type is just a type variable, then don't use
196+
// an expected type. Otherwise, we might write parts of the type
197+
// when checking the 'then' block which are incompatible with the
198+
// 'else' branch.
199+
fn adjust_for_branches<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
200+
match self.only_has_type() {
201+
ExpectHasType(ety) => {
202+
let ety = fcx.infcx().shallow_resolve(ety);
203+
if !ty::type_is_ty_var(ety) {
204+
ExpectHasType(ety)
205+
} else {
206+
NoExpectation
207+
}
208+
}
209+
_ => NoExpectation
210+
}
211+
}
212+
}
213+
182214
#[deriving(Copy, Clone)]
183215
pub struct UnsafetyState {
184216
pub def: ast::NodeId,
@@ -3047,7 +3079,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
30473079
}
30483080

30493081
// A generic function for checking the then and else in an if
3050-
// or if-check
3082+
// or if-else.
30513083
fn check_then_else<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
30523084
cond_expr: &ast::Expr,
30533085
then_blk: &ast::Block,
@@ -3057,33 +3089,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
30573089
expected: Expectation<'tcx>) {
30583090
check_expr_has_type(fcx, cond_expr, ty::mk_bool());
30593091

3060-
// Disregard "castable to" expectations because they
3061-
// can lead us astray. Consider for example `if cond
3062-
// {22} else {c} as u8` -- if we propagate the
3063-
// "castable to u8" constraint to 22, it will pick the
3064-
// type 22u8, which is overly constrained (c might not
3065-
// be a u8). In effect, the problem is that the
3066-
// "castable to" expectation is not the tightest thing
3067-
// we can say, so we want to drop it in this case.
3068-
// The tightest thing we can say is "must unify with
3069-
// else branch". Note that in the case of a "has type"
3070-
// constraint, this limitation does not hold.
3071-
3072-
// If the expected type is just a type variable, then don't use
3073-
// an expected type. Otherwise, we might write parts of the type
3074-
// when checking the 'then' block which are incompatible with the
3075-
// 'else' branch.
3076-
let expected = match expected.only_has_type() {
3077-
ExpectHasType(ety) => {
3078-
let ety = fcx.infcx().shallow_resolve(ety);
3079-
if !ty::type_is_ty_var(ety) {
3080-
ExpectHasType(ety)
3081-
} else {
3082-
NoExpectation
3083-
}
3084-
}
3085-
_ => NoExpectation
3086-
};
3092+
let expected = expected.adjust_for_branches(fcx);
30873093
check_block_with_expected(fcx, then_blk, expected);
30883094
let then_ty = fcx.node_ty(then_blk.id);
30893095

@@ -3989,7 +3995,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
39893995
}
39903996
}
39913997
ast::ExprMatch(ref discrim, ref arms, _) => {
3992-
_match::check_match(fcx, expr, &**discrim, arms.as_slice());
3998+
_match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected);
39933999
}
39944000
ast::ExprClosure(_, opt_kind, ref decl, ref body) => {
39954001
closure::check_expr_closure(fcx, expr, opt_kind, &**decl, &**body, expected);

src/test/compile-fail/lub-match.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,21 @@ pub fn opt_str1<'a>(maybestr: &'a Option<String>) -> &'a str {
3434

3535
pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str {
3636
match *maybestr {
37-
//~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements
3837
None => "(none)",
3938
Some(ref s) => {
4039
let s: &'a str = s.as_slice();
4140
s
41+
//~^ ERROR cannot infer an appropriate lifetime
4242
}
4343
}
4444
}
4545

4646
pub fn opt_str3<'a>(maybestr: &'a Option<String>) -> &'static str {
4747
match *maybestr {
48-
//~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements
4948
Some(ref s) => {
5049
let s: &'a str = s.as_slice();
5150
s
51+
//~^ ERROR cannot infer an appropriate lifetime
5252
}
5353
None => "(none)",
5454
}

src/test/run-pass/coerce-match.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that coercions are propagated through match and if expressions.
12+
13+
pub fn main() {
14+
let _: Box<[int]> = if true { box [1i, 2, 3] } else { box [1i] };
15+
16+
let _: Box<[int]> = match true { true => box [1i, 2, 3], false => box [1i] };
17+
18+
// Check we don't get over-keen at propagating coercions in the case of casts.
19+
let x = if true { 42 } else { 42u8 } as u16;
20+
let x = match true { true => 42, false => 42u8 } as u16;
21+
}

0 commit comments

Comments
 (0)