@@ -232,6 +232,9 @@ pub enum Expectation<'tcx> {
232
232
/// We know nothing about what type this expression should have.
233
233
NoExpectation ,
234
234
235
+ /// This expression is an `if` condition, it must resolve to `bool`.
236
+ ExpectIfCondition ,
237
+
235
238
/// This expression should have the type given (or some subtype)
236
239
ExpectHasType ( Ty < ' tcx > ) ,
237
240
@@ -310,9 +313,8 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
310
313
// no constraints yet present), just returns `None`.
311
314
fn resolve ( self , fcx : & FnCtxt < ' a , ' gcx , ' tcx > ) -> Expectation < ' tcx > {
312
315
match self {
313
- NoExpectation => {
314
- NoExpectation
315
- }
316
+ NoExpectation => NoExpectation ,
317
+ ExpectIfCondition => ExpectIfCondition ,
316
318
ExpectCastableToType ( t) => {
317
319
ExpectCastableToType ( fcx. resolve_type_vars_if_possible ( & t) )
318
320
}
@@ -328,6 +330,7 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
328
330
fn to_option ( self , fcx : & FnCtxt < ' a , ' gcx , ' tcx > ) -> Option < Ty < ' tcx > > {
329
331
match self . resolve ( fcx) {
330
332
NoExpectation => None ,
333
+ ExpectIfCondition => Some ( fcx. tcx . types . bool ) ,
331
334
ExpectCastableToType ( ty) |
332
335
ExpectHasType ( ty) |
333
336
ExpectRvalueLikeUnsized ( ty) => Some ( ty) ,
@@ -341,6 +344,7 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
341
344
fn only_has_type ( self , fcx : & FnCtxt < ' a , ' gcx , ' tcx > ) -> Option < Ty < ' tcx > > {
342
345
match self . resolve ( fcx) {
343
346
ExpectHasType ( ty) => Some ( ty) ,
347
+ ExpectIfCondition => Some ( fcx. tcx . types . bool ) ,
344
348
_ => None
345
349
}
346
350
}
@@ -2646,7 +2650,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
2646
2650
pub fn check_expr_has_type ( & self ,
2647
2651
expr : & ' gcx hir:: Expr ,
2648
2652
expected : Ty < ' tcx > ) -> Ty < ' tcx > {
2649
- let mut ty = self . check_expr_with_hint ( expr, expected) ;
2653
+ self . check_expr_expect_type ( expr, ExpectHasType ( expected) )
2654
+ }
2655
+
2656
+ fn check_expr_expect_type ( & self ,
2657
+ expr : & ' gcx hir:: Expr ,
2658
+ expected : Expectation < ' tcx > ) -> Ty < ' tcx > {
2659
+ let expected_ty = expected. to_option ( & self ) . unwrap_or ( self . tcx . types . bool ) ;
2660
+ let mut ty = self . check_expr_with_expectation ( expr, expected) ;
2650
2661
2651
2662
// While we don't allow *arbitrary* coercions here, we *do* allow
2652
2663
// coercions from ! to `expected`.
@@ -2662,7 +2673,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
2662
2673
ty = adj_ty;
2663
2674
}
2664
2675
2665
- self . demand_suptype ( expr. span , expected, ty) ;
2676
+ if let Some ( mut err) = self . demand_suptype_diag ( expr. span , expected_ty, ty) {
2677
+ // Add help to type error if this is an `if` condition with an assignment
2678
+ match ( expected, & expr. node ) {
2679
+ ( ExpectIfCondition , & hir:: ExprAssign ( ref lhs, ref rhs) ) => {
2680
+ let msg = "did you mean to compare equality?" ;
2681
+ if let ( Ok ( left) , Ok ( right) ) = (
2682
+ self . tcx . sess . codemap ( ) . span_to_snippet ( lhs. span ) ,
2683
+ self . tcx . sess . codemap ( ) . span_to_snippet ( rhs. span ) )
2684
+ {
2685
+ err. span_suggestion ( expr. span , msg, format ! ( "{} == {}" , left, right) ) ;
2686
+ } else {
2687
+ err. help ( msg) ;
2688
+ }
2689
+ }
2690
+ _ => ( ) ,
2691
+ }
2692
+ err. emit ( ) ;
2693
+ }
2666
2694
ty
2667
2695
}
2668
2696
@@ -2837,7 +2865,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
2837
2865
opt_else_expr : Option < & ' gcx hir:: Expr > ,
2838
2866
sp : Span ,
2839
2867
expected : Expectation < ' tcx > ) -> Ty < ' tcx > {
2840
- let cond_ty = self . check_expr_has_type ( cond_expr, self . tcx . types . bool ) ;
2868
+ let cond_ty = self . check_expr_expect_type ( cond_expr, ExpectIfCondition ) ;
2841
2869
let cond_diverges = self . diverges . get ( ) ;
2842
2870
self . diverges . set ( Diverges :: Maybe ) ;
2843
2871
@@ -3637,19 +3665,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
3637
3665
hir:: ExprAssign ( ref lhs, ref rhs) => {
3638
3666
let lhs_ty = self . check_expr_with_lvalue_pref ( & lhs, PreferMutLvalue ) ;
3639
3667
3640
- let tcx = self . tcx ;
3641
- if !tcx. expr_is_lval ( & lhs) {
3642
- struct_span_err ! (
3643
- tcx. sess, expr. span, E0070 ,
3644
- "invalid left-hand side expression" )
3645
- . span_label (
3646
- expr. span ,
3647
- "left-hand of expression not valid" )
3648
- . emit ( ) ;
3649
- }
3650
-
3651
3668
let rhs_ty = self . check_expr_coercable_to_type ( & rhs, lhs_ty) ;
3652
3669
3670
+ match expected {
3671
+ ExpectIfCondition => ( ) ,
3672
+ _ => {
3673
+ // Only check this if not in an `if` condition, as the
3674
+ // mistyped comparison help is more appropriate.
3675
+ if !self . tcx . expr_is_lval ( & lhs) {
3676
+ struct_span_err ! (
3677
+ self . tcx. sess, expr. span, E0070 ,
3678
+ "invalid left-hand side expression" )
3679
+ . span_label (
3680
+ expr. span ,
3681
+ "left-hand of expression not valid" )
3682
+ . emit ( ) ;
3683
+ }
3684
+ }
3685
+ }
3686
+
3653
3687
self . require_type_is_sized ( lhs_ty, lhs. span , traits:: AssignmentLhsSized ) ;
3654
3688
3655
3689
if lhs_ty. references_error ( ) || rhs_ty. references_error ( ) {
0 commit comments