Skip to content

Commit 78d8416

Browse files
committed
Auto merge of #42649 - estebank:if-cond, r=nikomatsakis
Report error for assignment in `if` condition For code like `if x = 3 {}`, output: ``` error[E0308]: mismatched types --> $DIR/issue-17283.rs:25:8 | 25 | if x = x { | ^^^^^ | | | help: did you mean to compare equality? `x == x` | expected bool, found () | = note: expected type `bool` found type `()` ``` Fix #40926.
2 parents dfb8c80 + da78b4d commit 78d8416

File tree

5 files changed

+145
-37
lines changed

5 files changed

+145
-37
lines changed

Diff for: src/librustc_typeck/check/_match.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
413413
// discriminant. This is sort of a workaround, see note (*) in
414414
// `check_pat` for some details.
415415
discrim_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(discrim.span));
416-
self.check_expr_has_type(discrim, discrim_ty);
416+
self.check_expr_has_type_or_error(discrim, discrim_ty);
417417
};
418418

419419
// If the discriminant diverges, the match is pointless (e.g.,
@@ -480,7 +480,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
480480
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
481481
if let Some(ref e) = arm.guard {
482482
self.diverges.set(pats_diverge);
483-
self.check_expr_has_type(e, tcx.types.bool);
483+
self.check_expr_has_type_or_error(e, tcx.types.bool);
484484
}
485485

486486
self.diverges.set(pats_diverge);

Diff for: src/librustc_typeck/check/demand.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
2626
// Requires that the two types unify, and prints an error message if
2727
// they don't.
2828
pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
29+
self.demand_suptype_diag(sp, expected, actual).map(|mut e| e.emit());
30+
}
31+
32+
pub fn demand_suptype_diag(&self,
33+
sp: Span,
34+
expected: Ty<'tcx>,
35+
actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
2936
let cause = &self.misc(sp);
3037
match self.at(cause, self.param_env).sup(expected, actual) {
3138
Ok(InferOk { obligations, value: () }) => {
3239
self.register_predicates(obligations);
40+
None
3341
},
3442
Err(e) => {
35-
self.report_mismatched_types(&cause, expected, actual, e).emit();
43+
Some(self.report_mismatched_types(&cause, expected, actual, e))
3644
}
3745
}
3846
}

Diff for: src/librustc_typeck/check/mod.rs

+62-25
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ pub enum Expectation<'tcx> {
232232
/// We know nothing about what type this expression should have.
233233
NoExpectation,
234234

