Skip to content

Commit 4096397

Browse files
committed
Auto merge of rust-lang#129392 - compiler-errors:raw-ref-op-doesnt-diverge-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`
2 parents a32d4a0 + 5250e5d commit 4096397

File tree

6 files changed

+99
-2
lines changed

6 files changed

+99
-2
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+24-2
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
237237
_ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"),
238238
}
239239

240-
// Any expression that produces a value of type `!` must have diverged
240+
// Any expression that produces a value of type `!` must have diverged,
241+
// unless it's the place of a raw ref expr, or a scrutinee of a match.
241242
if ty.is_never() {
242-
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
243+
if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Deref, _)) {
244+
match self.tcx.parent_hir_node(expr.hir_id) {
245+
hir::Node::Expr(hir::Expr {
246+
kind: hir::ExprKind::AddrOf(hir::BorrowKind::Raw, ..),
247+
..
248+
}) => {}
249+
hir::Node::Expr(hir::Expr {
250+
kind: hir::ExprKind::Let(hir::LetExpr { init: target, .. }),
251+
..
252+
})
253+
| hir::Node::Expr(hir::Expr {
254+
kind: hir::ExprKind::Match(target, _, _), ..
255+
})
256+
| hir::Node::LetStmt(hir::LetStmt { init: Some(target), .. })
257+
if expr.hir_id == target.hir_id => {}
258+
_ => {
259+
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
260+
}
261+
}
262+
} else {
263+
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
264+
}
243265
}
244266

245267
// Record the type, which applies it effects.

compiler/rustc_hir_typeck/src/pat.rs

+9
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
2727
use ty::VariantDef;
2828

2929
use super::report_unexpected_variant_res;
30+
use crate::diverges::Diverges;
3031
use crate::gather_locals::DeclOrigin;
3132
use crate::{errors, FnCtxt, LoweredTy};
3233

@@ -276,6 +277,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
276277
}
277278
};
278279

280+
// All other patterns constitute a read, which causes us to diverge
281+
// if the type is never.
282+
if !matches!(pat.kind, PatKind::Wild | PatKind::Never) {
283+
if ty.is_never() {
284+
self.diverges.set(self.diverges.get() | Diverges::always(pat.span));
285+
}
286+
}
287+
279288
self.write_ty(pat.hir_id, ty);
280289

281290
// (note_1): In most of the cases where (note_1) is referenced
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(never_type)]
2+
3+
fn make_up_a_value<T>() -> T {
4+
unsafe {
5+
//~^ ERROR mismatched types
6+
let x: *const ! = 0 as _;
7+
let _: ! = *x;
8+
// Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this
9+
// is unsound since we act as if it diverges but it doesn't.
10+
}
11+
}
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/diverging-place-match.rs:4:5
3+
|
4+
LL | fn make_up_a_value<T>() -> T {
5+
| - expected this type parameter
6+
LL | / unsafe {
7+
LL | |
8+
LL | | let x: *const ! = 0 as _;
9+
LL | | let _: ! = *x;
10+
LL | | // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this
11+
LL | | // is unsound since we act as if it diverges but it doesn't.
12+
LL | | }
13+
| |_____^ expected type parameter `T`, found `()`
14+
|
15+
= note: expected type parameter `T`
16+
found unit type `()`
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(never_type)]
2+
3+
fn make_up_a_value<T>() -> T {
4+
unsafe {
5+
//~^ ERROR mismatched types
6+
let x: *const ! = 0 as _;
7+
&raw const *x;
8+
// Since `*x` is `!`, HIR typeck used to think that it diverges
9+
// and allowed the block to coerce to any value, leading to UB.
10+
}
11+
}
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/never-place-isnt-diverging.rs:4:5
3+
|
4+
LL | fn make_up_a_value<T>() -> T {
5+
| - expected this type parameter
6+
LL | / unsafe {
7+
LL | |
8+
LL | | let x: *const ! = 0 as _;
9+
LL | | &raw const *x;
10+
LL | | // Since `*x` is `!`, HIR typeck used to think that it diverges
11+
LL | | // and allowed the block to coerce to any value, leading to UB.
12+
LL | | }
13+
| |_____^ expected type parameter `T`, found `()`
14+
|
15+
= note: expected type parameter `T`
16+
found unit type `()`
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)