diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index b2916cf740338..dde2035973585 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -116,11 +116,8 @@ pub enum SelectionCandidate<'tcx> { has_nested: bool, }, - /// Implementation of the `MetadataCast` trait. - MetadataCastCandidate { - /// Whether we should check for `Self == T`. - require_eq: bool, - }, + /// Implementation of the `MetadataCast` trait. + MetadataCastCandidate(MetadataCastKind<'tcx>), /// Implementation of transmutability trait. TransmutabilityCandidate, @@ -182,6 +179,13 @@ pub enum SelectionCandidate<'tcx> { ConstDestructCandidate(Option), } +#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)] +pub enum MetadataCastKind<'tcx> { + Unconditional, + Subtype, + Dyn(ty::PolyExistentialTraitRef<'tcx>, ty::PolyExistentialTraitRef<'tcx>), +} + /// 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_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index a192860744153..91c6cd2ae157e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -10,6 +10,7 @@ use hir::def_id::DefId; use hir::LangItem; use rustc_hir as hir; 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}; @@ -1121,13 +1122,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let ty::Tuple(tys) = target.kind() && tys.is_empty() { - candidates.vec.push(MetadataCastCandidate { require_eq: false }); + 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 @@ -1136,21 +1142,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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 { require_eq: false }); + 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 always allowed. + candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional)); + } return; } } - if source.has_non_region_infer() || target.has_non_region_infer() { - candidates.ambiguous = true; - } else if self.infcx.can_eq(obligation.param_env, source, target) { - candidates.vec.push(MetadataCastCandidate { require_eq: true }); - } + 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 00b5c21a3ff28..393fdaf738942 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,22 +52,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, data) } - MetadataCastCandidate { require_eq } => { - let data = if require_eq { - 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)?; - - obligations - } else { - Vec::new() - }; - + MetadataCastCandidate(kind) => { + let data = self.confirm_metadata_cast_candidate(obligation, kind)?; ImplSource::Builtin(BuiltinImplSource::Misc, data) } @@ -291,6 +278,37 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations } + fn confirm_metadata_cast_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + kind: MetadataCastKind<'tcx>, + ) -> 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) + } + 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) + } + } + } + #[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 791b8a071e64d..92bb6c5057f04 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1833,14 +1833,14 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ( BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_) - | MetadataCastCandidate { require_eq: false }, + | MetadataCastCandidate(MetadataCastKind::Unconditional), _, ) => DropVictim::Yes, ( _, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_) - | MetadataCastCandidate { require_eq: false }, + | MetadataCastCandidate(MetadataCastKind::Unconditional), ) => DropVictim::No, (ParamCandidate(other), ParamCandidate(victim)) => { @@ -1879,7 +1879,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | MetadataCastCandidate { .. } + | MetadataCastCandidate(_) | TraitAliasCandidate | ObjectCandidate(_) | ProjectionCandidate(_), @@ -1910,7 +1910,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | MetadataCastCandidate { require_eq: true } + | MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..)) | TraitAliasCandidate, ParamCandidate(ref victim_cand), ) => { @@ -1947,7 +1947,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | MetadataCastCandidate { .. } + | MetadataCastCandidate(_) | TraitAliasCandidate, ) => DropVictim::Yes, @@ -1964,7 +1964,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | MetadataCastCandidate { .. } + | MetadataCastCandidate(_) | TraitAliasCandidate, ObjectCandidate(_) | ProjectionCandidate(_), ) => DropVictim::No, @@ -2073,7 +2073,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | MetadataCastCandidate { require_eq: true } + | MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..)) | TraitAliasCandidate, ImplCandidate(_) | ClosureCandidate { .. } @@ -2086,7 +2086,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | MetadataCastCandidate { require_eq: true } + | MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..)) | TraitAliasCandidate, ) => DropVictim::No, } 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.rs b/tests/ui/cast/fat-ptr-cast.rs index 3d36d2298d14b..df960951f4f01 100644 --- a/tests/ui/cast/fat-ptr-cast.rs +++ b/tests/ui/cast/fat-ptr-cast.rs @@ -101,3 +101,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..31f67e2da9a9b 100644 --- a/tests/ui/cast/fat-ptr-cast.stderr +++ b/tests/ui/cast/fat-ptr-cast.stderr @@ -339,7 +339,15 @@ 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:107:5 + | +LL | ptr as _ + | ^^^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 47 previous errors Some errors have detailed explanations: E0054, E0277, E0604, E0605, E0606, E0607, E0609. For more information about an error, try `rustc --explain E0054`.