Skip to content

Commit f9989d0

Browse files
committed
Always make ptr->ptr casts explicit in THIR
THIR building doesn't produce `ExprKind::Cast` for trivial casts. This is all fine and good on its own but causes problems with raw pointers. Region-unaware HIR typeck considers casting `*const Foo<'short>` to `*const Foo<'long>` to be a trivial cast because they're the same type modulo regions, but borrowck disagrees, emitting errors. We now force all raw pointer casts to go through `ExprKind::Cast`, which allows MIR building to pick the right casts that make borrowck blissfully unaware of our lifetime crimes.
1 parent 5633798 commit f9989d0

File tree

3 files changed

+40
-12
lines changed

3 files changed

+40
-12
lines changed

compiler/rustc_mir_build/src/thir/cx/expr.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -172,21 +172,28 @@ impl<'tcx> Cx<'tcx> {
172172

173173
/// Lowers a cast expression.
174174
///
175-
/// Dealing with user type annotations is left to the caller.
175+
/// Dealing with user type annotations is left to the caller,
176+
/// but this method does look at the inferred target type.
176177
fn mirror_expr_cast(
177178
&mut self,
178179
source: &'tcx hir::Expr<'tcx>,
179180
temp_lifetime: Option<Scope>,
180181
span: Span,
182+
target_ty: Ty<'tcx>,
181183
) -> ExprKind<'tcx> {
182184
let tcx = self.tcx;
183-
184-
// Check to see if this cast is a "coercion cast", where the cast is actually done
185-
// using a coercion (or is a no-op).
186-
if self.typeck_results().is_coercion_cast(source.hir_id) {
185+
let source_ty = self.typeck_results().expr_ty(source);
186+
187+
// Raw pointer to raw pointer casts should always be explicit because they can change
188+
// regions, which HIR typeck cannot know, see #113257.
189+
if source_ty.is_unsafe_ptr() && target_ty.is_unsafe_ptr() {
190+
ExprKind::Cast { source: self.mirror_expr(source) }
191+
} else if self.typeck_results().is_coercion_cast(source.hir_id) {
192+
// Check to see if this cast is a "coercion cast", where the cast is actually done
193+
// using a coercion (or is a no-op).
187194
// Convert the lexpr to a vexpr.
188195
ExprKind::Use { source: self.mirror_expr(source) }
189-
} else if self.typeck_results().expr_ty(source).is_ref() {
196+
} else if source_ty.is_ref() {
190197
// Special cased so that we can type check that the element
191198
// type of the source matches the pointed to type of the
192199
// destination.
@@ -755,7 +762,7 @@ impl<'tcx> Cx<'tcx> {
755762
expr, cast_ty.hir_id, user_ty,
756763
);
757764

758-
let cast = self.mirror_expr_cast(*source, temp_lifetime, expr.span);
765+
let cast = self.mirror_expr_cast(*source, temp_lifetime, expr.span, expr_ty);
759766

760767
if let Some(user_ty) = user_ty {
761768
// NOTE: Creating a new Expr and wrapping a Cast inside of it may be

tests/mir-opt/casts.redundant.InstSimplify.diff

+10-5
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,23 @@
66
let mut _0: *const &u8;
77
let mut _2: *const &u8;
88
let mut _3: *const &u8;
9+
let mut _4: *const &u8;
910
scope 1 (inlined generic_cast::<&u8, &u8>) {
10-
debug x => _3;
11+
debug x => _4;
1112
}
1213

1314
bb0: {
1415
StorageLive(_2);
1516
StorageLive(_3);
16-
_3 = _1;
17-
- _2 = _3 as *const &u8 (PtrToPtr);
18-
+ _2 = _3;
19-
StorageDead(_3);
17+
StorageLive(_4);
18+
_4 = _1;
19+
- _3 = _4 as *const &u8 (PtrToPtr);
20+
+ _3 = _4;
21+
StorageDead(_4);
22+
- _2 = move _3 as *const &u8 (PtrToPtr);
23+
+ _2 = move _3;
2024
_0 = _2;
25+
StorageDead(_3);
2126
StorageDead(_2);
2227
return;
2328
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// check-pass
2+
3+
// https://github.com/rust-lang/rust/issues/113257
4+
5+
struct Foo<'a> { a: &'a () }
6+
7+
fn extend_lifetime_very_very_safely<'a>(v: *const Foo<'a>) -> *const Foo<'static> {
8+
// This should pass because raw pointer casts can do anything they want.
9+
v as *const Foo<'static>
10+
}
11+
12+
fn main() {
13+
let unit = ();
14+
let foo = Foo { a: &unit };
15+
let _long: *const Foo<'static> = extend_lifetime_very_very_safely(&foo);
16+
}

0 commit comments

Comments
 (0)