Skip to content

Commit 0264089

Browse files
Fix up tests
1 parent d04b116 commit 0264089

File tree

7 files changed

+244
-16
lines changed

7 files changed

+244
-16
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ struct Coerce<'a, 'tcx> {
8282
/// See #47489 and #48598
8383
/// See docs on the "AllowTwoPhase" type for a more detailed discussion
8484
allow_two_phase: AllowTwoPhase,
85+
/// Whether we allow `NeverToAny` coercions. This is unsound if we're
86+
/// coercing a place expression without it counting as a read in the MIR.
87+
/// This is a side-effect of HIR not really having a great distinction
88+
/// between places and values.
8589
coerce_never: bool,
8690
}
8791

@@ -1024,7 +1028,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10241028
debug!("coercion::can_with_predicates({:?} -> {:?})", source, target);
10251029

10261030
let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable);
1027-
// We don't ever need two-phase here since we throw out the result of the coercion
1031+
// We don't ever need two-phase here since we throw out the result of the coercion.
1032+
// We also just always set `coerce_never` to true, since this is a heuristic.
10281033
let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
10291034
self.probe(|_| {
10301035
let Ok(ok) = coerce.coerce(source, target) else {
@@ -1037,11 +1042,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10371042
}
10381043

10391044
/// Given a type and a target type, this function will calculate and return
1040-
/// how many dereference steps needed to achieve `expr_ty <: target`. If
1045+
/// how many dereference steps needed to coerce `expr_ty` to `target`. If
10411046
/// it's not possible, return `None`.
1042-
pub(crate) fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option<usize> {
1047+
pub(crate) fn deref_steps_for_suggestion(
1048+
&self,
1049+
expr_ty: Ty<'tcx>,
1050+
target: Ty<'tcx>,
1051+
) -> Option<usize> {
10431052
let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable);
1044-
// We don't ever need two-phase here since we throw out the result of the coercion
1053+
// We don't ever need two-phase here since we throw out the result of the coercion.
10451054
let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
10461055
coerce
10471056
.autoderef(DUMMY_SP, expr_ty)

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2585,7 +2585,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25852585
}
25862586

25872587
if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
2588-
&& let Some(1) = self.deref_steps(expected, checked_ty)
2588+
&& let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty)
25892589
{
25902590
// We have `*&T`, check if what was expected was `&T`.
25912591
// If so, we may want to suggest removing a `*`.
@@ -2715,7 +2715,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27152715
}
27162716
}
27172717
(_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => {
2718-
if let Some(steps) = self.deref_steps(ty_a, ty_b)
2718+
if let Some(steps) = self.deref_steps_for_suggestion(ty_a, ty_b)
27192719
// Only suggest valid if dereferencing needed.
27202720
&& steps > 0
27212721
// The pointer type implements `Copy` trait so the suggestion is always valid.
@@ -2759,7 +2759,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27592759
}
27602760
}
27612761
_ if sp == expr.span => {
2762-
if let Some(mut steps) = self.deref_steps(checked_ty, expected) {
2762+
if let Some(mut steps) = self.deref_steps_for_suggestion(checked_ty, expected) {
27632763
let mut expr = expr.peel_blocks();
27642764
let mut prefix_span = expr.span.shrink_to_lo();
27652765
let mut remove = String::new();

src/tools/miri/tests/pass/underscore_pattern.rs

+48-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Various tests ensuring that underscore patterns really just construct the place, but don't check its contents.
22
#![feature(strict_provenance)]
3+
#![feature(never_type)]
4+
35
use std::ptr;
46

57
fn main() {
@@ -9,6 +11,7 @@ fn main() {
911
invalid_let();
1012
dangling_let_type_annotation();
1113
invalid_let_type_annotation();
14+
never();
1215
}
1316

1417
fn dangling_match() {
@@ -34,13 +37,25 @@ fn invalid_match() {
3437
_ => {}
3538
}
3639
}
40+
41+
unsafe {
42+
let x: Uninit<!> = Uninit { uninit: () };
43+
match x.value {
44+
_ => {}
45+
}
46+
}
3747
}
3848

3949
fn dangling_let() {
4050
unsafe {
4151
let ptr = ptr::without_provenance::<bool>(0x40);
4252
let _ = *ptr;
4353
}
54+
55+
unsafe {
56+
let ptr = ptr::without_provenance::<!>(0x40);
57+
let _ = *ptr;
58+
}
4459
}
4560

4661
fn invalid_let() {
@@ -49,6 +64,12 @@ fn invalid_let() {
4964
let ptr = ptr::addr_of!(val).cast::<bool>();
5065
let _ = *ptr;
5166
}
67+
68+
unsafe {
69+
let val = 3u8;
70+
let ptr = ptr::addr_of!(val).cast::<!>();
71+
let _ = *ptr;
72+
}
5273
}
5374

5475
// Adding a type annotation used to change how MIR is generated, make sure we cover both cases.
@@ -57,6 +78,11 @@ fn dangling_let_type_annotation() {
5778
let ptr = ptr::without_provenance::<bool>(0x40);
5879
let _: bool = *ptr;
5980
}
81+
82+
unsafe {
83+
let ptr = ptr::without_provenance::<!>(0x40);
84+
let _: ! = *ptr;
85+
}
6086
}
6187

6288
fn invalid_let_type_annotation() {
@@ -65,7 +91,27 @@ fn invalid_let_type_annotation() {
6591
let ptr = ptr::addr_of!(val).cast::<bool>();
6692
let _: bool = *ptr;
6793
}
94+
95+
unsafe {
96+
let val = 3u8;
97+
let ptr = ptr::addr_of!(val).cast::<!>();
98+
let _: ! = *ptr;
99+
}
68100
}
69101

70-
// FIXME: we should also test `!`, not just `bool` -- but that s currently buggy:
71-
// https://github.com/rust-lang/rust/issues/117288
102+
// Regression test from <https://github.com/rust-lang/rust/issues/117288>.
103+
fn never() {
104+
unsafe {
105+
let x = 3u8;
106+
let x: *const ! = &x as *const u8 as *const _;
107+
let _: ! = *x;
108+
}
109+
110+
// Without a type annotation, make sure we don't implicitly coerce `!` to `()`
111+
// when we do the noop `*x`.
112+
unsafe {
113+
let x = 3u8;
114+
let x: *const ! = &x as *const u8 as *const _;
115+
let _ = *x;
116+
}
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// MIR for `main` after SimplifyLocals-final
2+
3+
fn main() -> () {
4+
let mut _0: ();
5+
let _1: u8;
6+
let mut _2: *const !;
7+
let mut _3: *const u8;
8+
let _4: u8;
9+
let mut _5: *const !;
10+
let mut _6: *const u8;
11+
scope 1 {
12+
debug x => _1;
13+
scope 2 {
14+
debug x => _2;
15+
scope 3 {
16+
}
17+
}
18+
}
19+
scope 4 {
20+
debug x => _4;
21+
scope 5 {
22+
debug x => _5;
23+
scope 6 {
24+
}
25+
}
26+
}
27+
28+
bb0: {
29+
StorageLive(_1);
30+
_1 = const 3_u8;
31+
StorageLive(_2);
32+
StorageLive(_3);
33+
_3 = &raw const _1;
34+
_2 = move _3 as *const ! (PtrToPtr);
35+
StorageDead(_3);
36+
StorageDead(_2);
37+
StorageDead(_1);
38+
StorageLive(_4);
39+
_4 = const 3_u8;
40+
StorageLive(_5);
41+
StorageLive(_6);
42+
_6 = &raw const _4;
43+
_5 = move _6 as *const ! (PtrToPtr);
44+
StorageDead(_6);
45+
StorageDead(_5);
46+
StorageDead(_4);
47+
return;
48+
}
49+
}

tests/mir-opt/uninhabited_not_read.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// skip-filecheck
2+
3+
//@ edition: 2021
4+
// In ed 2021 and below, we don't fallback `!` to `()`.
5+
// This would introduce a `! -> ()` coercion which would
6+
// be UB if we didn't disallow this explicitly.
7+
8+
#![feature(never_type)]
9+
10+
// EMIT_MIR uninhabited_not_read.main.SimplifyLocals-final.after.mir
11+
fn main() {
12+
// With a type annotation
13+
unsafe {
14+
let x = 3u8;
15+
let x: *const ! = &x as *const u8 as *const _;
16+
let _: ! = *x;
17+
}
18+
19+
// Without a type annotation, make sure we don't implicitly coerce `!` to `()`
20+
// when we do the noop `*x`.
21+
unsafe {
22+
let x = 3u8;
23+
let x: *const ! = &x as *const u8 as *const _;
24+
let _ = *x;
25+
}
26+
}
27+

tests/ui/never_type/diverging-place-match.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![feature(never_type)]
22

3-
fn make_up_a_value<T>() -> T {
3+
fn not_a_read() -> ! {
44
unsafe {
55
//~^ ERROR mismatched types
66
let x: *const ! = 0 as _;
@@ -10,4 +10,38 @@ fn make_up_a_value<T>() -> T {
1010
}
1111
}
1212

13+
fn not_a_read_implicit() -> ! {
14+
unsafe {
15+
//~^ ERROR mismatched types
16+
let x: *const ! = 0 as _;
17+
let _ = *x;
18+
}
19+
}
20+
21+
fn not_a_read_guide_coercion() -> ! {
22+
unsafe {
23+
//~^ ERROR mismatched types
24+
let x: *const ! = 0 as _;
25+
let _: () = *x;
26+
//~^ ERROR mismatched types
27+
}
28+
}
29+
30+
fn empty_match() -> ! {
31+
unsafe {
32+
//~^ ERROR mismatched types
33+
let x: *const ! = 0 as _;
34+
match *x { _ => {} };
35+
}
36+
}
37+
38+
fn field_projection() -> ! {
39+
unsafe {
40+
//~^ ERROR mismatched types
41+
let x: *const (!, ()) = 0 as _;
42+
let _ = (*x).0;
43+
// ^ I think this is still UB, but because of the inbounds projection.
44+
}
45+
}
46+
1347
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,83 @@
11
error[E0308]: mismatched types
22
--> $DIR/diverging-place-match.rs:4:5
33
|
4-
LL | fn make_up_a_value<T>() -> T {
5-
| - expected this type parameter
64
LL | / unsafe {
75
LL | |
86
LL | | let x: *const ! = 0 as _;
97
LL | | let _: ! = *x;
108
LL | | // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this
119
LL | | // is unsound since we act as if it diverges but it doesn't.
1210
LL | | }
13-
| |_____^ expected type parameter `T`, found `()`
11+
| |_____^ expected `!`, found `()`
1412
|
15-
= note: expected type parameter `T`
16-
found unit type `()`
13+
= note: expected type `!`
14+
found unit type `()`
1715

18-
error: aborting due to 1 previous error
16+
error[E0308]: mismatched types
17+
--> $DIR/diverging-place-match.rs:14:5
18+
|
19+
LL | / unsafe {
20+
LL | |
21+
LL | | let x: *const ! = 0 as _;
22+
LL | | let _ = *x;
23+
LL | | }
24+
| |_____^ expected `!`, found `()`
25+
|
26+
= note: expected type `!`
27+
found unit type `()`
28+
29+
error[E0308]: mismatched types
30+
--> $DIR/diverging-place-match.rs:25:21
31+
|
32+
LL | let _: () = *x;
33+
| -- ^^ expected `()`, found `!`
34+
| |
35+
| expected due to this
36+
|
37+
= note: expected unit type `()`
38+
found type `!`
39+
40+
error[E0308]: mismatched types
41+
--> $DIR/diverging-place-match.rs:22:5
42+
|
43+
LL | / unsafe {
44+
LL | |
45+
LL | | let x: *const ! = 0 as _;
46+
LL | | let _: () = *x;
47+
LL | |
48+
LL | | }
49+
| |_____^ expected `!`, found `()`
50+
|
51+
= note: expected type `!`
52+
found unit type `()`
53+
54+
error[E0308]: mismatched types
55+
--> $DIR/diverging-place-match.rs:31:5
56+
|
57+
LL | / unsafe {
58+
LL | |
59+
LL | | let x: *const ! = 0 as _;
60+
LL | | match *x { _ => {} };
61+
LL | | }
62+
| |_____^ expected `!`, found `()`
63+
|
64+
= note: expected type `!`
65+
found unit type `()`
66+
67+
error[E0308]: mismatched types
68+
--> $DIR/diverging-place-match.rs:39:5
69+
|
70+
LL | / unsafe {
71+
LL | |
72+
LL | | let x: *const (!, ()) = 0 as _;
73+
LL | | let _ = (*x).0;
74+
LL | | // ^ I think this is still UB, but because of the inbounds projection.
75+
LL | | }
76+
| |_____^ expected `!`, found `()`
77+
|
78+
= note: expected type `!`
79+
found unit type `()`
80+
81+
error: aborting due to 6 previous errors
1982

2083
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)