diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 4fb737f463a86..c69fa3e8acbb3 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -41,6 +41,7 @@ pub enum UnsafetyViolationDetails { MutationOfLayoutConstrainedField, BorrowOfLayoutConstrainedField, CallToFunctionWith, + TraitUpcastingCoercionOfRawPointer, } impl UnsafetyViolationDetails { @@ -102,6 +103,10 @@ impl UnsafetyViolationDetails { "call to function with `#[target_feature]`", "can only be called if the required target features are available", ), + TraitUpcastingCoercionOfRawPointer => ( + "trait upcasting coercion from raw pointer type", + "trait upcasting coercion depends on the validity of the pointer metadata", + ), } } } diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index 95c4237f38396..b82e8d9c54fd5 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -1052,7 +1052,12 @@ fn find_vtable_types_for_unsizing<'tcx>( assert_eq!(source_adt_def, target_adt_def); let CustomCoerceUnsized::Struct(coerce_index) = - monomorphize::custom_coerce_unsize_info(tcx, source_ty, target_ty); + crate::transform::custom_coerce_unsize_info( + tcx, + source_ty, + target_ty, + ty::ParamEnv::reveal_all(), + ); let source_fields = &source_adt_def.non_enum_variant().fields; let target_fields = &target_adt_def.non_enum_variant().fields; diff --git a/compiler/rustc_mir/src/monomorphize/mod.rs b/compiler/rustc_mir/src/monomorphize/mod.rs index 57d2723cf9cfd..ba485f9f0a354 100644 --- a/compiler/rustc_mir/src/monomorphize/mod.rs +++ b/compiler/rustc_mir/src/monomorphize/mod.rs @@ -1,33 +1,4 @@ -use rustc_middle::traits; -use rustc_middle::ty::adjustment::CustomCoerceUnsized; -use rustc_middle::ty::{self, Ty, TyCtxt}; - -use rustc_hir::lang_items::LangItem; - pub mod collector; pub mod partitioning; pub mod polymorphize; pub mod util; - -fn custom_coerce_unsize_info<'tcx>( - tcx: TyCtxt<'tcx>, - source_ty: Ty<'tcx>, - target_ty: Ty<'tcx>, -) -> CustomCoerceUnsized { - let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None); - - let trait_ref = ty::Binder::dummy(ty::TraitRef { - def_id, - substs: tcx.mk_substs_trait(source_ty, &[target_ty.into()]), - }); - - match tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) { - Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData { - impl_def_id, - .. - })) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(), - impl_source => { - bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); - } - } -} diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs index 1ff9bd1572108..e82d21fb6e8ba 100644 --- a/compiler/rustc_mir/src/transform/check_unsafety.rs +++ b/compiler/rustc_mir/src/transform/check_unsafety.rs @@ -8,7 +8,7 @@ use rustc_hir::Node; use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_session::lint::Level; @@ -132,6 +132,24 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { self.register_violations(&violations, &unsafe_blocks); } }, + Rvalue::Cast(kind, source, mir_cast_ty) => { + if let CastKind::Pointer(ty::adjustment::PointerCast::Unsize) = kind { + let mir_source_ty = match *source { + Operand::Copy(ref place) | Operand::Move(ref place) => { + place.as_ref().ty(self.body, self.tcx).ty + } + Operand::Constant(ref constant) => constant.ty(), + }; + + if self.is_trait_upcasting_coercion_from_raw_pointer(mir_source_ty, mir_cast_ty) + { + self.require_unsafe( + UnsafetyViolationKind::General, + UnsafetyViolationDetails::TraitUpcastingCoercionOfRawPointer, + ) + } + } + } _ => {} } self.super_rvalue(rvalue, location); @@ -370,6 +388,56 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { ) } } + + fn is_trait_upcasting_coercion_pointee(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> bool { + match (source.kind(), target.kind()) { + (&ty::Dynamic(data_a, ..), &ty::Dynamic(data_b, ..)) => { + data_a.principal_def_id() != data_b.principal_def_id() + } + _ => false, + } + } + + fn is_trait_upcasting_coercion_from_raw_pointer( + &self, + source: Ty<'tcx>, + target: Ty<'tcx>, + ) -> bool { + match (source.kind(), target.kind()) { + ( + &ty::Ref(_, _a, _), + &ty::Ref(_, _b, _) | &ty::RawPtr(ty::TypeAndMut { ty: _b, .. }), + ) => false, + ( + &ty::RawPtr(ty::TypeAndMut { ty: a, .. }), + &ty::RawPtr(ty::TypeAndMut { ty: b, .. }), + ) => { + if self.is_trait_upcasting_coercion_pointee(a, b) { + true + } else { + false + } + } + (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => { + let custom_coerce_unsized = crate::transform::custom_coerce_unsize_info( + self.tcx, + source, + target, + self.param_env, + ); + match custom_coerce_unsized { + ty::adjustment::CustomCoerceUnsized::Struct(field_idx) => { + assert_eq!(def_a, def_b); + let field = def_a.non_enum_variant().fields.get(field_idx).unwrap(); + let field_ty_a = field.ty(self.tcx, substs_a); + let field_ty_b = field.ty(self.tcx, substs_b); + self.is_trait_upcasting_coercion_from_raw_pointer(field_ty_a, field_ty_b) + } + } + } + _ => bug!("is_trait_upcasting_coercion_from_raw_pointer: called on bad types"), + } + } } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index fa648a6dd49ff..0361fa21c5623 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -8,9 +8,12 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted}; +use rustc_middle::traits; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_span::{Span, Symbol}; +use rustc_hir::lang_items::LangItem; + use std::borrow::Cow; pub mod abort_unwinding_calls; @@ -625,3 +628,27 @@ fn promoted_mir<'tcx>( tcx.arena.alloc(promoted) } + +pub(crate) fn custom_coerce_unsize_info<'tcx>( + tcx: TyCtxt<'tcx>, + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> ty::adjustment::CustomCoerceUnsized { + let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None); + + let trait_ref = ty::Binder::dummy(ty::TraitRef { + def_id, + substs: tcx.mk_substs_trait(source_ty, &[target_ty.into()]), + }); + + match tcx.codegen_fulfill_obligation((param_env, trait_ref)) { + Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData { + impl_def_id, + .. + })) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(), + impl_source => { + bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); + } + } +} diff --git a/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion.rs b/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion.rs new file mode 100644 index 0000000000000..f413dfd6f3259 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion.rs @@ -0,0 +1,30 @@ +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait A { + fn foo_a(&self) {} +} + +trait B { + fn foo_b(&self) {} +} + +trait C: A + B { + fn foo_c(&self) {} +} + +struct S; + +impl A for S {} + +impl B for S {} + +impl C for S {} + +fn unsafe_coercion(v: * const dyn C) { + let g: * const dyn A = v; + //~^ ERROR trait upcasting coercion from +} + + +fn main() {} diff --git a/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion.stderr b/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion.stderr new file mode 100644 index 0000000000000..f564adee4e34e --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion.stderr @@ -0,0 +1,11 @@ +error[E0133]: trait upcasting coercion from raw pointer type is unsafe and requires unsafe function or block + --> $DIR/unsafe-trait-upcasting-coercion.rs:25:28 + | +LL | let g: * const dyn A = v; + | ^ trait upcasting coercion from raw pointer type + | + = note: trait upcasting coercion depends on the validity of the pointer metadata + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion2.rs b/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion2.rs new file mode 100644 index 0000000000000..586718c2e8693 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion2.rs @@ -0,0 +1,29 @@ +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait A { + fn foo_a(&self) {} +} + +trait B { + fn foo_b(&self) {} +} + +trait C: A + B { + fn foo_c(&self) {} +} + +struct S; + +impl A for S {} + +impl B for S {} + +impl C for S {} + +fn invalid_coercion(v: * const Box) { + let g: * const Box = v; + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion2.stderr b/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion2.stderr new file mode 100644 index 0000000000000..45bdf24ac1f5b --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/unsafe-trait-upcasting-coercion2.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/unsafe-trait-upcasting-coercion2.rs:25:33 + | +LL | let g: * const Box = v; + | ------------------ ^ expected trait `A`, found trait `C` + | | + | expected due to this + | + = note: expected raw pointer `*const Box` + found raw pointer `*const Box<(dyn C + 'static)>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.