Skip to content

Commit 126f224

Browse files
committed
auto merge of #18015 : jakub-/rust/issue-4201, r=pcwalton
Closes #4201.
2 parents 8096fee + 43e5d10 commit 126f224

File tree

5 files changed

+63
-31
lines changed

5 files changed

+63
-31
lines changed

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

+33-29
Original file line numberDiff line numberDiff line change
@@ -2997,35 +2997,36 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
29972997
expected: Expectation) {
29982998
check_expr_has_type(fcx, cond_expr, ty::mk_bool());
29992999

3000+
// Disregard "castable to" expectations because they
3001+
// can lead us astray. Consider for example `if cond
3002+
// {22} else {c} as u8` -- if we propagate the
3003+
// "castable to u8" constraint to 22, it will pick the
3004+
// type 22u8, which is overly constrained (c might not
3005+
// be a u8). In effect, the problem is that the
3006+
// "castable to" expectation is not the tightest thing
3007+
// we can say, so we want to drop it in this case.
3008+
// The tightest thing we can say is "must unify with
3009+
// else branch". Note that in the case of a "has type"
3010+
// constraint, this limitation does not hold.
3011+
3012+
// If the expected type is just a type variable, then don't use
3013+
// an expected type. Otherwise, we might write parts of the type
3014+
// when checking the 'then' block which are incompatible with the
3015+
// 'else' branch.
3016+
let expected = match expected.only_has_type() {
3017+
ExpectHasType(ety) => {
3018+
match infer::resolve_type(fcx.infcx(), Some(sp), ety, force_tvar) {
3019+
Ok(rty) if !ty::type_is_ty_var(rty) => ExpectHasType(rty),
3020+
_ => NoExpectation
3021+
}
3022+
}
3023+
_ => NoExpectation
3024+
};
3025+
check_block_with_expected(fcx, then_blk, expected);
3026+
let then_ty = fcx.node_ty(then_blk.id);
3027+
30003028
let branches_ty = match opt_else_expr {
30013029
Some(ref else_expr) => {
3002-
// Disregard "castable to" expectations because they
3003-
// can lead us astray. Consider for example `if cond
3004-
// {22} else {c} as u8` -- if we propagate the
3005-
// "castable to u8" constraint to 22, it will pick the
3006-
// type 22u8, which is overly constrained (c might not
3007-
// be a u8). In effect, the problem is that the
3008-
// "castable to" expectation is not the tightest thing
3009-
// we can say, so we want to drop it in this case.
3010-
// The tightest thing we can say is "must unify with
3011-
// else branch". Note that in the case of a "has type"
3012-
// constraint, this limitation does not hold.
3013-
3014-
// If the expected type is just a type variable, then don't use
3015-
// an expected type. Otherwise, we might write parts of the type
3016-
// when checking the 'then' block which are incompatible with the
3017-
// 'else' branch.
3018-
let expected = match expected.only_has_type() {
3019-
ExpectHasType(ety) => {
3020-
match infer::resolve_type(fcx.infcx(), Some(sp), ety, force_tvar) {
3021-
Ok(rty) if !ty::type_is_ty_var(rty) => ExpectHasType(rty),
3022-
_ => NoExpectation
3023-
}
3024-
}
3025-
_ => NoExpectation
3026-
};
3027-
check_block_with_expected(fcx, then_blk, expected);
3028-
let then_ty = fcx.node_ty(then_blk.id);
30293030
check_expr_with_expectation(fcx, &**else_expr, expected);
30303031
let else_ty = fcx.expr_ty(&**else_expr);
30313032
infer::common_supertype(fcx.infcx(),
@@ -3035,8 +3036,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
30353036
else_ty)
30363037
}
30373038
None => {
3038-
check_block_no_value(fcx, then_blk);
3039-
ty::mk_nil()
3039+
infer::common_supertype(fcx.infcx(),
3040+
infer::IfExpressionWithNoElse(sp),
3041+
false,
3042+
then_ty,
3043+
ty::mk_nil())
30403044
}
30413045
};
30423046

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

+4
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> {
366366
infer::RelateOutputImplTypes(_) => "mismatched types",
367367
infer::MatchExpressionArm(_, _) => "match arms have incompatible types",
368368
infer::IfExpression(_) => "if and else have incompatible types",
369+
infer::IfExpressionWithNoElse(_) => "if may be missing an else clause",
369370
};
370371

371372
self.tcx.sess.span_err(
@@ -1486,6 +1487,9 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
14861487
infer::IfExpression(_) => {
14871488
format!("if and else have compatible types")
14881489
}
1490+
infer::IfExpressionWithNoElse(_) => {
1491+
format!("if may be missing an else clause")
1492+
}
14891493
};
14901494

14911495
match self.values_str(&trace.values) {

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

+7
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ pub enum TypeOrigin {
121121

122122
// Computing common supertype in an if expression
123123
IfExpression(Span),
124+
125+
// Computing common supertype of an if expression with no else counter-part
126+
IfExpressionWithNoElse(Span)
124127
}
125128

126129
/// See `error_reporting.rs` for more details
@@ -1001,6 +1004,7 @@ impl TypeOrigin {
10011004
RelateOutputImplTypes(span) => span,
10021005
MatchExpressionArm(match_span, _) => match_span,
10031006
IfExpression(span) => span,
1007+
IfExpressionWithNoElse(span) => span
10041008
}
10051009
}
10061010
}
@@ -1030,6 +1034,9 @@ impl Repr for TypeOrigin {
10301034
IfExpression(a) => {
10311035
format!("IfExpression({})", a.repr(tcx))
10321036
}
1037+
IfExpressionWithNoElse(a) => {
1038+
format!("IfExpressionWithNoElse({})", a.repr(tcx))
1039+
}
10331040
}
10341041
}
10351042
}

src/test/compile-fail/if-without-else-result.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// error-pattern:mismatched types: expected `()`, found `bool`
12-
1311
extern crate debug;
1412

1513
fn main() {
1614
let a = if true { true };
15+
//~^ ERROR if may be missing an else clause: expected `()`, found `bool` (expected (), found bool)
1716
println!("{:?}", a);
1817
}

src/test/compile-fail/issue-4201.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
fn main() {
12+
let a = if true {
13+
0
14+
} else if false {
15+
//~^ ERROR if may be missing an else clause: expected `()`, found `<generic integer #1>`
16+
1
17+
};
18+
}

0 commit comments

Comments
 (0)