diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 253fc5575c5..726b3ba9857 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1289,8 +1289,20 @@ fn coerce_inner<'a>( } // Error possibly reported in `check_assign` so avoid emitting error again. - err.emit_unless(expression.filter(|e| fcx.is_assign_to_bool(e, expected)) - .is_some()); + let assign_to_bool = expression + // #67273: Use initial expected type as opposed to `expected`. + // Otherwise we end up using prior coercions in e.g. a `match` expression: + // ``` + // match i { + // 0 => true, // Because of this... + // 1 => i = 1, // ...`expected == bool` now, but not when checking `i = 1`. + // _ => (), + // }; + // ``` + .filter(|e| fcx.is_assign_to_bool(e, self.expected_ty())) + .is_some(); + + err.emit_unless(assign_to_bool); self.final_ty = Some(fcx.tcx.types.err); } diff --git a/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs new file mode 100644 index 00000000000..e23c0d0a40a --- /dev/null +++ b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs @@ -0,0 +1,27 @@ +fn main() { + let mut i: i64; + // Expected type is an inference variable `?T` + // because the `match` is used as a statement. + // This is the "initial" type of the `coercion`. + match i { + // Add `bool` to the overall `coercion`. + 0 => true, + + // Necessary to cause the ICE: + 1 => true, + + // Suppose that we had `let _: bool = match i { ... }`. + // In that case, as the expected type would be `bool`, + // we would suggest `i == 1` as a fix. + // + // However, no type error happens when checking `i = 1` because `expected == ?T`, + // which will unify with `typeof(i = 1) == ()`. + // + // However, in #67273, we would delay the unification of this arm with the above + // because we used the hitherto accumulated coercion as opposed to the "initial" type. + 2 => i = 1, + //~^ ERROR match arms have incompatible types + + _ => (), + } +} diff --git a/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr new file mode 100644 index 00000000000..3547285542a --- /dev/null +++ b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr @@ -0,0 +1,22 @@ +error[E0308]: match arms have incompatible types + --> $DIR/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs:22:14 + | +LL | / match i { +LL | | // Add `bool` to the overall `coercion`. +LL | | 0 => true, + | | ---- this is found to be of type `bool` +LL | | +LL | | // Necessary to cause the ICE: +LL | | 1 => true, + | | ---- this is found to be of type `bool` +... | +LL | | 2 => i = 1, + | | ^^^^^ expected `bool`, found `()` +... | +LL | | _ => (), +LL | | } + | |_____- `match` arms have incompatible types + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.