Never consider raw pointer casts to be trival

HIR typeck tries to figure out which casts are trivial by doing them as
coercions and seeing whether this works. Since HIR typeck is oblivious
of lifetimes, this doesn't work for pointer casts that only change the
lifetime of the pointee, which are, as borrowck will tell you, not
trivial.

This change makes it so that raw pointer casts are never considered
trivial.

This also incidentally fixes the "trivial cast" lint false positive on
the same code. Unfortunately, "trivial cast" lints are now never emitted
on raw pointer casts, even if they truly are trivial. This could be
fixed by also doing the lint in borrowck for raw pointers specifically.
This commit is contained in:
Nilstrieb 2023-07-02 17:50:24 +02:00
parent e76cb8c498
commit b6657a8ad4
5 changed files with 64 additions and 18 deletions

View File

@ -660,9 +660,21 @@ pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {
} else {
match self.try_coercion_cast(fcx) {
Ok(()) => {
self.trivial_cast_lint(fcx);
debug!(" -> CoercionCast");
fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id);
if self.expr_ty.is_unsafe_ptr() && self.cast_ty.is_unsafe_ptr() {
// When casting a raw pointer to another raw pointer, we cannot convert the cast into
// a coercion because the pointee types might only differ in regions, which HIR typeck
// cannot distinguish. This would cause us to erroneously discard a cast which will
// lead to a borrowck error like #113257.
// We still did a coercion above to unify inference variables for `ptr as _` casts.
// This does cause us to miss some trivial casts in the trival cast lint.
debug!(" -> PointerCast");
} else {
self.trivial_cast_lint(fcx);
debug!(" -> CoercionCast");
fcx.typeck_results
.borrow_mut()
.set_coercion_cast(self.expr.hir_id.local_id);
}
}
Err(_) => {
match self.do_check(fcx) {

View File

@ -6,22 +6,27 @@
let mut _0: *const &u8;
let mut _2: *const &u8;
let mut _3: *const &u8;
let mut _4: *const &u8;
scope 1 (inlined generic_cast::<&u8, &u8>) {
debug x => _3;
let mut _4: *const &u8;
debug x => _4;
let mut _5: *const &u8;
}
bb0: {
StorageLive(_2);
StorageLive(_3);
_3 = _1;
StorageLive(_4);
_4 = _3;
- _2 = move _4 as *const &u8 (PtrToPtr);
+ _2 = move _4;
_4 = _1;
StorageLive(_5);
_5 = _4;
- _3 = move _5 as *const &u8 (PtrToPtr);
+ _3 = move _5;
StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
- _2 = move _3 as *const &u8 (PtrToPtr);
+ _2 = move _3;
_0 = _2;
StorageDead(_3);
StorageDead(_2);
return;
}

View File

@ -4,16 +4,21 @@
fn roundtrip(_1: *const u8) -> *const u8 {
debug x => _1;
let mut _0: *const u8;
let mut _2: *mut u8;
let mut _3: *const u8;
let mut _2: *const u8;
let mut _3: *mut u8;
let mut _4: *const u8;
bb0: {
StorageLive(_2);
StorageLive(_3);
_3 = _1;
_2 = move _3 as *mut u8 (PtrToPtr);
_0 = move _2 as *const u8 (PointerCoercion(MutToConstPointer));
StorageLive(_4);
_4 = _1;
_3 = move _4 as *mut u8 (PtrToPtr);
_2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer));
StorageDead(_4);
StorageDead(_3);
- _0 = move _2 as *const u8 (PtrToPtr);
+ _0 = move _2;
StorageDead(_2);
return;
}

View File

@ -18,8 +18,8 @@ pub fn redundant<'a, 'b: 'a>(x: *const &'a u8) -> *const &'a u8 {
// EMIT_MIR casts.roundtrip.InstSimplify.diff
pub fn roundtrip(x: *const u8) -> *const u8 {
// CHECK-LABEL: fn roundtrip(
// CHECK: _3 = _1;
// CHECK: _2 = move _3 as *mut u8 (PtrToPtr);
// CHECK: _0 = move _2 as *const u8 (PointerCoercion(MutToConstPointer));
// CHECK: _4 = _1;
// CHECK: _3 = move _4 as *mut u8 (PtrToPtr);
// CHECK: _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer));
x as *mut u8 as *const u8
}

View File

@ -0,0 +1,24 @@
// check-pass
// https://github.com/rust-lang/rust/issues/113257
#![deny(trivial_casts)] // The casts here are not trivial.
struct Foo<'a> { a: &'a () }
fn extend_lifetime_very_very_safely<'a>(v: *const Foo<'a>) -> *const Foo<'static> {
// This should pass because raw pointer casts can do anything they want.
v as *const Foo<'static>
}
trait Trait {}
fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) {
ptr as _
}
fn main() {
let unit = ();
let foo = Foo { a: &unit };
let _long: *const Foo<'static> = extend_lifetime_very_very_safely(&foo);
}