Skip to content

Commit

Permalink
Auto merge of rust-lang#129392 - compiler-errors:raw-ref-op-doesnt-di…
Browse files Browse the repository at this point in the history
…verge-but-more, r=<try>

[EXPERIMENT] Do not consider match/let/raw-ref of deref that evalautes to ! to diverge

This is the more involved version of rust-lang#129371. It doesn't fully fix rust-lang#117288, since we still need to fix the fact that never type fallback can cause an unintended load via the `NeverToAny` coercion. But I did want to probe crater to see if anyone relies on this behavior currently, since that's almost certainly UB.

r? `@ghost`
  • Loading branch information
bors committed Aug 22, 2024
2 parents a32d4a0 + 5250e5d commit 4096397
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 2 deletions.
26 changes: 24 additions & 2 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,31 @@ 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, or a scrutinee of a match.
if ty.is_never() {
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Deref, _)) {
match self.tcx.parent_hir_node(expr.hir_id) {
hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::AddrOf(hir::BorrowKind::Raw, ..),
..
}) => {}
hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Let(hir::LetExpr { init: target, .. }),
..
})
| hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Match(target, _, _), ..
})
| hir::Node::LetStmt(hir::LetStmt { init: Some(target), .. })
if expr.hir_id == target.hir_id => {}
_ => {
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
}
}
} else {
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
}
}

// Record the type, which applies it effects.
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
use ty::VariantDef;

use super::report_unexpected_variant_res;
use crate::diverges::Diverges;
use crate::gather_locals::DeclOrigin;
use crate::{errors, FnCtxt, LoweredTy};

Expand Down Expand Up @@ -276,6 +277,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};

// All other patterns constitute a read, which causes us to diverge
// if the type is never.
if !matches!(pat.kind, PatKind::Wild | PatKind::Never) {
if ty.is_never() {
self.diverges.set(self.diverges.get() | Diverges::always(pat.span));
}
}

self.write_ty(pat.hir_id, ty);

// (note_1): In most of the cases where (note_1) is referenced
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/never_type/diverging-place-match.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![feature(never_type)]

fn make_up_a_value<T>() -> T {
unsafe {
//~^ ERROR mismatched types
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() {}
20 changes: 20 additions & 0 deletions tests/ui/never_type/diverging-place-match.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0308]: mismatched types
--> $DIR/diverging-place-match.rs:4:5
|
LL | fn make_up_a_value<T>() -> T {
| - expected this type parameter
LL | / unsafe {
LL | |
LL | | let x: *const ! = 0 as _;
LL | | let _: ! = *x;
LL | | // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this
LL | | // is unsound since we act as if it diverges but it doesn't.
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`.
13 changes: 13 additions & 0 deletions tests/ui/raw-ref-op/never-place-isnt-diverging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![feature(never_type)]

fn make_up_a_value<T>() -> 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() {}
20 changes: 20 additions & 0 deletions tests/ui/raw-ref-op/never-place-isnt-diverging.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0308]: mismatched types
--> $DIR/never-place-isnt-diverging.rs:4:5
|
LL | fn make_up_a_value<T>() -> 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`.

0 comments on commit 4096397

Please sign in to comment.