235+
/// This expression is an `if` condition, it must resolve to `bool`.
236+
ExpectIfCondition,
237+
235238
/// This expression should have the type given (or some subtype)
236239
ExpectHasType(Ty<'tcx>),
237240

@@ -310,9 +313,8 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
310313
// no constraints yet present), just returns `None`.
311314
fn resolve(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Expectation<'tcx> {
312315
match self {
313-
NoExpectation => {
314-
NoExpectation
315-
}
316+
NoExpectation => NoExpectation,
317+
ExpectIfCondition => ExpectIfCondition,
316318
ExpectCastableToType(t) => {
317319
ExpectCastableToType(fcx.resolve_type_vars_if_possible(&t))
318320
}
@@ -328,6 +330,7 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
328330
fn to_option(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Option<Ty<'tcx>> {
329331
match self.resolve(fcx) {
330332
NoExpectation => None,
333+
ExpectIfCondition => Some(fcx.tcx.types.bool),
331334
ExpectCastableToType(ty) |
332335
ExpectHasType(ty) |
333336
ExpectRvalueLikeUnsized(ty) => Some(ty),
@@ -341,7 +344,8 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
341344
fn only_has_type(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Option<Ty<'tcx>> {
342345
match self.resolve(fcx) {
343346
ExpectHasType(ty) => Some(ty),
344-
_ => None
347+
ExpectIfCondition => Some(fcx.tcx.types.bool),
348+
NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None,
345349
}
346350
}
347351

@@ -2644,10 +2648,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
26442648
self.demand_eqtype(expr.span, expected, ty);
26452649
}
26462650

2647-
pub fn check_expr_has_type(&self,
2648-
expr: &'gcx hir::Expr,
2649-
expected: Ty<'tcx>) -> Ty<'tcx> {
2650-
let mut ty = self.check_expr_with_hint(expr, expected);
2651+
pub fn check_expr_has_type_or_error(&self,
2652+
expr: &'gcx hir::Expr,
2653+
expected: Ty<'tcx>) -> Ty<'tcx> {
2654+
self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected))
2655+
}
2656+
2657+
fn check_expr_meets_expectation_or_error(&self,
2658+
expr: &'gcx hir::Expr,
2659+
expected: Expectation<'tcx>) -> Ty<'tcx> {
2660+
let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool);
2661+
let mut ty = self.check_expr_with_expectation(expr, expected);
26512662

26522663
// While we don't allow *arbitrary* coercions here, we *do* allow
26532664
// coercions from ! to `expected`.
@@ -2663,7 +2674,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
26632674
ty = adj_ty;
26642675
}
26652676

2666-
self.demand_suptype(expr.span, expected, ty);
2677+
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
2678+
// Add help to type error if this is an `if` condition with an assignment
2679+
match (expected, &expr.node) {
2680+
(ExpectIfCondition, &hir::ExprAssign(ref lhs, ref rhs)) => {
2681+
let msg = "did you mean to compare equality?";
2682+
if let (Ok(left), Ok(right)) = (
2683+
self.tcx.sess.codemap().span_to_snippet(lhs.span),
2684+
self.tcx.sess.codemap().span_to_snippet(rhs.span))
2685+
{
2686+
err.span_suggestion(expr.span, msg, format!("{} == {}", left, right));
2687+
} else {
2688+
err.help(msg);
2689+
}
2690+
}
2691+
_ => (),
2692+
}
2693+
err.emit();
2694+
}
26672695
ty
26682696
}
26692697

@@ -2838,7 +2866,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
28382866
opt_else_expr: Option<&'gcx hir::Expr>,
28392867
sp: Span,
28402868
expected: Expectation<'tcx>) -> Ty<'tcx> {
2841-
let cond_ty = self.check_expr_has_type(cond_expr, self.tcx.types.bool);
2869+
let cond_ty = self.check_expr_meets_expectation_or_error(cond_expr, ExpectIfCondition);
28422870
let cond_diverges = self.diverges.get();
28432871
self.diverges.set(Diverges::Maybe);
28442872

@@ -3325,7 +3353,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
33253353
self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span, variant, fields,
33263354
base_expr.is_none());
33273355
if let &Some(ref base_expr) = base_expr {
3328-
self.check_expr_has_type(base_expr, struct_ty);
3356+
self.check_expr_has_type_or_error(base_expr, struct_ty);
33293357
match struct_ty.sty {
33303358
ty::TyAdt(adt, substs) if adt.is_struct() => {
33313359
let fru_field_types = adt.struct_variant().fields.iter().map(|f| {
@@ -3638,19 +3666,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
36383666
hir::ExprAssign(ref lhs, ref rhs) => {
36393667
let lhs_ty = self.check_expr_with_lvalue_pref(&lhs, PreferMutLvalue);
36403668

3641-
let tcx = self.tcx;
3642-
if !tcx.expr_is_lval(&lhs) {
3643-
struct_span_err!(
3644-
tcx.sess, expr.span, E0070,
3645-
"invalid left-hand side expression")
3646-
.span_label(
3647-
expr.span,
3648-
"left-hand of expression not valid")
3649-
.emit();
3650-
}
3651-
36523669
let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty);
36533670

3671+
match expected {
3672+
ExpectIfCondition => {
3673+
self.tcx.sess.delay_span_bug(lhs.span, "invalid lhs expression in if;\
3674+
expected error elsehwere");
3675+
}
3676+
_ => {
3677+
// Only check this if not in an `if` condition, as the
3678+
// mistyped comparison help is more appropriate.
3679+
if !self.tcx.expr_is_lval(&lhs) {
3680+
struct_span_err!(
3681+
self.tcx.sess, expr.span, E0070,
3682+
"invalid left-hand side expression")
3683+
.span_label(
3684+
expr.span,
3685+
"left-hand of expression not valid")
3686+
.emit();
3687+
}
3688+
}
3689+
}
3690+
36543691
self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
36553692

36563693
if lhs_ty.references_error() || rhs_ty.references_error() {
@@ -3671,7 +3708,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
36713708
};
36723709

36733710
self.with_breakable_ctxt(expr.id, ctxt, || {
3674-
self.check_expr_has_type(&cond, tcx.types.bool);
3711+
self.check_expr_has_type_or_error(&cond, tcx.types.bool);
36753712
let cond_diverging = self.diverges.get();
36763713
self.check_block_no_value(&body);
36773714

@@ -3809,7 +3846,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
38093846
}
38103847
None => {
38113848
let t: Ty = self.next_ty_var(TypeVariableOrigin::MiscVariable(element.span));
3812-
let element_ty = self.check_expr_has_type(&element, t);
3849+
let element_ty = self.check_expr_has_type_or_error(&element, t);
38133850
(element_ty, t)
38143851
}
38153852
};
@@ -4060,7 +4097,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
40604097
}
40614098
hir::StmtExpr(ref expr, _) => {
40624099
// Check with expected type of ()
4063-
self.check_expr_has_type(&expr, self.tcx.mk_nil());
4100+
self.check_expr_has_type_or_error(&expr, self.tcx.mk_nil());
40644101
}
40654102
hir::StmtSemi(ref expr, _) => {
40664103
self.check_expr(&expr);

Diff for: src/test/compile-fail/issue-17283.rs renamed to src/test/ui/type-check/assignment-in-if.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,29 @@ fn main() {
2424
// `x { ... }` should not be interpreted as a struct literal here
2525
if x = x {
2626
//~^ ERROR mismatched types
27-
//~| expected type `bool`
28-
//~| found type `()`
29-
//~| expected bool, found ()
27+
//~| HELP did you mean to compare equality?
3028
println!("{}", x);
3129
}
3230
// Explicit parentheses on the left should match behavior of above
3331
if (x = x) {
3432
//~^ ERROR mismatched types
35-
//~| expected type `bool`
36-
//~| found type `()`
37-
//~| expected bool, found ()
33+
//~| HELP did you mean to compare equality?
3834
println!("{}", x);
3935
}
4036
// The struct literal interpretation is fine with explicit parentheses on the right
4137
if y = (Foo { foo: x }) {
4238
//~^ ERROR mismatched types
43-
//~| expected type `bool`
44-
//~| found type `()`
45-
//~| expected bool, found ()
39+
//~| HELP did you mean to compare equality?
40+
println!("{}", x);
41+
}
42+
// "invalid left-hand side expression" error is suppresed
43+
if 3 = x {
44+
//~^ ERROR mismatched types
45+
//~| HELP did you mean to compare equality?
46+
println!("{}", x);
47+
}
48+
if (if true { x = 4 } else { x = 5 }) {
49+
//~^ ERROR mismatched types
4650
println!("{}", x);
4751
}
4852
}

Diff for: src/test/ui/type-check/assignment-in-if.stderr

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/assignment-in-if.rs:25:8
3+
|
4+
25 | if x = x {
5+
| ^^^^^
6+
| |
7+
| help: did you mean to compare equality? `x == x`
8+
| expected bool, found ()
9+
|
10+
= note: expected type `bool`
11+
found type `()`
12+
13+
error[E0308]: mismatched types
14+
--> $DIR/assignment-in-if.rs:31:8
15+
|
16+
31 | if (x = x) {
17+
| ^^^^^^^
18+
| |
19+
| help: did you mean to compare equality? `x == x`
20+
| expected bool, found ()
21+
|
22+
= note: expected type `bool`
23+
found type `()`
24+
25+
error[E0308]: mismatched types
26+
--> $DIR/assignment-in-if.rs:37:8
27+
|
28+
37 | if y = (Foo { foo: x }) {
29+
| ^^^^^^^^^^^^^^^^^^^^
30+
| |
31+
| help: did you mean to compare equality? `y == (Foo { foo: x })`
32+
| expected bool, found ()
33+
|
34+
= note: expected type `bool`
35+
found type `()`
36+
37+
error[E0308]: mismatched types
38+
--> $DIR/assignment-in-if.rs:43:8
39+
|
40+
43 | if 3 = x {
41+
| ^^^^^
42+
| |
43+
| help: did you mean to compare equality? `3 == x`
44+
| expected bool, found ()
45+
|
46+
= note: expected type `bool`
47+
found type `()`
48+
49+
error[E0308]: mismatched types
50+
--> $DIR/assignment-in-if.rs:48:8
51+
|
52+
48 | if (if true { x = 4 } else { x = 5 }) {
53+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found ()
54+
|
55+
= note: expected type `bool`
56+
found type `()`
57+
58+
error: aborting due to previous error(s)
59+

0 commit comments

Comments
 (0)