Change:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --I tried this code: . I expected to see this happen: it compiles, since raw pointer casts should be allowed even for completely different types. Instead, this happened: it fails with: It looks like for some reason the compiler creates a lifetime obligation here. Strangely, it works with : .
I've looked into this. The issue is that typeck considers this cast to be a "coercion cast", because typeck thinks that you could replace the function body with just since the types are the same (it also triggers the lint (allow-by-default)). HIR typeck isn't aware of lifetimes. So then THIR building and MIR building build MIR that doesn't contain a cast but instead just assigns one to the other. The borrow checker is rightfully not pleased. The bug here is that the THIR does not contain an explicit cast anymore. I'm not sure how to best fix this, as the current logic for "is this a no-op cast (called coercion cast in the code)" is just "try to coerce the types in the same way that happens on coercion sites and if it works it has to be useless". Which is wrong in the presence of lifetimes. It seems like the current strategy works this way because it reuses some code in THIR building. Thinking about it a little more, this is very special to pointer casts, because only pointer casts are allowed to change the type like this. Maybe THIR building should just special case pointer casts to always be emitted?
Unfortunately due to this bug there is that should be turned into a raw pointer cast, but we cannot.
I think that's a different case, these are boxes and not raw pointers, and box-box casts are not allowed to do arbitrary changes (and are in fact coercion casts, so the doesn't actually do anything). The error there is correct and this needs a transmute.
I'd like to replace the transmutes by raw pointer casts, which IMO is much preferable, similar to But this bug is in the way of that.

Discussion:
} else { match self.try_coercion_cast(fcx) { Ok(()) => { <del> self.trivial_cast_lint(fcx); debug!(" -> CoercionCast"); fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id); </del> <ins> 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); } </ins> } Err(_) => { match self.do_check(fcx) {

source: self.mirror_expr(source), cast: PointerCoercion::ArrayToPointer, } <del> } else { // check whether this is casting an enum variant discriminant // to prevent cycles, we refer to the discriminant initializer </del> <ins> } else if let hir::ExprKind::Path(ref qpath) = source.kind && let res = self.typeck_results().qpath_res(qpath, source.hir_id) && let ty = self.typeck_results().node_type(source.hir_id) && let ty::Adt(adt_def, args) = ty.kind() && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res { // Check whether this is casting an enum variant discriminant. // To prevent cycles, we refer to the discriminant initializer, </ins> // which is always an integer and thus doesn't need to know the <del> // enum's layout (or its tag type) to compute it during const eval </del> <ins> // enum's layout (or its tag type) to compute it during const eval. </ins> // Example: // enum Foo { //     A,

// The correct solution would be to add symbolic computations to miri, // so we wouldn't have to compute and store the actual value <del> let hir::ExprKind::Path(ref qpath) = source.kind else { return ExprKind::Cast { source: self.mirror_expr(source) }; }; let res = self.typeck_results().qpath_res(qpath, source.hir_id); let ty = self.typeck_results().node_type(source.hir_id); let ty::Adt(adt_def, args) = ty.kind() else { return ExprKind::Cast { source: self.mirror_expr(source) }; }; let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res else { return ExprKind::Cast { source: self.mirror_expr(source) }; }; </del> let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id); let (discr_did, discr_offset) = adt_def.discriminant_def_for_variant(idx);

}; ExprKind::Cast { source } <ins> } else { // Default to `ExprKind::Cast` for all explicit casts. // MIR building then picks the right MIR casts based on the types. ExprKind::Cast { source: self.mirror_expr(source) } </ins> } }

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

fn roundtrip(_1: *const u8) -> *const u8 { debug x => _1; let mut _0: *const u8; <del> let mut _2: *mut u8; let mut _3: *const u8; </del> <ins> let mut _2: *const u8; let mut _3: *mut u8; let mut _4: *const u8; </ins> bb0: { StorageLive(_2); StorageLive(_3); <del> _3 = _1; _2 = move _3 as *mut u8 (PtrToPtr); _0 = move _2 as *const u8 (PointerCoercion(MutToConstPointer)); </del> <ins> StorageLive(_4); _4 = _1; _3 = move _4 as *mut u8 (PtrToPtr); _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer)); StorageDead(_4); </ins> StorageDead(_3); <ins> -         _0 = move _2 as *const u8 (PtrToPtr); +         _0 = move _2; </ins> StorageDead(_2); return; }

// EMIT_MIR casts.roundtrip.InstSimplify.diff pub fn roundtrip(x: *const u8) -> *const u8 { // CHECK-LABEL: fn roundtrip( <del> // CHECK: _3 = _1; // CHECK: _2 = move _3 as *mut u8 (PtrToPtr); // CHECK: _0 = move _2 as *const u8 (PointerCoercion(MutToConstPointer)); </del> <ins> // CHECK: _4 = _1; // CHECK: _3 = move _4 as *mut u8 (PtrToPtr); // CHECK: _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer)); </ins> x as *mut u8 as *const u8 }

<ins> // 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); } </ins>