diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index fec6efdc0f71b..757d34c9e8617 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -237,9 +237,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), } - // Any expression that produces a value of type `!` must have diverged + // Any expression that produces a value of type `!` must have diverged, + // unless it's the place of a raw ref expr. if ty.is_never() { - self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::AddrOf(hir::BorrowKind::Raw, ..), + .. + }) = self.tcx.parent_hir_node(expr.hir_id) + { + // Taking a raw ref does not constitute a read, so this expression + // evaluating to `!` does not guarantee that the program diverges. + // So do nothing here. + } else { + self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); + } } // Record the type, which applies it effects. diff --git a/tests/ui/never_type/diverging-place-match.rs b/tests/ui/never_type/diverging-place-match.rs new file mode 100644 index 0000000000000..7c41be8d1ae9a --- /dev/null +++ b/tests/ui/never_type/diverging-place-match.rs @@ -0,0 +1,15 @@ +//@ known-bug: #117288 +//@ check-pass + +#![feature(never_type)] + +fn make_up_a_value() -> T { + unsafe { + let x: *const ! = 0 as _; + let _: ! = *x; + // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this + // is unsound since we act as if it diverges but it doesn't. + } +} + +fn main() {} diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.rs b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs new file mode 100644 index 0000000000000..52f4158dbc988 --- /dev/null +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs @@ -0,0 +1,13 @@ +#![feature(never_type)] + +fn make_up_a_value() -> T { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + &raw const *x; + // Since `*x` is `!`, HIR typeck used to think that it diverges + // and allowed the block to coerce to any value, leading to UB. + } +} + +fn main() {} diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr new file mode 100644 index 0000000000000..9eba57dde8fa7 --- /dev/null +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/never-place-isnt-diverging.rs:4:5 + | +LL | fn make_up_a_value() -> T { + | - expected this type parameter +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | &raw const *x; +LL | | // Since `*x` is `!`, HIR typeck used to think that it diverges +LL | | // and allowed the block to coerce to any value, leading to UB. +LL | | } + | |_____^ expected type parameter `T`, found `()` + | + = note: expected type parameter `T` + found unit type `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`.