Skip to content

Commit 36404ce

Browse files
committed
Allow coercions from never-type when ref binding is involved
When we type-check a binding that uses ref patterns, we do not perform coercions between the expression type and the pattern type. However, this has the unfortunate result of disallow code like: `let Foo { ref my_field } = diverging_expr();`. This code is accepted without the `ref` keyword. We now explicitly allow coercions when the expression type is ! In all other cases, we still avoid performing coersions. This avoids causing broader changes in type-checking (with potential soundness implications for 'ref mut'),
1 parent 42ae1a7 commit 36404ce

File tree

3 files changed

+23
-2
lines changed

3 files changed

+23
-2
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
131131
self.check_expr_with_expectation(expr, ExpectHasType(expected))
132132
}
133133

134-
fn check_expr_with_expectation_and_needs(
134+
pub(super) fn check_expr_with_expectation_and_needs(
135135
&self,
136136
expr: &'tcx hir::Expr<'tcx>,
137137
expected: Expectation<'tcx>,

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1444,7 +1444,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14441444
// referent for the reference that results is *equal to* the
14451445
// type of the place it is referencing, and not some
14461446
// supertype thereof.
1447-
let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
1447+
let mut init_ty = self.check_expr_with_expectation_and_needs(
1448+
init,
1449+
ExpectHasType(local_ty),
1450+
Needs::maybe_mut_place(m),
1451+
);
1452+
// The one exception to the above rule - we permit coercions when the expression has type !
1453+
// This allows `let Foo { ref my_field } = diverging_expr;`. The actual assignment is guaranteed
1454+
// to be unreachable, so the soundness concerns with 'ref mut' do not apply.
1455+
if init_ty.is_never() {
1456+
init_ty = self.demand_coerce(init, init_ty, local_ty, None, AllowTwoPhase::No);
1457+
};
1458+
14481459
if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) {
14491460
self.emit_type_mismatch_suggestions(
14501461
&mut diag,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// check-pass
2+
3+
pub struct Foo {
4+
bar: u8
5+
}
6+
7+
#[allow(unused_variables)]
8+
fn main() {
9+
let Foo { ref bar } = loop {};
10+
}

0 commit comments

Comments
 (0)