From b82416b184ae016f3c7d9b3576f5ce7c7b43daaf Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 24 Nov 2023 18:18:55 -0500 Subject: [PATCH] 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'), --- compiler/rustc_hir_typeck/src/expr.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 13 ++++++++++- .../issue-118113-ref-pattern-never-type.rs | 10 +++++++++ tests/ui/pattern/ref-pattern-assoc-type.rs | 22 +++++++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tests/ui/pattern/issue-118113-ref-pattern-never-type.rs create mode 100644 tests/ui/pattern/ref-pattern-assoc-type.rs diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 6d4bd981b7b4..7c4004374365 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -131,7 +131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_with_expectation(expr, ExpectHasType(expected)) } - fn check_expr_with_expectation_and_needs( + pub(super) fn check_expr_with_expectation_and_needs( &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a02073f30254..ada671e3e48d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1444,7 +1444,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // referent for the reference that results is *equal to* the // type of the place it is referencing, and not some // supertype thereof. - let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); + let mut init_ty = self.check_expr_with_expectation_and_needs( + init, + ExpectHasType(local_ty), + Needs::maybe_mut_place(m), + ); + // The one exception to the above rule - we permit coercions when the expression has type ! + // This allows `let Foo { ref my_field } = diverging_expr;`. The actual assignment is guaranteed + // to be unreachable, so the soundness concerns with 'ref mut' do not apply. + if init_ty.is_never() { + init_ty = self.demand_coerce(init, init_ty, local_ty, None, AllowTwoPhase::No); + }; + if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) { self.emit_type_mismatch_suggestions( &mut diag, diff --git a/tests/ui/pattern/issue-118113-ref-pattern-never-type.rs b/tests/ui/pattern/issue-118113-ref-pattern-never-type.rs new file mode 100644 index 000000000000..2a6e5620dc36 --- /dev/null +++ b/tests/ui/pattern/issue-118113-ref-pattern-never-type.rs @@ -0,0 +1,10 @@ +// check-pass + +pub struct Foo { + bar: u8 +} + +#[allow(unused_variables)] +fn main() { + let Foo { ref bar } = loop {}; +} diff --git a/tests/ui/pattern/ref-pattern-assoc-type.rs b/tests/ui/pattern/ref-pattern-assoc-type.rs new file mode 100644 index 000000000000..936aa2356513 --- /dev/null +++ b/tests/ui/pattern/ref-pattern-assoc-type.rs @@ -0,0 +1,22 @@ +// check-pass + +trait Call { + type Out; + fn call(self) -> Self::Out; +} + +impl T, T> Call for F { + type Out = T; + fn call(self) -> T { (self)() } +} + +pub struct Foo { + bar: u8 +} + +fn diverge() -> ! { todo!() } + +#[allow(unused_variables)] +fn main() { + let Foo { ref bar } = diverge.call(); +}