From 47082f53a4d945553dfd8f965b1ba8e76d006ac7 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Tue, 23 Jan 2024 11:54:09 +0000 Subject: [PATCH 1/9] normalize better --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 6 +- .../src/interpret/terminator.rs | 9 +-- compiler/rustc_middle/src/ty/sty.rs | 46 ++++++++++----- .../src/traits/project.rs | 57 +++++++++++++------ tests/ui/traits/pointee-normalize-equate.rs | 55 ++++++++++++++++++ 5 files changed, 129 insertions(+), 44 deletions(-) create mode 100644 tests/ui/traits/pointee-normalize-equate.rs diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index a0f9d5cf7cd36..bd20af72a7a67 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1973,10 +1973,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( match in_elem.kind() { ty::RawPtr(p) => { - let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| { + let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); - assert!(!check_sized); // we are in codegen, so we shouldn't see these types require!( metadata.is_unit(), InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem } @@ -1988,10 +1987,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } match out_elem.kind() { ty::RawPtr(p) => { - let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| { + let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); - assert!(!check_sized); // we are in codegen, so we shouldn't see these types require!( metadata.is_unit(), InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 7b993279f18e8..9d6fa5e05d101 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -372,14 +372,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; if let (Some(caller), Some(callee)) = (pointee_ty(caller.ty)?, pointee_ty(callee.ty)?) { // This is okay if they have the same metadata type. - let meta_ty = |ty: Ty<'tcx>| { - let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, |ty| ty); - assert!( - !only_if_sized, - "there should be no more 'maybe has that metadata' types during interpretation" - ); - meta - }; + let meta_ty = |ty: Ty<'tcx>| ty.ptr_metadata_ty(*self.tcx, |ty| ty); return Ok(meta_ty(caller) == meta_ty(callee)); } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index b089f4a9e78b9..24ac920befe77 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2710,12 +2710,12 @@ impl<'tcx> Ty<'tcx> { } /// Returns the type of metadata for (potentially fat) pointers to this type, - /// and a boolean signifying if this is conditional on this type being `Sized`. - pub fn ptr_metadata_ty( + /// or the struct tail if the metadata type cannot be determinded. + pub fn ptr_metadata_ty_or_tail( self, tcx: TyCtxt<'tcx>, normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, - ) -> (Ty<'tcx>, bool) { + ) -> Result, Ty<'tcx>> { let tail = tcx.struct_tail_with_normalize(self, normalize, || {}); match tail.kind() { // Sized types @@ -2739,29 +2739,45 @@ impl<'tcx> Ty<'tcx> { | ty::Foreign(..) // `dyn*` has no metadata | ty::Dynamic(_, _, ty::DynStar) - // If returned by `struct_tail_without_normalization` this is a unit struct + // If returned by `struct_tail_with_normalize` this is a unit struct // without any fields, or not a struct, and therefore is Sized. | ty::Adt(..) - // If returned by `struct_tail_without_normalization` this is the empty tuple, + // If returned by `struct_tail_with_normalize` this is the empty tuple, // a.k.a. unit type, which is Sized - | ty::Tuple(..) => (tcx.types.unit, false), + | ty::Tuple(..) => Ok(tcx.types.unit), + + ty::Str | ty::Slice(_) => Ok(tcx.types.usize), - ty::Str | ty::Slice(_) => (tcx.types.usize, false), ty::Dynamic(_, _, ty::Dyn) => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None); - (tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false) - }, + Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()])) + } - // type parameters only have unit metadata if they're sized, so return true - // to make sure we double check this during confirmation - ty::Param(_) | ty::Alias(..) => (tcx.types.unit, true), + // We don't know the metadata of `self`, but it must be equal to the + // metadata of `tail`. + ty::Param(_) | ty::Alias(..) => Err(tail), ty::Infer(ty::TyVar(_)) | ty::Bound(..) | ty::Placeholder(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("`ptr_metadata_ty` applied to unexpected type: {:?} (tail = {:?})", self, tail) - } + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!( + "`ptr_metadata_ty_or_tail` applied to unexpected type: {self:?} (tail = {tail:?})" + ), + } + } + + /// Returns the type of metadata for (potentially fat) pointers to this type. + /// Causes an ICE if the metadata type cannot be determined. + pub fn ptr_metadata_ty( + self, + tcx: TyCtxt<'tcx>, + normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, + ) -> Ty<'tcx> { + match self.ptr_metadata_ty_or_tail(tcx, normalize) { + Ok(metadata) => metadata, + Err(tail) => bug!( + "`ptr_metadata_ty` failed to get metadata for type: {self:?} (tail = {tail:?})" + ), } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index abbc2066eac16..5d5145bf74fa3 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1919,7 +1919,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // type parameters, opaques, and unnormalized projections have pointer // metadata if they're known (e.g. by the param_env) to be sized ty::Param(_) | ty::Alias(..) - if selcx.infcx.predicate_must_hold_modulo_regions( + if self_ty != tail || selcx.infcx.predicate_must_hold_modulo_regions( &obligation.with( selcx.tcx(), ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]), @@ -2278,7 +2278,8 @@ fn confirm_builtin_candidate<'cx, 'tcx>( let args = tcx.mk_args(&[self_ty.into()]); let lang_items = tcx.lang_items(); let item_def_id = obligation.predicate.def_id; - let trait_def_id = tcx.trait_of_item(item_def_id).unwrap(); + let trait_def_id: rustc_span::def_id::DefId = tcx.trait_of_item(item_def_id).unwrap(); + let mut potentially_unnormalized = false; let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) { let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); assert_eq!(discriminant_def_id, item_def_id); @@ -2289,7 +2290,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>( assert_eq!(metadata_def_id, item_def_id); let mut obligations = Vec::new(); - let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| { + let normalize = |ty| { normalize_with_depth_to( selcx, obligation.param_env, @@ -2298,17 +2299,34 @@ fn confirm_builtin_candidate<'cx, 'tcx>( ty, &mut obligations, ) - }); - if check_is_sized { - let sized_predicate = ty::TraitRef::from_lang_item( - tcx, - LangItem::Sized, - obligation.cause.span(), - [self_ty], - ); - obligations.push(obligation.with(tcx, sized_predicate)); - } - (metadata_ty.into(), obligations) + }; + let metadata = match self_ty.ptr_metadata_ty_or_tail(tcx, normalize) { + Ok(metadata) => metadata, + Err(tail) => { + let sized_predicate = ty::TraitRef::from_lang_item( + tcx, + LangItem::Sized, + obligation.cause.span(), + [self_ty], + ); + let sized_obligation = obligation.with(tcx, sized_predicate); + if self_ty == tail + || selcx.infcx.predicate_must_hold_considering_regions(&sized_obligation) + { + // If the `self_ty` is `Sized`, then the metadata is `()`. + // We check this before projecting to the metadata of `tail`, + // because we may know `self_ty: Sized`, but not `tail: Sized`. + obligations.push(sized_obligation); + tcx.types.unit + } else { + // We know that `self_ty` has the same metadata as `tail`. This allows + // us to prove predicates like `Wrapper::Metadata == T::Metadata`. + potentially_unnormalized = true; + Ty::new_projection(tcx, metadata_def_id, [tail]) + } + } + }; + (metadata.into(), obligations) } else { bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate); }; @@ -2316,9 +2334,14 @@ fn confirm_builtin_candidate<'cx, 'tcx>( let predicate = ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, item_def_id, args), term }; - confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) - .with_addl_obligations(obligations) - .with_addl_obligations(data) + confirm_param_env_candidate( + selcx, + obligation, + ty::Binder::dummy(predicate), + potentially_unnormalized, + ) + .with_addl_obligations(obligations) + .with_addl_obligations(data) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( diff --git a/tests/ui/traits/pointee-normalize-equate.rs b/tests/ui/traits/pointee-normalize-equate.rs new file mode 100644 index 0000000000000..7c775b0049794 --- /dev/null +++ b/tests/ui/traits/pointee-normalize-equate.rs @@ -0,0 +1,55 @@ +// check-pass + +#![feature(ptr_metadata)] + +use std::ptr::{self, Pointee}; + +fn cast_same_meta(ptr: *const T) -> *const U +where + T: Pointee::Metadata>, +{ + let (thin, meta) = ptr.to_raw_parts(); + ptr::from_raw_parts(thin, meta) +} + +struct Wrapper(T); + +// if `Wrapper: ?Sized` then normalize `Wrapper::Metadata` -> `T::Metadata` +fn wrapper_to_tail(ptr: *const T) -> *const Wrapper { + cast_same_meta(ptr) +} + +// if `Wrapper: Sized` then normalize `Wrapper::Metadata` -> `()` +fn wrapper_to_unit(ptr: *const ()) -> *const Wrapper +where + Wrapper: Sized, +{ + cast_same_meta(ptr) +} + +trait Project { + type Assoc: ?Sized; +} + +struct WrapperProject(T::Assoc); + +// normalize `WrapperProject::Metadata` -> `T::Assoc::Metadata` +fn wrapper_project(ptr: *const T::Assoc) -> *const WrapperProject { + cast_same_meta(ptr) +} + +// In this example, `WrapperProject<&'a T>` is `Sized` modulo regions, +// but `?Sized` considering regions. +// Normalize `WrapperProject<&'a T>::Metadata` -> `<&'a T as Project>::Assoc::Metadata` +// and don't require `WrapperProject<&'a T>: Sized`. +fn sized_modulo_regions<'a, T: ?Sized + 'static>( + ptr: *const <&'a T as Project>::Assoc, +) -> *const WrapperProject<&'a T> +where + for<'b> &'b T: Project, + WrapperProject<&'static T>: Sized, +{ + cast_same_meta(ptr) +} + +fn main() {} From a37053f6e4e3aaf66a6936d6b54413fb9e4c2ee2 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Thu, 18 Jan 2024 20:53:52 +0100 Subject: [PATCH 2/9] consolidate and improve fat pointer cast tests --- tests/ui/cast/fat-ptr-cast-rpass.rs | 29 ++ tests/ui/cast/fat-ptr-cast.rs | 78 ++++- tests/ui/cast/fat-ptr-cast.stderr | 293 ++++++++++++++++-- tests/ui/mismatched_types/cast-rfc0401.rs | 72 ----- tests/ui/mismatched_types/cast-rfc0401.stderr | 253 --------------- 5 files changed, 378 insertions(+), 347 deletions(-) delete mode 100644 tests/ui/mismatched_types/cast-rfc0401.rs delete mode 100644 tests/ui/mismatched_types/cast-rfc0401.stderr diff --git a/tests/ui/cast/fat-ptr-cast-rpass.rs b/tests/ui/cast/fat-ptr-cast-rpass.rs index f5747eb8b9666..f55b017c19b46 100644 --- a/tests/ui/cast/fat-ptr-cast-rpass.rs +++ b/tests/ui/cast/fat-ptr-cast-rpass.rs @@ -23,6 +23,28 @@ fn main() { let b: *const [i32] = a; assert_eq!(a as usize, b as *const () as usize); + // Casting to a different type keeps the metadata. + let c = b as *const [u8]; + unsafe { + assert_eq!((*a).len(), (*c).len()); + } + + // `str` <-> `[T]` conversions are allowed. + let a: *const [u32] = &[1953723730, 544434464, 2053205345, 560426601]; + let b = a as *const str; + unsafe { + if cfg!(target_endian = "little") { + assert_eq!((*b), *"Rust"); + } else if cfg!(target_endian = "big") { + assert_eq!((*b), *"tsuR"); + } + } + let a: *const str = "hello"; + let b = a as *const [u8]; + unsafe { + assert_eq!((*b), [104, 101, 108, 108, 111]); + } + // And conversion to a void pointer/address for trait objects too. let a: *mut dyn Foo = &mut Bar; let b = a as *mut () as usize; @@ -31,4 +53,11 @@ fn main() { assert_eq!(b, d); assert_eq!(c, d); + + // Adding auto traits is OK. + let _ = a as *mut (dyn Foo + Send); + + // Casting between auto-trait-only trait objects is OK. + let unprincipled: *mut dyn Send = &mut Bar; + let _ = unprincipled as *mut dyn Sync; } diff --git a/tests/ui/cast/fat-ptr-cast.rs b/tests/ui/cast/fat-ptr-cast.rs index b5276dc619bfb..3d36d2298d14b 100644 --- a/tests/ui/cast/fat-ptr-cast.rs +++ b/tests/ui/cast/fat-ptr-cast.rs @@ -1,4 +1,12 @@ -trait Trait {} +trait Foo { fn foo(&self) {} } +impl Foo for T {} + +trait Bar { fn foo(&self) {} } +impl Bar for T {} + +enum E { + A, B +} // Make sure casts between thin-pointer <-> fat pointer obey RFC401 fn main() { @@ -12,22 +20,84 @@ fn main() { a as i16; //~ ERROR casting `&[i32]` as `i16` is invalid a as u32; //~ ERROR casting `&[i32]` as `u32` is invalid b as usize; //~ ERROR non-primitive cast - p as usize; - //~^ ERROR casting + p as usize; //~ ERROR casting // #22955 q as *const [i32]; //~ ERROR cannot cast // #21397 - let t: *mut (dyn Trait + 'static) = 0 as *mut _; + let t: *mut (dyn Foo + 'static) = 0 as *mut _; //~^ ERROR cannot cast `usize` to a pointer that is wide let mut fail: *const str = 0 as *const str; //~^ ERROR cannot cast `usize` to a pointer that is wide let mut fail2: *const str = 0isize as *const str; //~^ ERROR cannot cast `isize` to a pointer that is wide + + let f: f32 = 1.2; + let v = core::ptr::null::(); + let fat_v : *const [u8] = &[]; + let fat_sv : *const [i8] = &[]; + let foo: &dyn Foo = &f; + + let _ = v as &u8; //~ ERROR non-primitive cast + let _ = v as E; //~ ERROR non-primitive cast + let _ = v as fn(); //~ ERROR non-primitive cast + let _ = v as (u32,); //~ ERROR non-primitive cast + let _ = Some(&v) as *const u8; //~ ERROR non-primitive cast + + let _ = v as f32; //~ ERROR is invalid + let _ = main as f64; //~ ERROR is invalid + let _ = &v as usize; //~ ERROR is invalid + let _ = f as *const u8; //~ ERROR is invalid + let _ = 3_i32 as bool; //~ ERROR cannot cast + let _ = E::A as bool; //~ ERROR cannot cast + let _ = 0x61u32 as char; //~ ERROR can be cast as + + let _ = false as f32; //~ ERROR is invalid + let _ = E::A as f32; //~ ERROR is invalid + let _ = 'a' as f32; //~ ERROR is invalid + + let _ = false as *const u8; //~ ERROR is invalid + let _ = E::A as *const u8; //~ ERROR is invalid + let _ = 'a' as *const u8; //~ ERROR is invalid + + let _ = 42usize as *const [u8]; //~ ERROR cannot cast `usize` to a pointer that is wide + let _ = v as *const [u8]; //~ ERROR cannot cast + let _ = fat_v as *const dyn Foo; //~ ERROR the size for values of type + let _ = foo as *const str; //~ ERROR is invalid + let _ = foo as *mut str; //~ ERROR is invalid + let _ = main as *mut str; //~ ERROR is invalid + let _ = &f as *mut f32; //~ ERROR is invalid + let _ = &f as *const f64; //~ ERROR is invalid + let _ = fat_sv as usize; //~ ERROR is invalid + + let a : *const str = "hello"; + let _ = a as *const dyn Foo; //~ ERROR the size for values of type + + // check no error cascade + let _ = main.f as *const u32; //~ ERROR no field + + let cf: *const dyn Foo = &0; + let _ = cf as *const [u16]; //~ ERROR is invalid + let _ = cf as *const dyn Bar; //~ ERROR is invalid + + // casting principal away is not allowed for now + let _ = cf as *const dyn Send; //~ ERROR is invalid + + vec![0.0].iter().map(|s| s as f32).collect::>(); //~ ERROR is invalid } fn foo() { let s = 0 as *const T; //~^ ERROR cannot cast `usize` to a pointer that may be wide } + +fn illegal_cast(u: *const U) -> *const V +{ + u as *const V //~ ERROR is invalid +} + +fn illegal_cast_2(u: *const U) -> *const str +{ + u as *const str //~ ERROR is invalid +} diff --git a/tests/ui/cast/fat-ptr-cast.stderr b/tests/ui/cast/fat-ptr-cast.stderr index 18e7b68ff3c22..951bc9bb906dd 100644 --- a/tests/ui/cast/fat-ptr-cast.stderr +++ b/tests/ui/cast/fat-ptr-cast.stderr @@ -1,5 +1,11 @@ +error[E0609]: no field `f` on type `fn() {main}` + --> $DIR/fat-ptr-cast.rs:78:18 + | +LL | let _ = main.f as *const u32; + | ^ unknown field + error[E0606]: casting `&[i32]` as `usize` is invalid - --> $DIR/fat-ptr-cast.rs:10:5 + --> $DIR/fat-ptr-cast.rs:18:5 | LL | a as usize; | ^^^^^^^^^^ @@ -7,7 +13,7 @@ LL | a as usize; = help: cast through a raw pointer first error[E0606]: casting `&[i32]` as `isize` is invalid - --> $DIR/fat-ptr-cast.rs:11:5 + --> $DIR/fat-ptr-cast.rs:19:5 | LL | a as isize; | ^^^^^^^^^^ @@ -15,7 +21,7 @@ LL | a as isize; = help: cast through a raw pointer first error[E0606]: casting `&[i32]` as `i16` is invalid - --> $DIR/fat-ptr-cast.rs:12:5 + --> $DIR/fat-ptr-cast.rs:20:5 | LL | a as i16; | ^^^^^^^^ @@ -23,7 +29,7 @@ LL | a as i16; = help: cast through a raw pointer first error[E0606]: casting `&[i32]` as `u32` is invalid - --> $DIR/fat-ptr-cast.rs:13:5 + --> $DIR/fat-ptr-cast.rs:21:5 | LL | a as u32; | ^^^^^^^^ @@ -31,13 +37,13 @@ LL | a as u32; = help: cast through a raw pointer first error[E0605]: non-primitive cast: `Box<[i32]>` as `usize` - --> $DIR/fat-ptr-cast.rs:14:5 + --> $DIR/fat-ptr-cast.rs:22:5 | LL | b as usize; | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0606]: casting `*const [i32]` as `usize` is invalid - --> $DIR/fat-ptr-cast.rs:15:5 + --> $DIR/fat-ptr-cast.rs:23:5 | LL | p as usize; | ^^^^^^^^^^ @@ -45,21 +51,21 @@ LL | p as usize; = help: cast through a thin pointer first error[E0607]: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]` - --> $DIR/fat-ptr-cast.rs:19:5 + --> $DIR/fat-ptr-cast.rs:26:5 | LL | q as *const [i32]; | ^^^^^^^^^^^^^^^^^ error[E0606]: cannot cast `usize` to a pointer that is wide - --> $DIR/fat-ptr-cast.rs:22:46 + --> $DIR/fat-ptr-cast.rs:29:44 | -LL | let t: *mut (dyn Trait + 'static) = 0 as *mut _; - | - ^^^^^^ creating a `*mut (dyn Trait + 'static)` requires both an address and a vtable - | | - | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` +LL | let t: *mut (dyn Foo + 'static) = 0 as *mut _; + | - ^^^^^^ creating a `*mut (dyn Foo + 'static)` requires both an address and a vtable + | | + | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` error[E0606]: cannot cast `usize` to a pointer that is wide - --> $DIR/fat-ptr-cast.rs:24:37 + --> $DIR/fat-ptr-cast.rs:31:37 | LL | let mut fail: *const str = 0 as *const str; | - ^^^^^^^^^^ creating a `*const str` requires both an address and a length @@ -67,22 +73,273 @@ LL | let mut fail: *const str = 0 as *const str; | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` error[E0606]: cannot cast `isize` to a pointer that is wide - --> $DIR/fat-ptr-cast.rs:26:43 + --> $DIR/fat-ptr-cast.rs:33:43 | LL | let mut fail2: *const str = 0isize as *const str; | ------ ^^^^^^^^^^ creating a `*const str` requires both an address and a length | | | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` +error[E0605]: non-primitive cast: `*const u8` as `&u8` + --> $DIR/fat-ptr-cast.rs:42:13 + | +LL | let _ = v as &u8; + | ^^^^^^^^ invalid cast + | +help: consider borrowing the value + | +LL - let _ = v as &u8; +LL + let _ = &*v; + | + +error[E0605]: non-primitive cast: `*const u8` as `E` + --> $DIR/fat-ptr-cast.rs:43:13 + | +LL | let _ = v as E; + | ^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error[E0605]: non-primitive cast: `*const u8` as `fn()` + --> $DIR/fat-ptr-cast.rs:44:13 + | +LL | let _ = v as fn(); + | ^^^^^^^^^ invalid cast + +error[E0605]: non-primitive cast: `*const u8` as `(u32,)` + --> $DIR/fat-ptr-cast.rs:45:13 + | +LL | let _ = v as (u32,); + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error[E0605]: non-primitive cast: `Option<&*const u8>` as `*const u8` + --> $DIR/fat-ptr-cast.rs:46:13 + | +LL | let _ = Some(&v) as *const u8; + | ^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error[E0606]: casting `*const u8` as `f32` is invalid + --> $DIR/fat-ptr-cast.rs:48:13 + | +LL | let _ = v as f32; + | ^^^^^^^^ + +error[E0606]: casting `fn() {main}` as `f64` is invalid + --> $DIR/fat-ptr-cast.rs:49:13 + | +LL | let _ = main as f64; + | ^^^^^^^^^^^ + +error[E0606]: casting `&*const u8` as `usize` is invalid + --> $DIR/fat-ptr-cast.rs:50:13 + | +LL | let _ = &v as usize; + | ^^^^^^^^^^^ + | + = help: cast through a raw pointer first + +error[E0606]: casting `f32` as `*const u8` is invalid + --> $DIR/fat-ptr-cast.rs:51:13 + | +LL | let _ = f as *const u8; + | ^^^^^^^^^^^^^^ + +error[E0054]: cannot cast `i32` as `bool` + --> $DIR/fat-ptr-cast.rs:52:13 + | +LL | let _ = 3_i32 as bool; + | ^^^^^^^^^^^^^ + | +help: compare with zero instead + | +LL | let _ = 3_i32 != 0; + | ~~~~ + +error[E0054]: cannot cast `E` as `bool` + --> $DIR/fat-ptr-cast.rs:53:13 + | +LL | let _ = E::A as bool; + | ^^^^^^^^^^^^ unsupported cast + +error[E0604]: only `u8` can be cast as `char`, not `u32` + --> $DIR/fat-ptr-cast.rs:54:13 + | +LL | let _ = 0x61u32 as char; + | ^^^^^^^^^^^^^^^ + | | + | invalid cast + | help: try `char::from_u32` instead: `char::from_u32(0x61u32)` + +error[E0606]: casting `bool` as `f32` is invalid + --> $DIR/fat-ptr-cast.rs:56:13 + | +LL | let _ = false as f32; + | ^^^^^^^^^^^^ + | + = help: cast through an integer first + +error[E0606]: casting `E` as `f32` is invalid + --> $DIR/fat-ptr-cast.rs:57:13 + | +LL | let _ = E::A as f32; + | ^^^^^^^^^^^ + | + = help: cast through an integer first + +error[E0606]: casting `char` as `f32` is invalid + --> $DIR/fat-ptr-cast.rs:58:13 + | +LL | let _ = 'a' as f32; + | ^^^^^^^^^^ + | + = help: cast through an integer first + +error[E0606]: casting `bool` as `*const u8` is invalid + --> $DIR/fat-ptr-cast.rs:60:13 + | +LL | let _ = false as *const u8; + | ^^^^^^^^^^^^^^^^^^ + +error[E0606]: casting `E` as `*const u8` is invalid + --> $DIR/fat-ptr-cast.rs:61:13 + | +LL | let _ = E::A as *const u8; + | ^^^^^^^^^^^^^^^^^ + +error[E0606]: casting `char` as `*const u8` is invalid + --> $DIR/fat-ptr-cast.rs:62:13 + | +LL | let _ = 'a' as *const u8; + | ^^^^^^^^^^^^^^^^ + +error[E0606]: cannot cast `usize` to a pointer that is wide + --> $DIR/fat-ptr-cast.rs:64:24 + | +LL | let _ = 42usize as *const [u8]; + | ------- ^^^^^^^^^^^ creating a `*const [u8]` requires both an address and a length + | | + | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` + +error[E0607]: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]` + --> $DIR/fat-ptr-cast.rs:65:13 + | +LL | let _ = v as *const [u8]; + | ^^^^^^^^^^^^^^^^ + +error[E0606]: casting `&dyn Foo` as `*const str` is invalid + --> $DIR/fat-ptr-cast.rs:67:13 + | +LL | let _ = foo as *const str; + | ^^^^^^^^^^^^^^^^^ + +error[E0606]: casting `&dyn Foo` as `*mut str` is invalid + --> $DIR/fat-ptr-cast.rs:68:13 + | +LL | let _ = foo as *mut str; + | ^^^^^^^^^^^^^^^ + +error[E0606]: casting `fn() {main}` as `*mut str` is invalid + --> $DIR/fat-ptr-cast.rs:69:13 + | +LL | let _ = main as *mut str; + | ^^^^^^^^^^^^^^^^ + +error[E0606]: casting `&f32` as `*mut f32` is invalid + --> $DIR/fat-ptr-cast.rs:70:13 + | +LL | let _ = &f as *mut f32; + | ^^^^^^^^^^^^^^ + +error[E0606]: casting `&f32` as `*const f64` is invalid + --> $DIR/fat-ptr-cast.rs:71:13 + | +LL | let _ = &f as *const f64; + | ^^^^^^^^^^^^^^^^ + +error[E0606]: casting `*const [i8]` as `usize` is invalid + --> $DIR/fat-ptr-cast.rs:72:13 + | +LL | let _ = fat_sv as usize; + | ^^^^^^^^^^^^^^^ + | + = help: cast through a thin pointer first + +error[E0606]: casting `*const dyn Foo` as `*const [u16]` is invalid + --> $DIR/fat-ptr-cast.rs:81:13 + | +LL | let _ = cf as *const [u16]; + | ^^^^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error[E0606]: casting `*const dyn Foo` as `*const dyn Bar` is invalid + --> $DIR/fat-ptr-cast.rs:82:13 + | +LL | let _ = cf as *const dyn Bar; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error[E0606]: casting `*const dyn Foo` as `*const dyn Send` is invalid + --> $DIR/fat-ptr-cast.rs:85:13 + | +LL | let _ = cf as *const dyn Send; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/fat-ptr-cast.rs:66:13 + | +LL | let _ = fat_v as *const dyn Foo; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: required for the cast from `*const [u8]` to `*const dyn Foo` + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/fat-ptr-cast.rs:75:13 + | +LL | let _ = a as *const dyn Foo; + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: required for the cast from `*const str` to `*const dyn Foo` + +error[E0606]: casting `&{float}` as `f32` is invalid + --> $DIR/fat-ptr-cast.rs:87:30 + | +LL | vec![0.0].iter().map(|s| s as f32).collect::>(); + | ^^^^^^^^ + | +help: dereference the expression + | +LL | vec![0.0].iter().map(|s| *s as f32).collect::>(); + | + + error[E0606]: cannot cast `usize` to a pointer that may be wide - --> $DIR/fat-ptr-cast.rs:31:18 + --> $DIR/fat-ptr-cast.rs:91:18 | LL | let s = 0 as *const T; | - ^^^^^^^^ creating a `*const T` requires both an address and type-specific metadata | | | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` -error: aborting due to 11 previous errors +error[E0606]: casting `*const U` as `*const V` is invalid + --> $DIR/fat-ptr-cast.rs:97:5 + | +LL | u as *const V + | ^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error[E0606]: casting `*const U` as `*const str` is invalid + --> $DIR/fat-ptr-cast.rs:102:5 + | +LL | u as *const str + | ^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 46 previous errors -Some errors have detailed explanations: E0605, E0606, E0607. -For more information about an error, try `rustc --explain E0605`. +Some errors have detailed explanations: E0054, E0277, E0604, E0605, E0606, E0607, E0609. +For more information about an error, try `rustc --explain E0054`. diff --git a/tests/ui/mismatched_types/cast-rfc0401.rs b/tests/ui/mismatched_types/cast-rfc0401.rs deleted file mode 100644 index 57222f45947b4..0000000000000 --- a/tests/ui/mismatched_types/cast-rfc0401.rs +++ /dev/null @@ -1,72 +0,0 @@ -fn illegal_cast(u: *const U) -> *const V -{ - u as *const V //~ ERROR is invalid -} - -fn illegal_cast_2(u: *const U) -> *const str -{ - u as *const str //~ ERROR is invalid -} - -trait Foo { fn foo(&self) {} } -impl Foo for T {} - -trait Bar { fn foo(&self) {} } -impl Bar for T {} - -enum E { - A, B -} - -fn main() -{ - let f: f32 = 1.2; - let v = core::ptr::null::(); - let fat_v : *const [u8] = unsafe { &*core::ptr::null::<[u8; 1]>()}; - let fat_sv : *const [i8] = unsafe { &*core::ptr::null::<[i8; 1]>()}; - let foo: &dyn Foo = &f; - - let _ = v as &u8; //~ ERROR non-primitive cast - let _ = v as E; //~ ERROR non-primitive cast - let _ = v as fn(); //~ ERROR non-primitive cast - let _ = v as (u32,); //~ ERROR non-primitive cast - let _ = Some(&v) as *const u8; //~ ERROR non-primitive cast - - let _ = v as f32; //~ ERROR is invalid - let _ = main as f64; //~ ERROR is invalid - let _ = &v as usize; //~ ERROR is invalid - let _ = f as *const u8; //~ ERROR is invalid - let _ = 3_i32 as bool; //~ ERROR cannot cast - let _ = E::A as bool; //~ ERROR cannot cast - let _ = 0x61u32 as char; //~ ERROR can be cast as - - let _ = false as f32; //~ ERROR is invalid - let _ = E::A as f32; //~ ERROR is invalid - let _ = 'a' as f32; //~ ERROR is invalid - - let _ = false as *const u8; //~ ERROR is invalid - let _ = E::A as *const u8; //~ ERROR is invalid - let _ = 'a' as *const u8; //~ ERROR is invalid - - let _ = 42usize as *const [u8]; //~ ERROR cannot cast `usize` to a pointer that is wide - let _ = v as *const [u8]; //~ ERROR cannot cast - let _ = fat_v as *const dyn Foo; //~ ERROR the size for values of type - let _ = foo as *const str; //~ ERROR is invalid - let _ = foo as *mut str; //~ ERROR is invalid - let _ = main as *mut str; //~ ERROR is invalid - let _ = &f as *mut f32; //~ ERROR is invalid - let _ = &f as *const f64; //~ ERROR is invalid - let _ = fat_sv as usize; //~ ERROR is invalid - - let a : *const str = "hello"; - let _ = a as *const dyn Foo; //~ ERROR the size for values of type - - // check no error cascade - let _ = main.f as *const u32; //~ ERROR no field - - let cf: *const dyn Foo = &0; - let _ = cf as *const [u16]; //~ ERROR is invalid - let _ = cf as *const dyn Bar; //~ ERROR is invalid - - vec![0.0].iter().map(|s| s as f32).collect::>(); //~ ERROR is invalid -} diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr deleted file mode 100644 index 142a52aef13d0..0000000000000 --- a/tests/ui/mismatched_types/cast-rfc0401.stderr +++ /dev/null @@ -1,253 +0,0 @@ -error[E0606]: casting `*const U` as `*const V` is invalid - --> $DIR/cast-rfc0401.rs:3:5 - | -LL | u as *const V - | ^^^^^^^^^^^^^ - | - = note: vtable kinds may not match - -error[E0606]: casting `*const U` as `*const str` is invalid - --> $DIR/cast-rfc0401.rs:8:5 - | -LL | u as *const str - | ^^^^^^^^^^^^^^^ - | - = note: vtable kinds may not match - -error[E0609]: no field `f` on type `fn() {main}` - --> $DIR/cast-rfc0401.rs:65:18 - | -LL | let _ = main.f as *const u32; - | ^ unknown field - -error[E0605]: non-primitive cast: `*const u8` as `&u8` - --> $DIR/cast-rfc0401.rs:29:13 - | -LL | let _ = v as &u8; - | ^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL - let _ = v as &u8; -LL + let _ = &*v; - | - -error[E0605]: non-primitive cast: `*const u8` as `E` - --> $DIR/cast-rfc0401.rs:30:13 - | -LL | let _ = v as E; - | ^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object - -error[E0605]: non-primitive cast: `*const u8` as `fn()` - --> $DIR/cast-rfc0401.rs:31:13 - | -LL | let _ = v as fn(); - | ^^^^^^^^^ invalid cast - -error[E0605]: non-primitive cast: `*const u8` as `(u32,)` - --> $DIR/cast-rfc0401.rs:32:13 - | -LL | let _ = v as (u32,); - | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object - -error[E0605]: non-primitive cast: `Option<&*const u8>` as `*const u8` - --> $DIR/cast-rfc0401.rs:33:13 - | -LL | let _ = Some(&v) as *const u8; - | ^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object - -error[E0606]: casting `*const u8` as `f32` is invalid - --> $DIR/cast-rfc0401.rs:35:13 - | -LL | let _ = v as f32; - | ^^^^^^^^ - -error[E0606]: casting `fn() {main}` as `f64` is invalid - --> $DIR/cast-rfc0401.rs:36:13 - | -LL | let _ = main as f64; - | ^^^^^^^^^^^ - -error[E0606]: casting `&*const u8` as `usize` is invalid - --> $DIR/cast-rfc0401.rs:37:13 - | -LL | let _ = &v as usize; - | ^^^^^^^^^^^ - | - = help: cast through a raw pointer first - -error[E0606]: casting `f32` as `*const u8` is invalid - --> $DIR/cast-rfc0401.rs:38:13 - | -LL | let _ = f as *const u8; - | ^^^^^^^^^^^^^^ - -error[E0054]: cannot cast `i32` as `bool` - --> $DIR/cast-rfc0401.rs:39:13 - | -LL | let _ = 3_i32 as bool; - | ^^^^^^^^^^^^^ - | -help: compare with zero instead - | -LL | let _ = 3_i32 != 0; - | ~~~~ - -error[E0054]: cannot cast `E` as `bool` - --> $DIR/cast-rfc0401.rs:40:13 - | -LL | let _ = E::A as bool; - | ^^^^^^^^^^^^ unsupported cast - -error[E0604]: only `u8` can be cast as `char`, not `u32` - --> $DIR/cast-rfc0401.rs:41:13 - | -LL | let _ = 0x61u32 as char; - | ^^^^^^^^^^^^^^^ - | | - | invalid cast - | help: try `char::from_u32` instead: `char::from_u32(0x61u32)` - -error[E0606]: casting `bool` as `f32` is invalid - --> $DIR/cast-rfc0401.rs:43:13 - | -LL | let _ = false as f32; - | ^^^^^^^^^^^^ - | - = help: cast through an integer first - -error[E0606]: casting `E` as `f32` is invalid - --> $DIR/cast-rfc0401.rs:44:13 - | -LL | let _ = E::A as f32; - | ^^^^^^^^^^^ - | - = help: cast through an integer first - -error[E0606]: casting `char` as `f32` is invalid - --> $DIR/cast-rfc0401.rs:45:13 - | -LL | let _ = 'a' as f32; - | ^^^^^^^^^^ - | - = help: cast through an integer first - -error[E0606]: casting `bool` as `*const u8` is invalid - --> $DIR/cast-rfc0401.rs:47:13 - | -LL | let _ = false as *const u8; - | ^^^^^^^^^^^^^^^^^^ - -error[E0606]: casting `E` as `*const u8` is invalid - --> $DIR/cast-rfc0401.rs:48:13 - | -LL | let _ = E::A as *const u8; - | ^^^^^^^^^^^^^^^^^ - -error[E0606]: casting `char` as `*const u8` is invalid - --> $DIR/cast-rfc0401.rs:49:13 - | -LL | let _ = 'a' as *const u8; - | ^^^^^^^^^^^^^^^^ - -error[E0606]: cannot cast `usize` to a pointer that is wide - --> $DIR/cast-rfc0401.rs:51:24 - | -LL | let _ = 42usize as *const [u8]; - | ------- ^^^^^^^^^^^ creating a `*const [u8]` requires both an address and a length - | | - | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` - -error[E0607]: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]` - --> $DIR/cast-rfc0401.rs:52:13 - | -LL | let _ = v as *const [u8]; - | ^^^^^^^^^^^^^^^^ - -error[E0606]: casting `&dyn Foo` as `*const str` is invalid - --> $DIR/cast-rfc0401.rs:54:13 - | -LL | let _ = foo as *const str; - | ^^^^^^^^^^^^^^^^^ - -error[E0606]: casting `&dyn Foo` as `*mut str` is invalid - --> $DIR/cast-rfc0401.rs:55:13 - | -LL | let _ = foo as *mut str; - | ^^^^^^^^^^^^^^^ - -error[E0606]: casting `fn() {main}` as `*mut str` is invalid - --> $DIR/cast-rfc0401.rs:56:13 - | -LL | let _ = main as *mut str; - | ^^^^^^^^^^^^^^^^ - -error[E0606]: casting `&f32` as `*mut f32` is invalid - --> $DIR/cast-rfc0401.rs:57:13 - | -LL | let _ = &f as *mut f32; - | ^^^^^^^^^^^^^^ - -error[E0606]: casting `&f32` as `*const f64` is invalid - --> $DIR/cast-rfc0401.rs:58:13 - | -LL | let _ = &f as *const f64; - | ^^^^^^^^^^^^^^^^ - -error[E0606]: casting `*const [i8]` as `usize` is invalid - --> $DIR/cast-rfc0401.rs:59:13 - | -LL | let _ = fat_sv as usize; - | ^^^^^^^^^^^^^^^ - | - = help: cast through a thin pointer first - -error[E0606]: casting `*const dyn Foo` as `*const [u16]` is invalid - --> $DIR/cast-rfc0401.rs:68:13 - | -LL | let _ = cf as *const [u16]; - | ^^^^^^^^^^^^^^^^^^ - | - = note: vtable kinds may not match - -error[E0606]: casting `*const dyn Foo` as `*const dyn Bar` is invalid - --> $DIR/cast-rfc0401.rs:69:13 - | -LL | let _ = cf as *const dyn Bar; - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: vtable kinds may not match - -error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/cast-rfc0401.rs:53:13 - | -LL | let _ = fat_v as *const dyn Foo; - | ^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `[u8]` - = note: required for the cast from `*const [u8]` to `*const dyn Foo` - -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/cast-rfc0401.rs:62:13 - | -LL | let _ = a as *const dyn Foo; - | ^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `str` - = note: required for the cast from `*const str` to `*const dyn Foo` - -error[E0606]: casting `&{float}` as `f32` is invalid - --> $DIR/cast-rfc0401.rs:71:30 - | -LL | vec![0.0].iter().map(|s| s as f32).collect::>(); - | ^^^^^^^^ - | -help: dereference the expression - | -LL | vec![0.0].iter().map(|s| *s as f32).collect::>(); - | + - -error: aborting due to 34 previous errors - -Some errors have detailed explanations: E0054, E0277, E0604, E0605, E0606, E0607, E0609. -For more information about an error, try `rustc --explain E0054`. From a84dab3e4ba3432c80f6ef59a3d47fcf7de1257f Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Thu, 18 Jan 2024 15:33:34 +0000 Subject: [PATCH 3/9] add `MetadataCast` built-in trait --- compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_middle/src/traits/select.rs | 13 +- compiler/rustc_span/src/symbol.rs | 1 + .../src/traits/select/candidate_assembly.rs | 46 ++++ .../src/traits/select/confirmation.rs | 28 +++ .../src/traits/select/mod.rs | 24 +- library/core/src/ptr/metadata.rs | 16 ++ library/core/src/ptr/mod.rs | 2 + .../ui/traits/metadata-cast-fail-lifetimes.rs | 39 +++ .../metadata-cast-fail-lifetimes.stderr | 40 +++ tests/ui/traits/metadata-cast-fail-traits.rs | 45 ++++ .../traits/metadata-cast-fail-traits.stderr | 231 ++++++++++++++++++ tests/ui/traits/metadata-cast-pass.rs | 79 ++++++ 13 files changed, 558 insertions(+), 7 deletions(-) create mode 100644 tests/ui/traits/metadata-cast-fail-lifetimes.rs create mode 100644 tests/ui/traits/metadata-cast-fail-lifetimes.stderr create mode 100644 tests/ui/traits/metadata-cast-fail-traits.rs create mode 100644 tests/ui/traits/metadata-cast-fail-traits.stderr create mode 100644 tests/ui/traits/metadata-cast-pass.rs diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 85d10872b3d1d..cbdf34c9ef270 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -155,6 +155,7 @@ language_item_table! { PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None; Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None; DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None; + MetadataCast, sym::metadata_cast, metadata_cast_trait, Target::Trait, GenericRequirement::Minimum(1); Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 64f4af08e12ed..d0d12cbfcf9bb 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -108,7 +108,7 @@ pub enum SelectionCandidate<'tcx> { /// A builtin implementation for some specific traits, used in cases /// where we cannot rely an ordinary library implementations. /// - /// The most notable examples are `sized`, `Copy` and `Clone`. This is also + /// The most notable examples are `Sized`, `Copy` and `Clone`. This is also /// used for the `DiscriminantKind` and `Pointee` trait, both of which have /// an associated type. BuiltinCandidate { @@ -116,6 +116,9 @@ pub enum SelectionCandidate<'tcx> { has_nested: bool, }, + /// Implementation of the `MetadataCast` trait. + MetadataCastCandidate(MetadataCastKind<'tcx>), + /// Implementation of transmutability trait. TransmutabilityCandidate, @@ -176,6 +179,14 @@ pub enum SelectionCandidate<'tcx> { ConstDestructCandidate(Option), } +#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)] +pub enum MetadataCastKind { + /// No further obligations. + Unconditional, + /// `T: MetadataCast` if `T <: U`. + Subtype, +} + /// The result of trait evaluation. The order is important /// here as the evaluation of a list is the maximum of the /// evaluations. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 90a38b26f73ca..715e5f22a4858 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1017,6 +1017,7 @@ symbols! { memtag, message, meta, + metadata_cast, metadata_type, min_align_of, min_align_of_val, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 12aea88e9b6a2..a62544e65149d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -11,6 +11,7 @@ use hir::LangItem; use rustc_hir as hir; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; +use rustc_middle::traits::select::MetadataCastKind; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -100,6 +101,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidate_for_pointer_like(obligation, &mut candidates); } else if lang_items.fn_ptr_trait() == Some(def_id) { self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); + } else if lang_items.metadata_cast_trait() == Some(def_id) { + self.assemble_candidates_for_metadata_cast(obligation, &mut candidates); } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -1158,4 +1161,47 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidates_for_metadata_cast( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + let target = self.infcx.shallow_resolve(target); + + if target.is_unit() { + candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional)); + return; + } + + let source = obligation.self_ty().skip_binder(); + let source = self.infcx.shallow_resolve(source); + + if source.has_non_region_infer() || target.has_non_region_infer() { + candidates.ambiguous = true; + return; + } + + if let ty::Adt(src_def, src_args) = source.kind() + && let ty::Adt(tgt_def, tgt_args) = target.kind() + && src_def == tgt_def + && Some(src_def.did()) == self.tcx().lang_items().dyn_metadata() + { + let src_dyn = src_args.type_at(0); + let tgt_dyn = tgt_args.type_at(0); + + // We could theoretically allow casting the principal away, but `as` casts + // don't allow that, so neither does `MetadataCast` for now. + if let ty::Dynamic(src_pred, _, ty::Dyn) = src_dyn.kind() + && let ty::Dynamic(tgt_pred, _, ty::Dyn) = tgt_dyn.kind() + && src_pred.principal_def_id() == tgt_pred.principal_def_id() + { + candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional)); + return; + } + } + + candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Subtype)); + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 74f388e53a3c9..26a8e026b6a09 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -11,6 +11,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; +use rustc_middle::traits::select::MetadataCastKind; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, @@ -51,6 +52,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, data) } + MetadataCastCandidate(kind) => { + let data = self.confirm_metadata_cast_candidate(obligation, kind)?; + ImplSource::Builtin(BuiltinImplSource::Misc, data) + } + TransmutabilityCandidate => { let data = self.confirm_transmutability_candidate(obligation)?; ImplSource::Builtin(BuiltinImplSource::Misc, data) @@ -272,6 +278,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations } + fn confirm_metadata_cast_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + kind: MetadataCastKind, + ) -> Result>, SelectionError<'tcx>> { + match kind { + MetadataCastKind::Unconditional => Ok(Vec::new()), + MetadataCastKind::Subtype => { + let source = obligation.self_ty().skip_binder(); + let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .sub(DefineOpaqueTypes::No, source, target) + .map_err(|_| Unimplemented)?; + + Ok(obligations) + } + } + } + #[instrument(level = "debug", skip(self))] fn confirm_transmutability_candidate( &mut self, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 6a6adcbb680ea..1bf8578dfda60 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1830,12 +1830,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> { (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, // (*) - (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => { - DropVictim::Yes - } - (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => { - DropVictim::No - } + ( + BuiltinCandidate { has_nested: false } + | ConstDestructCandidate(_) + | MetadataCastCandidate(MetadataCastKind::Unconditional), + _, + ) => DropVictim::Yes, + ( + _, + BuiltinCandidate { has_nested: false } + | ConstDestructCandidate(_) + | MetadataCastCandidate(MetadataCastKind::Unconditional), + ) => DropVictim::No, (ParamCandidate(other), ParamCandidate(victim)) => { let same_except_bound_vars = other.skip_binder().trait_ref @@ -1873,6 +1879,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } + | MetadataCastCandidate(_) | TraitAliasCandidate | ObjectCandidate(_) | ProjectionCandidate(_), @@ -1903,6 +1910,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } + | MetadataCastCandidate(MetadataCastKind::Subtype) | TraitAliasCandidate, ParamCandidate(ref victim_cand), ) => { @@ -1939,6 +1947,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } + | MetadataCastCandidate(_) | TraitAliasCandidate, ) => DropVictim::Yes, @@ -1955,6 +1964,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } + | MetadataCastCandidate(_) | TraitAliasCandidate, ObjectCandidate(_) | ProjectionCandidate(_), ) => DropVictim::No, @@ -2063,6 +2073,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } + | MetadataCastCandidate(MetadataCastKind::Subtype) | TraitAliasCandidate, ImplCandidate(_) | ClosureCandidate { .. } @@ -2075,6 +2086,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } + | MetadataCastCandidate(MetadataCastKind::Subtype) | TraitAliasCandidate, ) => DropVictim::No, } diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 040aa06978748..26416bbf45f4a 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -77,6 +77,22 @@ pub trait Pointee { // NOTE: don’t stabilize this before trait aliases are stable in the language? pub trait Thin = Pointee; +/// A marker for valid metadata conversions during pointer-to-pointer casts. +/// +/// A type `T` implements `MetadataCast` if and only if pointers with metadata `T` +/// can be cast to pointers with metadata `U`. +#[unstable(feature = "ptr_metadata_cast", issue = "none")] +#[cfg_attr(not(bootstrap), lang = "metadata_cast")] +#[rustc_deny_explicit_impl(implement_via_object = false)] +pub trait MetadataCast {} + +/// A marker for valid pointer-to-pointer casts. +/// +/// A type `T` implements `PointerCast` if and only if a pointer to `T` can be +/// cast into a pointer to `U`. +#[unstable(feature = "ptr_metadata_cast", issue = "none")] +pub trait PointerCast = Pointee::Metadata>>; + /// Extract the metadata component of a pointer. /// /// Values of type `*mut T`, `&T`, or `&mut T` can be passed directly to this function diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index a9078854125c3..e171791e1fd65 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -406,6 +406,8 @@ pub use crate::intrinsics::write_bytes; mod metadata; #[unstable(feature = "ptr_metadata", issue = "81513")] pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin}; +#[unstable(feature = "ptr_metadata_cast", issue = "none")] +pub use metadata::{MetadataCast, PointerCast}; mod non_null; #[stable(feature = "nonnull", since = "1.25.0")] diff --git a/tests/ui/traits/metadata-cast-fail-lifetimes.rs b/tests/ui/traits/metadata-cast-fail-lifetimes.rs new file mode 100644 index 0000000000000..70a0cdd145959 --- /dev/null +++ b/tests/ui/traits/metadata-cast-fail-lifetimes.rs @@ -0,0 +1,39 @@ +#![feature(ptr_metadata, ptr_metadata_cast)] + +use std::ptr::{self, DynMetadata, MetadataCast, Pointee, PointerCast}; + +fn cast(ptr: *mut T) -> *mut U +where + T: PointerCast, +{ + todo!() +} + +fn check() +where + T: MetadataCast, +{ +} + +fn main() {} + +fn err1<'a, 'b>() { + // changing lifetimes in `DynMetadata` is only allowed if `T = dyn Trait` + check::, DynMetadata<&'b ()>>(); + //~^ ERROR may not live long enough +} + +fn err2<'a, 'b>() { + check::<&'a (), &'b ()>(); + //~^ ERROR may not live long enough +} + +fn subtype_fail<'short, 'long: 'short, T, U>(ptr: *mut T) -> *mut U +where + T: ?Sized + Pointee, + U: ?Sized + Pointee, +{ + // extending the lifetime of arbitrary metadata is not allowed + cast(ptr) + //~^ ERROR may not live long enough +} diff --git a/tests/ui/traits/metadata-cast-fail-lifetimes.stderr b/tests/ui/traits/metadata-cast-fail-lifetimes.stderr new file mode 100644 index 0000000000000..bfaadb1ef2be4 --- /dev/null +++ b/tests/ui/traits/metadata-cast-fail-lifetimes.stderr @@ -0,0 +1,40 @@ +error: lifetime may not live long enough + --> $DIR/metadata-cast-fail-lifetimes.rs:22:5 + | +LL | fn err1<'a, 'b>() { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | // changing lifetimes in `DynMetadata` is only allowed if `T = dyn Trait` +LL | check::, DynMetadata<&'b ()>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/metadata-cast-fail-lifetimes.rs:27:5 + | +LL | fn err2<'a, 'b>() { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | check::<&'a (), &'b ()>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/metadata-cast-fail-lifetimes.rs:37:5 + | +LL | fn subtype_fail<'short, 'long: 'short, T, U>(ptr: *mut T) -> *mut U + | ------ ----- lifetime `'long` defined here + | | + | lifetime `'short` defined here +... +LL | cast(ptr) + | ^^^^^^^^^ requires that `'short` must outlive `'long` + | + = help: consider adding the following bound: `'short: 'long` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/traits/metadata-cast-fail-traits.rs b/tests/ui/traits/metadata-cast-fail-traits.rs new file mode 100644 index 0000000000000..378264e2b6b51 --- /dev/null +++ b/tests/ui/traits/metadata-cast-fail-traits.rs @@ -0,0 +1,45 @@ +#![feature(ptr_metadata, ptr_metadata_cast)] + +use std::ptr::{DynMetadata, MetadataCast, PointerCast}; + +fn cast(_: *mut T) -> *mut U +where + T: PointerCast, +{ + todo!() +} + +fn check() +where + T: MetadataCast, +{ +} + +trait Trait {} +trait Trait2 {} + +fn main() { + check::<(), usize>(); //~ ERROR not satisfied + check::<(), DynMetadata>(); //~ ERROR not satisfied + check::>(); //~ ERROR not satisfied + check::, usize>(); //~ ERROR not satisfied + check::, DynMetadata>(); //~ ERROR not satisfied + check::(); //~ ERROR not satisfied + + // `dyn MetadataCast` should not implement `MetadataCast` + check::, usize>(); //~ ERROR not satisfied + + // this could pass in the future, but for now it matches the behavior of `as` casts + check::, DynMetadata>(); //~ ERROR not satisfied +} + +fn do_casts<'a>(thin: &mut i32, slice: &mut [i32], trait_object: &mut (dyn Trait + Send)) { + let _: *mut [u8] = cast(thin); //~ ERROR not satisfied + let _: *mut dyn Trait = cast(thin); //~ ERROR not satisfied + let _: *mut [u8] = cast(trait_object); //~ ERROR not satisfied + let _: *mut dyn Trait = cast(slice); //~ ERROR not satisfied + let _: *mut dyn Trait2 = cast(trait_object); //~ ERROR not satisfied + + // may be allowed in the future + let _: *mut dyn Send = cast(trait_object); //~ ERROR not satisfied +} diff --git a/tests/ui/traits/metadata-cast-fail-traits.stderr b/tests/ui/traits/metadata-cast-fail-traits.stderr new file mode 100644 index 0000000000000..2b215cdd7ee90 --- /dev/null +++ b/tests/ui/traits/metadata-cast-fail-traits.stderr @@ -0,0 +1,231 @@ +error[E0277]: the trait bound `(): MetadataCast` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:22:13 + | +LL | check::<(), usize>(); + | ^^ the trait `MetadataCast` is not implemented for `()` + | +note: required by a bound in `check` + --> $DIR/metadata-cast-fail-traits.rs:14:8 + | +LL | fn check() + | ----- required by a bound in this function +LL | where +LL | T: MetadataCast, + | ^^^^^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `(): MetadataCast>` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:23:13 + | +LL | check::<(), DynMetadata>(); + | ^^ the trait `MetadataCast>` is not implemented for `()` + | +note: required by a bound in `check` + --> $DIR/metadata-cast-fail-traits.rs:14:8 + | +LL | fn check() + | ----- required by a bound in this function +LL | where +LL | T: MetadataCast, + | ^^^^^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `usize: MetadataCast>` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:24:13 + | +LL | check::>(); + | ^^^^^ the trait `MetadataCast>` is not implemented for `usize` + | +note: required by a bound in `check` + --> $DIR/metadata-cast-fail-traits.rs:14:8 + | +LL | fn check() + | ----- required by a bound in this function +LL | where +LL | T: MetadataCast, + | ^^^^^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `DynMetadata: MetadataCast` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:25:13 + | +LL | check::, usize>(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `MetadataCast` is not implemented for `DynMetadata` + | +note: required by a bound in `check` + --> $DIR/metadata-cast-fail-traits.rs:14:8 + | +LL | fn check() + | ----- required by a bound in this function +LL | where +LL | T: MetadataCast, + | ^^^^^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `DynMetadata: MetadataCast>` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:26:13 + | +LL | check::, DynMetadata>(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `MetadataCast>` is not implemented for `DynMetadata` + | +note: required by a bound in `check` + --> $DIR/metadata-cast-fail-traits.rs:14:8 + | +LL | fn check() + | ----- required by a bound in this function +LL | where +LL | T: MetadataCast, + | ^^^^^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `dyn Trait + Send: MetadataCast` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:27:13 + | +LL | check::(); + | ^^^^^^^^^^^^^^^^ the trait `MetadataCast` is not implemented for `dyn Trait + Send` + | +note: required by a bound in `check` + --> $DIR/metadata-cast-fail-traits.rs:14:8 + | +LL | fn check() + | ----- required by a bound in this function +LL | where +LL | T: MetadataCast, + | ^^^^^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `dyn MetadataCast: MetadataCast` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:30:13 + | +LL | check::, usize>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `MetadataCast` is not implemented for `dyn MetadataCast` + | +note: required by a bound in `check` + --> $DIR/metadata-cast-fail-traits.rs:14:8 + | +LL | fn check() + | ----- required by a bound in this function +LL | where +LL | T: MetadataCast, + | ^^^^^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `DynMetadata: MetadataCast>` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:33:13 + | +LL | check::, DynMetadata>(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `MetadataCast>` is not implemented for `DynMetadata` + | +note: required by a bound in `check` + --> $DIR/metadata-cast-fail-traits.rs:14:8 + | +LL | fn check() + | ----- required by a bound in this function +LL | where +LL | T: MetadataCast, + | ^^^^^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `(): MetadataCast` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:37:29 + | +LL | let _: *mut [u8] = cast(thin); + | ---- ^^^^ the trait `MetadataCast` is not implemented for `()` + | | + | required by a bound introduced by this call + | + = note: required for `i32` to implement `PointerCast<[u8]>` +note: required by a bound in `cast` + --> $DIR/metadata-cast-fail-traits.rs:7:8 + | +LL | fn cast(_: *mut T) -> *mut U + | ---- required by a bound in this function +LL | where +LL | T: PointerCast, + | ^^^^^^^^^^^^^^ required by this bound in `cast` + +error[E0277]: the trait bound `(): MetadataCast>` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:38:34 + | +LL | let _: *mut dyn Trait = cast(thin); + | ---- ^^^^ the trait `MetadataCast>` is not implemented for `()` + | | + | required by a bound introduced by this call + | + = note: required for `i32` to implement `PointerCast` +note: required by a bound in `cast` + --> $DIR/metadata-cast-fail-traits.rs:7:8 + | +LL | fn cast(_: *mut T) -> *mut U + | ---- required by a bound in this function +LL | where +LL | T: PointerCast, + | ^^^^^^^^^^^^^^ required by this bound in `cast` + +error[E0277]: the trait bound `DynMetadata: MetadataCast` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:39:29 + | +LL | let _: *mut [u8] = cast(trait_object); + | ---- ^^^^^^^^^^^^ the trait `MetadataCast` is not implemented for `DynMetadata` + | | + | required by a bound introduced by this call + | + = note: required for `dyn Trait + Send` to implement `PointerCast<[u8]>` +note: required by a bound in `cast` + --> $DIR/metadata-cast-fail-traits.rs:7:8 + | +LL | fn cast(_: *mut T) -> *mut U + | ---- required by a bound in this function +LL | where +LL | T: PointerCast, + | ^^^^^^^^^^^^^^ required by this bound in `cast` + +error[E0277]: the trait bound `usize: MetadataCast>` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:40:34 + | +LL | let _: *mut dyn Trait = cast(slice); + | ---- ^^^^^ the trait `MetadataCast>` is not implemented for `usize` + | | + | required by a bound introduced by this call + | + = note: required for `[i32]` to implement `PointerCast` +note: required by a bound in `cast` + --> $DIR/metadata-cast-fail-traits.rs:7:8 + | +LL | fn cast(_: *mut T) -> *mut U + | ---- required by a bound in this function +LL | where +LL | T: PointerCast, + | ^^^^^^^^^^^^^^ required by this bound in `cast` + +error[E0277]: the trait bound `DynMetadata: MetadataCast>` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:41:35 + | +LL | let _: *mut dyn Trait2 = cast(trait_object); + | ---- ^^^^^^^^^^^^ the trait `MetadataCast>` is not implemented for `DynMetadata` + | | + | required by a bound introduced by this call + | + = note: required for `dyn Trait + Send` to implement `PointerCast` +note: required by a bound in `cast` + --> $DIR/metadata-cast-fail-traits.rs:7:8 + | +LL | fn cast(_: *mut T) -> *mut U + | ---- required by a bound in this function +LL | where +LL | T: PointerCast, + | ^^^^^^^^^^^^^^ required by this bound in `cast` + +error[E0277]: the trait bound `DynMetadata: MetadataCast>` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:44:33 + | +LL | let _: *mut dyn Send = cast(trait_object); + | ---- ^^^^^^^^^^^^ the trait `MetadataCast>` is not implemented for `DynMetadata` + | | + | required by a bound introduced by this call + | + = note: required for `dyn Trait + Send` to implement `PointerCast` +note: required by a bound in `cast` + --> $DIR/metadata-cast-fail-traits.rs:7:8 + | +LL | fn cast(_: *mut T) -> *mut U + | ---- required by a bound in this function +LL | where +LL | T: PointerCast, + | ^^^^^^^^^^^^^^ required by this bound in `cast` + +error: aborting due to 14 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/metadata-cast-pass.rs b/tests/ui/traits/metadata-cast-pass.rs new file mode 100644 index 0000000000000..cd8c0fef8bdb5 --- /dev/null +++ b/tests/ui/traits/metadata-cast-pass.rs @@ -0,0 +1,79 @@ +// check-pass +#![feature(ptr_metadata, ptr_metadata_cast)] + +use std::ptr::{self, DynMetadata, MetadataCast, Pointee, PointerCast, Thin}; + +fn cast(_: *mut T) -> *mut U +where + T: PointerCast, +{ + todo!() +} + +fn check() +where + T: MetadataCast, +{ +} + +trait Trait {} + +fn main() { + // any to () is OK + check::<(), ()>(); + check::(); + check::, ()>(); + check::, ()>(); + + // same types are OK + check::(); + check::(); + check::, DynMetadata>(); + check::, dyn MetadataCast>(); + + // changing auto traits of trait object in DynMetadata is OK + check::, DynMetadata>(); + check::, DynMetadata>(); +} + +fn lifetimes_ok<'a, 'b>() { + // changing lifetimes of trait object in DynMetadata is OK + check::, DynMetadata>(); + + // otherwise, require source outlives target + check::<&'a (), &'a ()>(); + check::<&'static (), &'a ()>(); +} + +fn do_casts<'a>(thin: &mut i32, slice: &mut [i32], trait_object: &mut dyn Trait) { + let _: *mut u8 = cast(thin); + let _: *mut u8 = cast(slice); + let _: *mut [u8] = cast(slice); + let _: *mut u8 = cast(trait_object); + let _: *mut (dyn Trait + Send + 'a) = cast(trait_object); +} + +fn subtype_ok<'short, 'long: 'short, T, U>(ptr: *mut T) -> *mut U +where + T: ?Sized + Pointee, + U: ?Sized + Pointee, +{ + if true { + // Because this allows subtyping ... + ptr::from_raw_parts_mut(ptr.cast::<()>(), ptr::metadata(ptr)) + } else { + // ... `PointerCast` should allow it as well + cast(ptr) + } +} + +fn same_metadata(ptr: *mut T) -> *mut U +where + T: Pointee::Metadata>, +{ + cast(ptr) +} + +fn to_thin(ptr: *mut T) -> *mut U { + cast(ptr) +} From a498abe732278be3b6e0c1a3d54d5e3290e1cf6d Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 19 Jan 2024 11:25:34 +0000 Subject: [PATCH 4/9] [NFC] clean up pointer cast typeck code --- compiler/rustc_hir_typeck/src/cast.rs | 101 +++++++++++++------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index bd8a2024caa1e..4bcc01d0c2499 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -4,7 +4,7 @@ //! * `e` has type `T` and `T` coerces to `U`; *coercion-cast* //! * `e` has type `*T`, `U` is `*U_0`, and either `U_0: Sized` or //! pointer_kind(`T`) = pointer_kind(`U_0`); *ptr-ptr-cast* -//! * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* +//! * `e` has type `*T` and `U` is an integer type, while `T: Sized`; *ptr-addr-cast* //! * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* //! * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* //! * `e` is a C-like enum and `U` is an integer type; *enum-cast* @@ -689,7 +689,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { if !fcx.type_is_sized_modulo_regions(fcx.param_env, mt.ty) { return Err(CastError::IllegalCast); } - self.check_ref_cast(fcx, TypeAndMut { mutbl, ty: inner_ty }, mt) + self.check_array_ptr_cast( + fcx, + TypeAndMut { mutbl, ty: inner_ty }, + mt, + )?; + Ok(CastKind::ArrayPtrCast) } _ => Err(CastError::NonScalar), }; @@ -724,14 +729,18 @@ impl<'a, 'tcx> CastCheck<'tcx> { Err(CastError::IllegalCast) } - // ptr -> * - (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast - + // ptr-ptr-cast + (Ptr(m_e), Ptr(m_c)) => { + self.check_ptr_ptr_cast(fcx, m_e.ty, m_c.ty)?; + Ok(CastKind::PtrPtrCast) + } // ptr-addr-cast (Ptr(m_expr), Int(t_c)) => { self.lossy_provenance_ptr2int_lint(fcx, t_c); - self.check_ptr_addr_cast(fcx, m_expr) + self.check_ptr_addr_cast(fcx, m_expr.ty)?; + Ok(CastKind::PtrAddrCast) } + // fptr-addr-cast (FnPtr, Int(_)) => { // FIXME(#95489): there should eventually be a lint for these casts Ok(CastKind::FnPtrAddrCast) @@ -739,10 +748,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { // addr-ptr-cast (Int(_), Ptr(mt)) => { self.fuzzy_provenance_int2ptr_lint(fcx); - self.check_addr_ptr_cast(fcx, mt) + self.check_addr_ptr_cast(fcx, mt.ty)?; + Ok(CastKind::AddrPtrCast) + } + // fptr-ptr-cast + (FnPtr, Ptr(mt)) => { + self.check_fptr_ptr_cast(fcx, mt.ty)?; + Ok(CastKind::FnPtrPtrCast) } - // fn-ptr-cast - (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), // prim -> prim (Int(CEnum), Int(_)) => { @@ -765,17 +778,17 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } + /// ptr-ptr-cast. metadata must match. fn check_ptr_ptr_cast( &self, fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - m_cast: ty::TypeAndMut<'tcx>, - ) -> Result { - debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast); - // ptr-ptr cast. vtables must match. + expr: Ty<'tcx>, + cast: Ty<'tcx>, + ) -> Result<(), CastError> { + debug!(?expr, ?cast, "check_ptr_ptr_cast"); - let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?; - let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?; + let expr_kind = fcx.pointer_kind(expr, self.span)?; + let cast_kind = fcx.pointer_kind(cast, self.span)?; let Some(cast_kind) = cast_kind else { // We can't cast if target pointer kind is unknown @@ -784,7 +797,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // Cast to thin pointer is OK if cast_kind == PointerKind::Thin { - return Ok(CastKind::PtrPtrCast); + return Ok(()); } let Some(expr_kind) = expr_kind else { @@ -799,56 +812,48 @@ impl<'a, 'tcx> CastCheck<'tcx> { // vtable kinds must match if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) { - Ok(CastKind::PtrPtrCast) + Ok(()) } else { Err(CastError::DifferingKinds) } } - fn check_fptr_ptr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_cast: ty::TypeAndMut<'tcx>, - ) -> Result { - // fptr-ptr cast. must be to thin ptr - - match fcx.pointer_kind(m_cast.ty, self.span)? { + /// fptr-ptr-cast. target must be thin. + fn check_fptr_ptr_cast(&self, fcx: &FnCtxt<'a, 'tcx>, cast: Ty<'tcx>) -> Result<(), CastError> { + match fcx.pointer_kind(cast, self.span)? { None => Err(CastError::UnknownCastPtrKind), - Some(PointerKind::Thin) => Ok(CastKind::FnPtrPtrCast), + Some(PointerKind::Thin) => Ok(()), _ => Err(CastError::IllegalCast), } } + /// ptr-addr-cast. source must be thin. fn check_ptr_addr_cast( &self, fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, + expr: Ty<'tcx>, ) -> Result { - // ptr-addr cast. must be from thin ptr - - match fcx.pointer_kind(m_expr.ty, self.span)? { + match fcx.pointer_kind(expr, self.span)? { None => Err(CastError::UnknownExprPtrKind), Some(PointerKind::Thin) => Ok(CastKind::PtrAddrCast), _ => Err(CastError::NeedViaThinPtr), } } - fn check_ref_cast( + /// array-ptr-cast. + /// + /// This is a special case to cast from `&[T; N]` directly to `*const T`. + /// It was added to work around LLVM limitations way before Rust 1.0 and only exists + /// for backwards compatibility now. + fn check_array_ptr_cast( &self, fcx: &FnCtxt<'a, 'tcx>, m_expr: ty::TypeAndMut<'tcx>, m_cast: ty::TypeAndMut<'tcx>, - ) -> Result { - // array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const + ) -> Result<(), CastError> { + // allow mut-to-mut, mut-to-const, const-to-const if m_expr.mutbl >= m_cast.mutbl { if let ty::Array(ety, _) = m_expr.ty.kind() { - // Due to the limitations of LLVM global constants, - // region pointers end up pointing at copies of - // vector elements instead of the original values. - // To allow raw pointers to work correctly, we - // need to special-case obtaining a raw pointer - // from a region pointer to a vector. - // Coerce to a raw pointer so that we generate AddressOf in MIR. let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr); fcx.coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None) @@ -862,22 +867,18 @@ impl<'a, 'tcx> CastCheck<'tcx> { // this will report a type mismatch if needed fcx.demand_eqtype(self.span, *ety, m_cast.ty); - return Ok(CastKind::ArrayPtrCast); + return Ok(()); } } Err(CastError::IllegalCast) } - fn check_addr_ptr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_cast: TypeAndMut<'tcx>, - ) -> Result { - // ptr-addr cast. pointer must be thin. - match fcx.pointer_kind(m_cast.ty, self.span)? { + /// addr-ptr-cast. target must be thin. + fn check_addr_ptr_cast(&self, fcx: &FnCtxt<'a, 'tcx>, cast: Ty<'tcx>) -> Result<(), CastError> { + match fcx.pointer_kind(cast, self.span)? { None => Err(CastError::UnknownCastPtrKind), - Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast), + Some(PointerKind::Thin) => Ok(()), Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))), Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))), Some(PointerKind::OfAlias(_) | PointerKind::OfParam(_)) => { From 5510da0ece87ba66a1ac6c0c8a8e5effbb5290b1 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 19 Jan 2024 02:40:01 +0100 Subject: [PATCH 5/9] typeck ptr-to-ptr casts via traits --- compiler/rustc_hir_typeck/src/cast.rs | 56 +++++++++++-------- .../ui/traits/metadata-cast-fail-lifetimes.rs | 2 +- tests/ui/traits/metadata-cast-fail-traits.rs | 4 +- .../traits/metadata-cast-fail-traits.stderr | 12 ++-- tests/ui/traits/metadata-cast-pass.rs | 4 +- 5 files changed, 43 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 4bcc01d0c2499..e056d15e16f78 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,10 +32,10 @@ use super::FnCtxt; use crate::errors; use crate::type_error_struct; -use hir::ExprKind; +use hir::{ExprKind, LangItem}; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_macros::{TypeFoldable, TypeVisitable}; +use rustc_infer::traits::Obligation; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; @@ -45,7 +45,8 @@ use rustc_session::lint; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::sym; use rustc_span::Span; -use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; /// Reifies a cast check to be checked once we have full type information for /// a function context. @@ -67,7 +68,7 @@ pub struct CastCheck<'tcx> { /// The kind of pointer and associated metadata (thin, length or vtable) - we /// only allow casts between fat pointers if their metadata have the same /// kind. -#[derive(Debug, Copy, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, @@ -787,33 +788,40 @@ impl<'a, 'tcx> CastCheck<'tcx> { ) -> Result<(), CastError> { debug!(?expr, ?cast, "check_ptr_ptr_cast"); - let expr_kind = fcx.pointer_kind(expr, self.span)?; - let cast_kind = fcx.pointer_kind(cast, self.span)?; + let meta_did = fcx.tcx.require_lang_item(LangItem::Metadata, Some(self.span)); + let expr_meta = Ty::new_projection(fcx.tcx, meta_did, [expr]); + let cast_meta = Ty::new_projection(fcx.tcx, meta_did, [cast]); + let expr_meta = fcx.normalize(self.span, expr_meta); + let cast_meta = fcx.normalize(self.span, cast_meta); - let Some(cast_kind) = cast_kind else { - // We can't cast if target pointer kind is unknown - return Err(CastError::UnknownCastPtrKind); - }; + let pred = ty::TraitRef::from_lang_item( + fcx.tcx, + LangItem::MetadataCast, + self.span, + [expr_meta, cast_meta], + ); - // Cast to thin pointer is OK - if cast_kind == PointerKind::Thin { + let obligation = Obligation::new(fcx.tcx, fcx.misc(self.span), fcx.param_env, pred); + if fcx.predicate_must_hold_modulo_regions(&obligation) { return Ok(()); } - let Some(expr_kind) = expr_kind else { - // We can't cast to fat pointer if source pointer kind is unknown - return Err(CastError::UnknownExprPtrKind); - }; - - // thin -> fat? report invalid cast (don't complain about vtable kinds) - if expr_kind == PointerKind::Thin { - return Err(CastError::SizedUnsizedCast); - } + expr_meta.error_reported()?; + cast_meta.error_reported()?; - // vtable kinds must match - if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) { - Ok(()) + if cast_meta == fcx.tcx.types.unit { + span_bug!(self.span, "cast to thin pointer, but predicate didn't hold: {pred}"); + } else if cast_meta.has_infer_types() { + // We can't cast if target pointer kind is unknown + Err(CastError::UnknownCastPtrKind) + } else if expr_meta.has_infer_types() { + // We can't cast to fat pointer if source pointer kind is unknown + Err(CastError::UnknownExprPtrKind) + } else if expr_meta == fcx.tcx.types.unit { + // thin -> fat? report invalid cast (don't complain about metadata kinds) + Err(CastError::SizedUnsizedCast) } else { + // metadata kinds must match Err(CastError::DifferingKinds) } } diff --git a/tests/ui/traits/metadata-cast-fail-lifetimes.rs b/tests/ui/traits/metadata-cast-fail-lifetimes.rs index 70a0cdd145959..fe024014d56ce 100644 --- a/tests/ui/traits/metadata-cast-fail-lifetimes.rs +++ b/tests/ui/traits/metadata-cast-fail-lifetimes.rs @@ -6,7 +6,7 @@ fn cast(ptr: *mut T) -> *mut U where T: PointerCast, { - todo!() + ptr as _ } fn check() diff --git a/tests/ui/traits/metadata-cast-fail-traits.rs b/tests/ui/traits/metadata-cast-fail-traits.rs index 378264e2b6b51..e1624e1b79c18 100644 --- a/tests/ui/traits/metadata-cast-fail-traits.rs +++ b/tests/ui/traits/metadata-cast-fail-traits.rs @@ -2,11 +2,11 @@ use std::ptr::{DynMetadata, MetadataCast, PointerCast}; -fn cast(_: *mut T) -> *mut U +fn cast(ptr: *mut T) -> *mut U where T: PointerCast, { - todo!() + ptr as _ } fn check() diff --git a/tests/ui/traits/metadata-cast-fail-traits.stderr b/tests/ui/traits/metadata-cast-fail-traits.stderr index 2b215cdd7ee90..7525335c4d7bc 100644 --- a/tests/ui/traits/metadata-cast-fail-traits.stderr +++ b/tests/ui/traits/metadata-cast-fail-traits.stderr @@ -130,7 +130,7 @@ LL | let _: *mut [u8] = cast(thin); note: required by a bound in `cast` --> $DIR/metadata-cast-fail-traits.rs:7:8 | -LL | fn cast(_: *mut T) -> *mut U +LL | fn cast(ptr: *mut T) -> *mut U | ---- required by a bound in this function LL | where LL | T: PointerCast, @@ -148,7 +148,7 @@ LL | let _: *mut dyn Trait = cast(thin); note: required by a bound in `cast` --> $DIR/metadata-cast-fail-traits.rs:7:8 | -LL | fn cast(_: *mut T) -> *mut U +LL | fn cast(ptr: *mut T) -> *mut U | ---- required by a bound in this function LL | where LL | T: PointerCast, @@ -166,7 +166,7 @@ LL | let _: *mut [u8] = cast(trait_object); note: required by a bound in `cast` --> $DIR/metadata-cast-fail-traits.rs:7:8 | -LL | fn cast(_: *mut T) -> *mut U +LL | fn cast(ptr: *mut T) -> *mut U | ---- required by a bound in this function LL | where LL | T: PointerCast, @@ -184,7 +184,7 @@ LL | let _: *mut dyn Trait = cast(slice); note: required by a bound in `cast` --> $DIR/metadata-cast-fail-traits.rs:7:8 | -LL | fn cast(_: *mut T) -> *mut U +LL | fn cast(ptr: *mut T) -> *mut U | ---- required by a bound in this function LL | where LL | T: PointerCast, @@ -202,7 +202,7 @@ LL | let _: *mut dyn Trait2 = cast(trait_object); note: required by a bound in `cast` --> $DIR/metadata-cast-fail-traits.rs:7:8 | -LL | fn cast(_: *mut T) -> *mut U +LL | fn cast(ptr: *mut T) -> *mut U | ---- required by a bound in this function LL | where LL | T: PointerCast, @@ -220,7 +220,7 @@ LL | let _: *mut dyn Send = cast(trait_object); note: required by a bound in `cast` --> $DIR/metadata-cast-fail-traits.rs:7:8 | -LL | fn cast(_: *mut T) -> *mut U +LL | fn cast(ptr: *mut T) -> *mut U | ---- required by a bound in this function LL | where LL | T: PointerCast, diff --git a/tests/ui/traits/metadata-cast-pass.rs b/tests/ui/traits/metadata-cast-pass.rs index cd8c0fef8bdb5..73104781ac086 100644 --- a/tests/ui/traits/metadata-cast-pass.rs +++ b/tests/ui/traits/metadata-cast-pass.rs @@ -3,11 +3,11 @@ use std::ptr::{self, DynMetadata, MetadataCast, Pointee, PointerCast, Thin}; -fn cast(_: *mut T) -> *mut U +fn cast(ptr: *mut T) -> *mut U where T: PointerCast, { - todo!() + ptr as _ } fn check() From e171473e54a8309768a91ec05d5640e0f79021bc Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Sat, 20 Jan 2024 13:45:46 +0100 Subject: [PATCH 6/9] borrowck ptr-to-ptr casts via traits --- compiler/rustc_borrowck/src/type_check/mod.rs | 22 ++++++++++++++++++- .../ui/traits/metadata-cast-fail-lifetimes.rs | 10 +++++++++ .../metadata-cast-fail-lifetimes.stderr | 15 ++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index cf28e62177fb4..f05082d0d446f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2296,7 +2296,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => (), + (Some(CastTy::Ptr(pointee_from)), Some(CastTy::Ptr(pointee_to))) => { + let metadata_def_id = + tcx.require_lang_item(LangItem::Metadata, Some(span)); + let metadata_from = + Ty::new_projection(tcx, metadata_def_id, [pointee_from.ty]); + let metadata_to = + Ty::new_projection(tcx, metadata_def_id, [pointee_to.ty]); + + let predicate = ty::TraitRef::from_lang_item( + tcx, + LangItem::MetadataCast, + span, + [metadata_from, metadata_to], + ); + + self.prove_predicate( + predicate, + location.to_locations(), + ConstraintCategory::Cast { unsize_to: None }, + ); + } _ => { span_mirbug!( self, diff --git a/tests/ui/traits/metadata-cast-fail-lifetimes.rs b/tests/ui/traits/metadata-cast-fail-lifetimes.rs index fe024014d56ce..8f12e65e6844e 100644 --- a/tests/ui/traits/metadata-cast-fail-lifetimes.rs +++ b/tests/ui/traits/metadata-cast-fail-lifetimes.rs @@ -37,3 +37,13 @@ where cast(ptr) //~^ ERROR may not live long enough } + +fn borrowck_cast<'short, 'long: 'short, T, U>(ptr: *mut T) -> *mut U +where + T: ?Sized + Pointee, + U: ?Sized + Pointee, +{ + // test that pointer-to-pointer casts are borrow-checked + ptr as _ + //~^ ERROR may not live long enough +} diff --git a/tests/ui/traits/metadata-cast-fail-lifetimes.stderr b/tests/ui/traits/metadata-cast-fail-lifetimes.stderr index bfaadb1ef2be4..0f3e22e12c230 100644 --- a/tests/ui/traits/metadata-cast-fail-lifetimes.stderr +++ b/tests/ui/traits/metadata-cast-fail-lifetimes.stderr @@ -36,5 +36,18 @@ LL | cast(ptr) | = help: consider adding the following bound: `'short: 'long` -error: aborting due to 3 previous errors +error: lifetime may not live long enough + --> $DIR/metadata-cast-fail-lifetimes.rs:47:5 + | +LL | fn borrowck_cast<'short, 'long: 'short, T, U>(ptr: *mut T) -> *mut U + | ------ ----- lifetime `'long` defined here + | | + | lifetime `'short` defined here +... +LL | ptr as _ + | ^^^^^^^^ cast requires that `'short` must outlive `'long` + | + = help: consider adding the following bound: `'short: 'long` + +error: aborting due to 4 previous errors From 54e49e0dbd93b384388fe8d85b0e0b107d3fbbdb Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Fri, 19 Jan 2024 02:40:54 +0100 Subject: [PATCH 7/9] allow casting to fat pointers with `ptr.cast()` --- library/core/src/ptr/const_ptr.rs | 14 ++++++++++++++ library/core/src/ptr/mut_ptr.rs | 14 ++++++++++++++ library/core/src/ptr/non_null.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 12ff64de8790a..9deebc55e7c59 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -57,10 +57,24 @@ impl *const T { #[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")] #[rustc_diagnostic_item = "const_ptr_cast"] #[inline(always)] + #[cfg(bootstrap)] pub const fn cast(self) -> *const U { self as _ } + /// Casts to a pointer of another type. + #[stable(feature = "ptr_cast", since = "1.38.0")] + #[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")] + #[rustc_diagnostic_item = "const_ptr_cast"] + #[inline(always)] + #[cfg(not(bootstrap))] + pub const fn cast(self) -> *const U + where + T: PointerCast, + { + self as _ + } + /// Use the pointer value in a new pointer of another type. /// /// In case `meta` is a (fat) pointer to an unsized type, this operation diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 4f5fca4367d08..3ba42aee299bd 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -57,10 +57,24 @@ impl *mut T { #[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")] #[rustc_diagnostic_item = "ptr_cast"] #[inline(always)] + #[cfg(bootstrap)] pub const fn cast(self) -> *mut U { self as _ } + /// Casts to a pointer of another type. + #[stable(feature = "ptr_cast", since = "1.38.0")] + #[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")] + #[rustc_diagnostic_item = "ptr_cast"] + #[inline(always)] + #[cfg(not(bootstrap))] + pub const fn cast(self) -> *mut U + where + T: PointerCast, + { + self as _ + } + /// Use the pointer value in a new pointer of another type. /// /// In case `meta` is a (fat) pointer to an unsized type, this operation diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 427a9f3f49456..15ab76553c6f9 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -468,11 +468,39 @@ impl NonNull { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] + #[cfg(bootstrap)] pub const fn cast(self) -> NonNull { // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null unsafe { NonNull::new_unchecked(self.as_ptr() as *mut U) } } + /// Casts to a pointer of another type. + /// + /// # Examples + /// + /// ``` + /// use std::ptr::NonNull; + /// + /// let mut x = 0u32; + /// let ptr = NonNull::new(&mut x as *mut _).expect("null pointer"); + /// + /// let casted_ptr = ptr.cast::(); + /// let raw_ptr: *mut i8 = casted_ptr.as_ptr(); + /// ``` + #[stable(feature = "nonnull_cast", since = "1.27.0")] + #[rustc_const_stable(feature = "const_nonnull_cast", since = "1.36.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[cfg(not(bootstrap))] + pub const fn cast(self) -> NonNull + where + T: super::PointerCast, + { + // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null + unsafe { NonNull::new_unchecked(self.as_ptr() as *mut U) } + } + /// Calculates the offset from a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer From 5927b913570c11aec73bad10752e9723abe18f2f Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Mon, 22 Jan 2024 15:44:02 +0100 Subject: [PATCH 8/9] implement Waffle's cast semantics --- compiler/rustc_middle/src/traits/select.rs | 4 +- compiler/rustc_middle/src/ty/context.rs | 2 +- .../src/traits/select/candidate_assembly.rs | 40 +++++++++++++++---- .../src/traits/select/confirmation.rs | 11 ++++- .../src/traits/select/mod.rs | 6 +-- library/alloc/src/boxed.rs | 6 +-- tests/ui/cast/cast-rfc0401-vtable-kinds.rs | 24 ----------- tests/ui/cast/fat-ptr-cast-borrowck.rs | 8 ++++ tests/ui/cast/fat-ptr-cast-borrowck.stderr | 28 +++++++++++++ tests/ui/cast/fat-ptr-cast-rpass.rs | 8 ++-- tests/ui/cast/fat-ptr-cast.rs | 13 ++++++ tests/ui/cast/fat-ptr-cast.stderr | 36 ++++++++++++++--- ...sue-114797-bad-parentheses-dyn-trait.fixed | 2 +- .../issue-114797-bad-parentheses-dyn-trait.rs | 2 +- tests/ui/traits/metadata-cast-fail-traits.rs | 1 + .../traits/metadata-cast-fail-traits.stderr | 37 ++++++++++++----- tests/ui/traits/metadata-cast-pass.rs | 12 ++++-- tests/ui/traits/upcast_soundness_bug.rs | 3 +- tests/ui/traits/upcast_soundness_bug.stderr | 11 +++++ 19 files changed, 185 insertions(+), 69 deletions(-) create mode 100644 tests/ui/cast/fat-ptr-cast-borrowck.rs create mode 100644 tests/ui/cast/fat-ptr-cast-borrowck.stderr create mode 100644 tests/ui/traits/upcast_soundness_bug.stderr diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index d0d12cbfcf9bb..ac1c071bee80b 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -180,11 +180,13 @@ pub enum SelectionCandidate<'tcx> { } #[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)] -pub enum MetadataCastKind { +pub enum MetadataCastKind<'tcx> { /// No further obligations. Unconditional, /// `T: MetadataCast` if `T <: U`. Subtype, + /// Require principals of dyn traits to be equal. + Dyn(ty::PolyExistentialTraitRef<'tcx>, ty::PolyExistentialTraitRef<'tcx>), } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0d53870a0baf0..d46bcc0c22f0d 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1760,7 +1760,7 @@ impl<'tcx> TyCtxt<'tcx> { /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used /// to identify which traits may define a given associated type to help avoid cycle errors. /// Returns a `DefId` iterator. - fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator + 'tcx { + pub fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator + 'tcx { let mut set = FxHashSet::default(); let mut stack = vec![trait_def_id]; diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index a62544e65149d..cdddb695f9c27 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -1183,21 +1183,47 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } + let tcx = self.tcx(); if let ty::Adt(src_def, src_args) = source.kind() && let ty::Adt(tgt_def, tgt_args) = target.kind() && src_def == tgt_def - && Some(src_def.did()) == self.tcx().lang_items().dyn_metadata() + && Some(src_def.did()) == tcx.lang_items().dyn_metadata() { let src_dyn = src_args.type_at(0); let tgt_dyn = tgt_args.type_at(0); - // We could theoretically allow casting the principal away, but `as` casts - // don't allow that, so neither does `MetadataCast` for now. - if let ty::Dynamic(src_pred, _, ty::Dyn) = src_dyn.kind() - && let ty::Dynamic(tgt_pred, _, ty::Dyn) = tgt_dyn.kind() - && src_pred.principal_def_id() == tgt_pred.principal_def_id() + if let ty::Dynamic(src_preds, _, ty::Dyn) = src_dyn.kind() + && let ty::Dynamic(tgt_preds, _, ty::Dyn) = tgt_dyn.kind() { - candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional)); + let mut src_traits = src_preds.auto_traits().collect::>(); + + if let Some(src_principal_did) = src_preds.principal_def_id() { + src_traits.extend(tcx.super_traits_of(src_principal_did)); + } + + for tgt_auto_trait in tgt_preds.auto_traits() { + if !src_traits.contains(&tgt_auto_trait) { + // Adding auto traits that aren't on the source or implied by + // the source principal is not allowed. + return; + } + } + + if let Some(src_principal) = src_preds.principal() { + if let Some(tgt_principal) = tgt_preds.principal() { + candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Dyn( + src_principal, + tgt_principal, + ))); + } else { + // We could theoretically allow casting the principal away, but `as` casts + // don't allow that, so neither does `MetadataCast` for now. + } + } else if tgt_preds.principal().is_none() { + // Casting between auto-trait-only trait objects is allowed if the target + // traits are a subset of the source traits, which we checked above. + candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional)); + } return; } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 26a8e026b6a09..393fdaf738942 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -281,7 +281,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_metadata_cast_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - kind: MetadataCastKind, + kind: MetadataCastKind<'tcx>, ) -> Result>, SelectionError<'tcx>> { match kind { MetadataCastKind::Unconditional => Ok(Vec::new()), @@ -297,6 +297,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(obligations) } + MetadataCastKind::Dyn(source, target) => { + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(DefineOpaqueTypes::No, source, target) + .map_err(|_| Unimplemented)?; + + Ok(obligations) + } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1bf8578dfda60..92bb6c5057f04 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1910,7 +1910,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | MetadataCastCandidate(MetadataCastKind::Subtype) + | MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..)) | TraitAliasCandidate, ParamCandidate(ref victim_cand), ) => { @@ -2073,7 +2073,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | MetadataCastCandidate(MetadataCastKind::Subtype) + | MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..)) | TraitAliasCandidate, ImplCandidate(_) | ClosureCandidate { .. } @@ -2086,7 +2086,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | MetadataCastCandidate(MetadataCastKind::Subtype) + | MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..)) | TraitAliasCandidate, ) => DropVictim::No, } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 6977681e5a397..f8ecc9ba56da7 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -2192,7 +2192,7 @@ impl dyn Error + Send { let err: Box = self; ::downcast(err).map_err(|s| unsafe { // Reapply the `Send` marker. - Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send)) + mem::transmute::, Box>(s) }) } } @@ -2205,8 +2205,8 @@ impl dyn Error + Send + Sync { pub fn downcast(self: Box) -> Result, Box> { let err: Box = self; ::downcast(err).map_err(|s| unsafe { - // Reapply the `Send + Sync` marker. - Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send + Sync)) + // Reapply the `Send + Sync` markers. + mem::transmute::, Box>(s) }) } } diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs index 249481467e646..ec7e8f27107a1 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs @@ -4,25 +4,6 @@ #![feature(unsized_tuple_coercion)] -trait Foo { - fn foo(&self, _: T) -> u32 { 42 } -} - -trait Bar { - fn bar(&self) { println!("Bar!"); } -} - -impl Foo for () {} -impl Foo for u32 { fn foo(&self, _: u32) -> u32 { self+43 } } -impl Bar for () {} - -unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo+'a)) -> u32 { - let foo_e : *const dyn Foo = t as *const _; - let r_1 = foo_e as *mut dyn Foo; - - (&*r_1).foo(0) -} - #[repr(C)] struct FooS(T); #[repr(C)] @@ -38,11 +19,6 @@ fn tuple_i32_to_u32(u: *const (i32, T)) -> *const (u32, T) { fn main() { - let x = 4u32; - let y : &dyn Foo = &x; - let fl = unsafe { round_trip_and_call(y as *const dyn Foo) }; - assert_eq!(fl, (43+4)); - let s = FooS([0,1,2]); let u: &FooS<[u32]> = &s; let u: *const FooS<[u32]> = u; diff --git a/tests/ui/cast/fat-ptr-cast-borrowck.rs b/tests/ui/cast/fat-ptr-cast-borrowck.rs new file mode 100644 index 0000000000000..a1d24eb08b60b --- /dev/null +++ b/tests/ui/cast/fat-ptr-cast-borrowck.rs @@ -0,0 +1,8 @@ +trait LifetimeParam<'a> {} +fn lifetime_param<'a, 'b>(ptr: *const dyn LifetimeParam<'a>) -> *const dyn LifetimeParam<'b> { + ptr as _ + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/cast/fat-ptr-cast-borrowck.stderr b/tests/ui/cast/fat-ptr-cast-borrowck.stderr new file mode 100644 index 0000000000000..db5c842c6feab --- /dev/null +++ b/tests/ui/cast/fat-ptr-cast-borrowck.stderr @@ -0,0 +1,28 @@ +error: lifetime may not live long enough + --> $DIR/fat-ptr-cast-borrowck.rs:3:5 + | +LL | fn lifetime_param<'a, 'b>(ptr: *const dyn LifetimeParam<'a>) -> *const dyn LifetimeParam<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | ptr as _ + | ^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: lifetime may not live long enough + --> $DIR/fat-ptr-cast-borrowck.rs:3:5 + | +LL | fn lifetime_param<'a, 'b>(ptr: *const dyn LifetimeParam<'a>) -> *const dyn LifetimeParam<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | ptr as _ + | ^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +help: `'b` and `'a` must be the same: replace one with the other + +error: aborting due to 2 previous errors + diff --git a/tests/ui/cast/fat-ptr-cast-rpass.rs b/tests/ui/cast/fat-ptr-cast-rpass.rs index f55b017c19b46..5eadf2272007e 100644 --- a/tests/ui/cast/fat-ptr-cast-rpass.rs +++ b/tests/ui/cast/fat-ptr-cast-rpass.rs @@ -2,7 +2,7 @@ #![feature(ptr_metadata)] -trait Foo { +trait Foo: Send { fn foo(&self) {} } @@ -54,10 +54,10 @@ fn main() { assert_eq!(b, d); assert_eq!(c, d); - // Adding auto traits is OK. + // Adding auto traits is OK if they're implied by the principal. let _ = a as *mut (dyn Foo + Send); - // Casting between auto-trait-only trait objects is OK. - let unprincipled: *mut dyn Send = &mut Bar; + // Removing traits from auto-trait-only trait objects is OK. + let unprincipled: *mut (dyn Send + Sync) = &mut Bar; let _ = unprincipled as *mut dyn Sync; } diff --git a/tests/ui/cast/fat-ptr-cast.rs b/tests/ui/cast/fat-ptr-cast.rs index 3d36d2298d14b..0bf631e303173 100644 --- a/tests/ui/cast/fat-ptr-cast.rs +++ b/tests/ui/cast/fat-ptr-cast.rs @@ -81,9 +81,17 @@ fn main() { let _ = cf as *const [u16]; //~ ERROR is invalid let _ = cf as *const dyn Bar; //~ ERROR is invalid + // Adding auto traits is not allowed. + let _ = cf as *const (dyn Foo + Send); //~ ERROR is invalid + // casting principal away is not allowed for now let _ = cf as *const dyn Send; //~ ERROR is invalid + // Casting between auto-trait-only trait objects requires the target traits + // to be a subset of the source traits. + let unprincipled: *mut dyn Send = &mut (); + let _ = unprincipled as *mut dyn Sync; //~ ERROR is invalid + vec![0.0].iter().map(|s| s as f32).collect::>(); //~ ERROR is invalid } @@ -101,3 +109,8 @@ fn illegal_cast_2(u: *const U) -> *const str { u as *const str //~ ERROR is invalid } + +trait TypeParam {} +fn type_param(ptr: *const dyn TypeParam) -> *const dyn TypeParam { + ptr as _ //~ ERROR is invalid +} diff --git a/tests/ui/cast/fat-ptr-cast.stderr b/tests/ui/cast/fat-ptr-cast.stderr index 951bc9bb906dd..a1c73eb2e5a1e 100644 --- a/tests/ui/cast/fat-ptr-cast.stderr +++ b/tests/ui/cast/fat-ptr-cast.stderr @@ -278,14 +278,30 @@ LL | let _ = cf as *const dyn Bar; | = note: vtable kinds may not match -error[E0606]: casting `*const dyn Foo` as `*const dyn Send` is invalid +error[E0606]: casting `*const dyn Foo` as `*const dyn Foo + Send` is invalid --> $DIR/fat-ptr-cast.rs:85:13 | +LL | let _ = cf as *const (dyn Foo + Send); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error[E0606]: casting `*const dyn Foo` as `*const dyn Send` is invalid + --> $DIR/fat-ptr-cast.rs:88:13 + | LL | let _ = cf as *const dyn Send; | ^^^^^^^^^^^^^^^^^^^^^ | = note: vtable kinds may not match +error[E0606]: casting `*mut dyn Send` as `*mut dyn Sync` is invalid + --> $DIR/fat-ptr-cast.rs:93:13 + | +LL | let _ = unprincipled as *mut dyn Sync; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/fat-ptr-cast.rs:66:13 | @@ -305,7 +321,7 @@ LL | let _ = a as *const dyn Foo; = note: required for the cast from `*const str` to `*const dyn Foo` error[E0606]: casting `&{float}` as `f32` is invalid - --> $DIR/fat-ptr-cast.rs:87:30 + --> $DIR/fat-ptr-cast.rs:95:30 | LL | vec![0.0].iter().map(|s| s as f32).collect::>(); | ^^^^^^^^ @@ -316,7 +332,7 @@ LL | vec![0.0].iter().map(|s| *s as f32).collect::>(); | + error[E0606]: cannot cast `usize` to a pointer that may be wide - --> $DIR/fat-ptr-cast.rs:91:18 + --> $DIR/fat-ptr-cast.rs:99:18 | LL | let s = 0 as *const T; | - ^^^^^^^^ creating a `*const T` requires both an address and type-specific metadata @@ -324,7 +340,7 @@ LL | let s = 0 as *const T; | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` error[E0606]: casting `*const U` as `*const V` is invalid - --> $DIR/fat-ptr-cast.rs:97:5 + --> $DIR/fat-ptr-cast.rs:105:5 | LL | u as *const V | ^^^^^^^^^^^^^ @@ -332,14 +348,22 @@ LL | u as *const V = note: vtable kinds may not match error[E0606]: casting `*const U` as `*const str` is invalid - --> $DIR/fat-ptr-cast.rs:102:5 + --> $DIR/fat-ptr-cast.rs:110:5 | LL | u as *const str | ^^^^^^^^^^^^^^^ | = note: vtable kinds may not match -error: aborting due to 46 previous errors +error[E0606]: casting `*const (dyn TypeParam + 'static)` as `*const dyn TypeParam` is invalid + --> $DIR/fat-ptr-cast.rs:115:5 + | +LL | ptr as _ + | ^^^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 49 previous errors Some errors have detailed explanations: E0054, E0277, E0604, E0605, E0606, E0607, E0609. For more information about an error, try `rustc --explain E0054`. diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed index 57387936a4cab..4a00369e34535 100644 --- a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed @@ -5,7 +5,7 @@ trait Trait {} fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) { //~^ ERROR incorrect parentheses around trait bounds - ptr as _ + unsafe { std::mem::transmute(ptr) } } fn foo2(_: &(dyn Trait + Send)) {} diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs index 8a1939bcfe93c..cd779294da4bd 100644 --- a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs @@ -5,7 +5,7 @@ trait Trait {} fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { //~^ ERROR incorrect parentheses around trait bounds - ptr as _ + unsafe { std::mem::transmute(ptr) } } fn foo2(_: &dyn (Trait + Send)) {} diff --git a/tests/ui/traits/metadata-cast-fail-traits.rs b/tests/ui/traits/metadata-cast-fail-traits.rs index e1624e1b79c18..78c42879e9b7f 100644 --- a/tests/ui/traits/metadata-cast-fail-traits.rs +++ b/tests/ui/traits/metadata-cast-fail-traits.rs @@ -23,6 +23,7 @@ fn main() { check::<(), DynMetadata>(); //~ ERROR not satisfied check::>(); //~ ERROR not satisfied check::, usize>(); //~ ERROR not satisfied + check::, dyn Trait + Send>(); //~ ERROR not satisfied check::, DynMetadata>(); //~ ERROR not satisfied check::(); //~ ERROR not satisfied diff --git a/tests/ui/traits/metadata-cast-fail-traits.stderr b/tests/ui/traits/metadata-cast-fail-traits.stderr index 7525335c4d7bc..dec599a7970fa 100644 --- a/tests/ui/traits/metadata-cast-fail-traits.stderr +++ b/tests/ui/traits/metadata-cast-fail-traits.stderr @@ -58,9 +58,24 @@ LL | where LL | T: MetadataCast, | ^^^^^^^^^^^^^^^ required by this bound in `check` -error[E0277]: the trait bound `DynMetadata: MetadataCast>` is not satisfied +error[E0277]: the trait bound `DynMetadata: MetadataCast` is not satisfied --> $DIR/metadata-cast-fail-traits.rs:26:13 | +LL | check::, dyn Trait + Send>(); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `MetadataCast` is not implemented for `DynMetadata` + | +note: required by a bound in `check` + --> $DIR/metadata-cast-fail-traits.rs:14:8 + | +LL | fn check() + | ----- required by a bound in this function +LL | where +LL | T: MetadataCast, + | ^^^^^^^^^^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `DynMetadata: MetadataCast>` is not satisfied + --> $DIR/metadata-cast-fail-traits.rs:27:13 + | LL | check::, DynMetadata>(); | ^^^^^^^^^^^^^^^^^^^^^^ the trait `MetadataCast>` is not implemented for `DynMetadata` | @@ -74,7 +89,7 @@ LL | T: MetadataCast, | ^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `dyn Trait + Send: MetadataCast` is not satisfied - --> $DIR/metadata-cast-fail-traits.rs:27:13 + --> $DIR/metadata-cast-fail-traits.rs:28:13 | LL | check::(); | ^^^^^^^^^^^^^^^^ the trait `MetadataCast` is not implemented for `dyn Trait + Send` @@ -89,7 +104,7 @@ LL | T: MetadataCast, | ^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `dyn MetadataCast: MetadataCast` is not satisfied - --> $DIR/metadata-cast-fail-traits.rs:30:13 + --> $DIR/metadata-cast-fail-traits.rs:31:13 | LL | check::, usize>(); | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `MetadataCast` is not implemented for `dyn MetadataCast` @@ -104,7 +119,7 @@ LL | T: MetadataCast, | ^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `DynMetadata: MetadataCast>` is not satisfied - --> $DIR/metadata-cast-fail-traits.rs:33:13 + --> $DIR/metadata-cast-fail-traits.rs:34:13 | LL | check::, DynMetadata>(); | ^^^^^^^^^^^^^^^^^^^^^^ the trait `MetadataCast>` is not implemented for `DynMetadata` @@ -119,7 +134,7 @@ LL | T: MetadataCast, | ^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `(): MetadataCast` is not satisfied - --> $DIR/metadata-cast-fail-traits.rs:37:29 + --> $DIR/metadata-cast-fail-traits.rs:38:29 | LL | let _: *mut [u8] = cast(thin); | ---- ^^^^ the trait `MetadataCast` is not implemented for `()` @@ -137,7 +152,7 @@ LL | T: PointerCast, | ^^^^^^^^^^^^^^ required by this bound in `cast` error[E0277]: the trait bound `(): MetadataCast>` is not satisfied - --> $DIR/metadata-cast-fail-traits.rs:38:34 + --> $DIR/metadata-cast-fail-traits.rs:39:34 | LL | let _: *mut dyn Trait = cast(thin); | ---- ^^^^ the trait `MetadataCast>` is not implemented for `()` @@ -155,7 +170,7 @@ LL | T: PointerCast, | ^^^^^^^^^^^^^^ required by this bound in `cast` error[E0277]: the trait bound `DynMetadata: MetadataCast` is not satisfied - --> $DIR/metadata-cast-fail-traits.rs:39:29 + --> $DIR/metadata-cast-fail-traits.rs:40:29 | LL | let _: *mut [u8] = cast(trait_object); | ---- ^^^^^^^^^^^^ the trait `MetadataCast` is not implemented for `DynMetadata` @@ -173,7 +188,7 @@ LL | T: PointerCast, | ^^^^^^^^^^^^^^ required by this bound in `cast` error[E0277]: the trait bound `usize: MetadataCast>` is not satisfied - --> $DIR/metadata-cast-fail-traits.rs:40:34 + --> $DIR/metadata-cast-fail-traits.rs:41:34 | LL | let _: *mut dyn Trait = cast(slice); | ---- ^^^^^ the trait `MetadataCast>` is not implemented for `usize` @@ -191,7 +206,7 @@ LL | T: PointerCast, | ^^^^^^^^^^^^^^ required by this bound in `cast` error[E0277]: the trait bound `DynMetadata: MetadataCast>` is not satisfied - --> $DIR/metadata-cast-fail-traits.rs:41:35 + --> $DIR/metadata-cast-fail-traits.rs:42:35 | LL | let _: *mut dyn Trait2 = cast(trait_object); | ---- ^^^^^^^^^^^^ the trait `MetadataCast>` is not implemented for `DynMetadata` @@ -209,7 +224,7 @@ LL | T: PointerCast, | ^^^^^^^^^^^^^^ required by this bound in `cast` error[E0277]: the trait bound `DynMetadata: MetadataCast>` is not satisfied - --> $DIR/metadata-cast-fail-traits.rs:44:33 + --> $DIR/metadata-cast-fail-traits.rs:45:33 | LL | let _: *mut dyn Send = cast(trait_object); | ---- ^^^^^^^^^^^^ the trait `MetadataCast>` is not implemented for `DynMetadata` @@ -226,6 +241,6 @@ LL | where LL | T: PointerCast, | ^^^^^^^^^^^^^^ required by this bound in `cast` -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/metadata-cast-pass.rs b/tests/ui/traits/metadata-cast-pass.rs index 73104781ac086..436d59d84bf0f 100644 --- a/tests/ui/traits/metadata-cast-pass.rs +++ b/tests/ui/traits/metadata-cast-pass.rs @@ -17,6 +17,7 @@ where } trait Trait {} +trait ImpliesSend: Send {} fn main() { // any to () is OK @@ -31,9 +32,12 @@ fn main() { check::, DynMetadata>(); check::, dyn MetadataCast>(); - // changing auto traits of trait object in DynMetadata is OK - check::, DynMetadata>(); - check::, DynMetadata>(); + // removing auto traits of trait object in DynMetadata is OK + check::, DynMetadata>(); + check::, DynMetadata>(); + + // adding implied auto traits is OK + check::, DynMetadata>(); } fn lifetimes_ok<'a, 'b>() { @@ -45,7 +49,7 @@ fn lifetimes_ok<'a, 'b>() { check::<&'static (), &'a ()>(); } -fn do_casts<'a>(thin: &mut i32, slice: &mut [i32], trait_object: &mut dyn Trait) { +fn do_casts<'a>(thin: &mut i32, slice: &mut [i32], trait_object: &mut (dyn Trait + Send + Sync)) { let _: *mut u8 = cast(thin); let _: *mut u8 = cast(slice); let _: *mut [u8] = cast(slice); diff --git a/tests/ui/traits/upcast_soundness_bug.rs b/tests/ui/traits/upcast_soundness_bug.rs index 32e32850925f7..ed51b6b38a7c6 100644 --- a/tests/ui/traits/upcast_soundness_bug.rs +++ b/tests/ui/traits/upcast_soundness_bug.rs @@ -1,6 +1,4 @@ #![feature(trait_upcasting)] -// known-bug: #120222 -// check-pass //! This will segfault at runtime. pub trait SupSupA { @@ -56,6 +54,7 @@ pub fn user2() -> &'static dyn Trait { fn main() { let p: *const dyn Trait = &(); let p = p as *const dyn Trait; // <- this is bad! + //~^ ERROR casting `*const dyn Trait` as `*const dyn Trait` is invalid let p = p as *const dyn Super; // <- this upcast accesses improper vtable entry // accessing from L__unnamed_2 the position for the 'Super vtable (pointer)', // thus reading 'null pointer for missing_method' diff --git a/tests/ui/traits/upcast_soundness_bug.stderr b/tests/ui/traits/upcast_soundness_bug.stderr new file mode 100644 index 0000000000000..0187ce808e703 --- /dev/null +++ b/tests/ui/traits/upcast_soundness_bug.stderr @@ -0,0 +1,11 @@ +error[E0606]: casting `*const dyn Trait` as `*const dyn Trait` is invalid + --> $DIR/upcast_soundness_bug.rs:56:13 + | +LL | let p = p as *const dyn Trait; // <- this is bad! + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0606`. From d5d4d9ff6988b33ecc02969f4990f0229a265420 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Sat, 3 Feb 2024 00:09:46 +0100 Subject: [PATCH 9/9] fix soundness, but regress stable code --- compiler/rustc_trait_selection/src/traits/project.rs | 2 +- tests/ui/traits/pointee-normalize-equate.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 5d5145bf74fa3..6373a4d48fadb 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2311,7 +2311,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>( ); let sized_obligation = obligation.with(tcx, sized_predicate); if self_ty == tail - || selcx.infcx.predicate_must_hold_considering_regions(&sized_obligation) + && selcx.infcx.predicate_must_hold_modulo_regions(&sized_obligation) { // If the `self_ty` is `Sized`, then the metadata is `()`. // We check this before projecting to the metadata of `tail`, diff --git a/tests/ui/traits/pointee-normalize-equate.rs b/tests/ui/traits/pointee-normalize-equate.rs index 7c775b0049794..8405e3bfd9b0c 100644 --- a/tests/ui/traits/pointee-normalize-equate.rs +++ b/tests/ui/traits/pointee-normalize-equate.rs @@ -14,15 +14,15 @@ where struct Wrapper(T); -// if `Wrapper: ?Sized` then normalize `Wrapper::Metadata` -> `T::Metadata` +// normalize `Wrapper::Metadata` -> `T::Metadata` fn wrapper_to_tail(ptr: *const T) -> *const Wrapper { cast_same_meta(ptr) } -// if `Wrapper: Sized` then normalize `Wrapper::Metadata` -> `()` +// normalize `Wrapper::Metadata` -> `T::Metadata` -> `()` fn wrapper_to_unit(ptr: *const ()) -> *const Wrapper where - Wrapper: Sized, + T: Sized, { cast_same_meta(ptr) }