diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index d9c4647cba3..fd96858010e 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -31,21 +31,10 @@ pub(crate) fn unsized_info<'tcx>( if data_a.principal_def_id() == data_b.principal_def_id() { return old_info; } + // trait upcasting coercion - - // if both of the two `principal`s are `None`, this function would have returned early above. - // and if one of the two `principal`s is `None`, typechecking would have rejected this case. - let principal_a = data_a - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - let principal_b = data_b - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - - let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( - principal_a.with_self_ty(fx.tcx, source), - principal_b.with_self_ty(fx.tcx, source), - )); + let vptr_entry_idx = + fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((source, target)); if let Some(entry_idx) = vptr_entry_idx { let entry_idx = u32::try_from(entry_idx).unwrap(); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 064a51c8f60..a5143a755fe 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -150,19 +150,8 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // trait upcasting coercion - // if both of the two `principal`s are `None`, this function would have returned early above. - // and if one of the two `principal`s is `None`, typechecking would have rejected this case. - let principal_a = data_a - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - let principal_b = data_b - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - - let vptr_entry_idx = cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot(( - principal_a.with_self_ty(cx.tcx(), source), - principal_b.with_self_ty(cx.tcx(), source), - )); + let vptr_entry_idx = + cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target)); if let Some(entry_idx) = vptr_entry_idx { let ptr_ty = cx.type_i8p(); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5b37556985b..85b1274da10 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -987,9 +987,9 @@ rustc_queries! { desc { |tcx| "finding all vtable entries for trait {}", tcx.def_path_str(key.def_id()) } } - query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>)) -> Option { - desc { |tcx| "finding the slot within vtable for trait {} vtable ptr during trait upcasting coercion from {} vtable", - tcx.def_path_str(key.1.def_id()), tcx.def_path_str(key.0.def_id()) } + query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::Ty<'tcx>, ty::Ty<'tcx>)) -> Option { + desc { |tcx| "finding the slot within vtable for trait object {} vtable ptr during trait upcasting coercion from {} vtable", + key.1, key.0 } } query codegen_fulfill_obligation( diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index d8d98bba3fb..469da858ccf 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -505,6 +505,9 @@ pub enum ImplSource<'tcx, N> { /// Successful resolution for a builtin trait. Builtin(ImplSourceBuiltinData), + /// ImplSource for trait upcasting coercion + TraitUpcasting(ImplSourceTraitUpcastingData<'tcx, N>), + /// ImplSource automatically generated for a closure. The `DefId` is the ID /// of the closure expression. This is a `ImplSource::UserDefined` in spirit, but the /// impl is generated by the compiler and does not appear in the source. @@ -540,6 +543,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) | ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(), ImplSource::TraitAlias(d) => d.nested, + ImplSource::TraitUpcasting(d) => d.nested, } } @@ -556,6 +560,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) | ImplSource::Pointee(ImplSourcePointeeData) => &[], ImplSource::TraitAlias(d) => &d.nested[..], + ImplSource::TraitUpcasting(d) => &d.nested[..], } } @@ -607,6 +612,13 @@ impl<'tcx, N> ImplSource<'tcx, N> { substs: d.substs, nested: d.nested.into_iter().map(f).collect(), }), + ImplSource::TraitUpcasting(d) => { + ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { + upcast_trait_ref: d.upcast_trait_ref, + vtable_vptr_slot: d.vtable_vptr_slot, + nested: d.nested.into_iter().map(f).collect(), + }) + } } } } @@ -652,6 +664,20 @@ pub struct ImplSourceAutoImplData { pub nested: Vec, } +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceTraitUpcastingData<'tcx, N> { + /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. + pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, + + /// The vtable is formed by concatenating together the method lists of + /// the base object trait and all supertraits, pointers to supertrait vtable will + /// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable + /// within that vtable. + pub vtable_vptr_slot: Option, + + pub nested: Vec, +} + #[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] pub struct ImplSourceBuiltinData { pub nested: Vec, @@ -663,8 +689,9 @@ pub struct ImplSourceObjectData<'tcx, N> { pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, /// The vtable is formed by concatenating together the method lists of - /// the base object trait and all supertraits; this is the start of - /// `upcast_trait_ref`'s methods in that vtable. + /// the base object trait and all supertraits, pointers to supertrait vtable will + /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods + /// in that vtable. pub vtable_base: usize, pub nested: Vec, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 924568a01a7..3b7c201f3ee 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -135,6 +135,11 @@ pub enum SelectionCandidate<'tcx> { /// `rustc_infer::traits::util::supertraits`. ObjectCandidate(usize), + /// Perform trait upcasting coercion of `dyn Trait` to a supertrait of `Trait`. + /// The index is the position in the iterator returned by + /// `rustc_infer::traits::util::supertraits`. + TraitUpcastingUnsizeCandidate(usize), + BuiltinObjectCandidate, BuiltinUnsizeCandidate, diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index 4f978e63630..aa16e14fedc 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -30,6 +30,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d), super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d), + + super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), } } } @@ -70,6 +72,16 @@ impl fmt::Debug for traits::ImplSourceBuiltinData { } } +impl fmt::Debug for traits::ImplSourceTraitUpcastingData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceTraitUpcastingData(upcast={:?}, vtable_vptr_slot={:?}, nested={:?})", + self.upcast_trait_ref, self.vtable_vptr_slot, self.nested + ) + } +} + impl fmt::Debug for traits::ImplSourceAutoImplData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs index cee4c2e30aa..697e98311e2 100644 --- a/compiler/rustc_mir/src/interpret/cast.rs +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -275,16 +275,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return self.write_immediate(*val, dest); } // trait upcasting coercion - let principal_a = data_a.principal().expect( - "unsize_into_ptr: missing principal trait for trait upcasting coercion", - ); - let principal_b = data_b.principal().expect( - "unsize_into_ptr: missing principal trait for trait upcasting coercion", - ); - let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( - principal_a.with_self_ty(*self.tcx, src_pointee_ty), - principal_b.with_self_ty(*self.tcx, src_pointee_ty), + src_pointee_ty, + dest_pointee_ty, )); if let Some(entry_idx) = vptr_entry_idx { diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs index ace7cffd16d..38ab26d66ac 100644 --- a/compiler/rustc_query_impl/src/keys.rs +++ b/compiler/rustc_query_impl/src/keys.rs @@ -332,6 +332,16 @@ impl<'tcx> Key for Ty<'tcx> { } } +impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) { + #[inline(always)] + fn query_crate_is_local(&self) -> bool { + true + } + fn default_span(&self, _: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for &'tcx ty::List> { #[inline(always)] fn query_crate_is_local(&self) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 585fcf795b7..477d29f1a4f 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -28,6 +28,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::{ @@ -759,48 +760,38 @@ fn vtable_trait_first_method_offset<'tcx>( pub fn vtable_trait_upcasting_coercion_new_vptr_slot( tcx: TyCtxt<'tcx>, key: ( - ty::PolyTraitRef<'tcx>, // trait owning vtable - ty::PolyTraitRef<'tcx>, // super trait ref + Ty<'tcx>, // trait object type whose trait owning vtable + Ty<'tcx>, // trait object for supertrait ), ) -> Option { - let (trait_owning_vtable, super_trait_ref) = key; - let super_trait_did = super_trait_ref.def_id(); - // FIXME: take substsref part into account here after upcasting coercion allows the same def_id occur - // multiple times. + let (source, target) = key; + assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer()); + assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer()); - let vtable_segment_callback = { - let mut vptr_offset = 0; - move |segment| { - match segment { - VtblSegment::MetadataDSA => { - vptr_offset += COMMON_VTABLE_ENTRIES.len(); - } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - vptr_offset += util::count_own_vtable_entries(tcx, trait_ref); - if trait_ref.def_id() == super_trait_did { - if emit_vptr { - return ControlFlow::Break(Some(vptr_offset)); - } else { - return ControlFlow::Break(None); - } - } + // this has been typecked-before, so diagnostics is not really needed. + let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None); - if emit_vptr { - vptr_offset += 1; - } - } - } - ControlFlow::Continue(()) - } + let trait_ref = ty::TraitRef { + def_id: unsize_trait_did, + substs: tcx.mk_substs_trait(source, &[target.into()]), + }; + let obligation = Obligation::new( + ObligationCause::dummy(), + ty::ParamEnv::reveal_all(), + ty::Binder::dummy(ty::TraitPredicate { trait_ref, constness: hir::Constness::NotConst }), + ); + + let implsrc = tcx.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + selcx.select(&obligation).unwrap() + }); + + let implsrc_traitcasting = match implsrc { + Some(ImplSource::TraitUpcasting(data)) => data, + _ => bug!(), }; - if let Some(vptr_offset) = - prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback) - { - vptr_offset - } else { - bug!("Failed to find info for expected trait in vtable"); - } + implsrc_traitcasting.vtable_vptr_slot } pub fn provide(providers: &mut ty::query::Providers) { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index d1ab9fa025e..f75f7b887a5 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1483,7 +1483,9 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // why we special case object types. false } - super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) => { + super::ImplSource::AutoImpl(..) + | super::ImplSource::Builtin(..) + | super::ImplSource::TraitUpcasting(_) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -1554,6 +1556,7 @@ fn confirm_select_candidate<'cx, 'tcx>( | super::ImplSource::AutoImpl(..) | super::ImplSource::Param(..) | super::ImplSource::Builtin(..) + | super::ImplSource::TraitUpcasting(_) | super::ImplSource::TraitAlias(..) => { // we don't create Select candidates with this kind of resolution span_bug!( 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 4312cc94682..e18828fec3f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -690,19 +690,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?source, ?target, "assemble_candidates_for_unsizing"); - let may_apply = match (source.kind(), target.kind()) { + match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { - // See `confirm_builtin_unsize_candidate` for more info. + // Upcast coercions permit several things: + // + // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` + // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` + // 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar` + // + // Note that neither of the first two of these changes requires any + // change at runtime. The third needs to change pointer metadata at runtime. + // + // We always perform upcasting coercions when we can because of reason + // #2 (region bounds). let auto_traits_compatible = data_b .auto_traits() // All of a's auto traits need to be in b's auto traits. .all(|b| data_a.auto_traits().any(|a| a == b)); - auto_traits_compatible + if auto_traits_compatible { + let principal_def_id_a = data_a.principal_def_id(); + let principal_def_id_b = data_b.principal_def_id(); + if principal_def_id_a == principal_def_id_b { + // no cyclic + candidates.vec.push(BuiltinUnsizeCandidate); + } else if principal_def_id_a.is_some() && principal_def_id_b.is_some() { + // not casual unsizing, now check whether this is trait upcasting coercion. + let principal_a = data_a.principal().unwrap(); + let target_trait_did = principal_def_id_b.unwrap(); + let source_trait_ref = principal_a.with_self_ty(self.tcx(), source); + for (idx, upcast_trait_ref) in + util::supertraits(self.tcx(), source_trait_ref).enumerate() + { + if upcast_trait_ref.def_id() == target_trait_did { + candidates.vec.push(TraitUpcastingUnsizeCandidate(idx)); + } + } + } + } } // `T` -> `Trait` - (_, &ty::Dynamic(..)) => true, + (_, &ty::Dynamic(..)) => { + candidates.vec.push(BuiltinUnsizeCandidate); + } // Ambiguous handling is below `T` -> `Trait`, because inference // variables can still implement `Unsize` and nested @@ -710,26 +741,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => { debug!("assemble_candidates_for_unsizing: ambiguous"); candidates.ambiguous = true; - false } // `[T; n]` -> `[T]` - (&ty::Array(..), &ty::Slice(_)) => true, + (&ty::Array(..), &ty::Slice(_)) => { + candidates.vec.push(BuiltinUnsizeCandidate); + } // `Struct` -> `Struct` (&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => { - def_id_a == def_id_b + if def_id_a == def_id_b { + candidates.vec.push(BuiltinUnsizeCandidate); + } } // `(.., T)` -> `(.., U)` - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(), + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { + if tys_a.len() == tys_b.len() { + candidates.vec.push(BuiltinUnsizeCandidate); + } + } - _ => false, + _ => {} }; - - if may_apply { - candidates.vec.push(BuiltinUnsizeCandidate); - } } fn assemble_candidates_for_trait_alias( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 0c2099593a2..0f2f5357eb7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -26,12 +26,13 @@ use crate::traits::Normalized; use crate::traits::OutputTypeParameterMismatch; use crate::traits::Selection; use crate::traits::TraitNotObjectSafe; +use crate::traits::VtblSegment; use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation}; use crate::traits::{ ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData, - ImplSourceUserDefinedData, + ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, }; use crate::traits::{ObjectCastObligation, PredicateObligation, TraitObligation}; use crate::traits::{Obligation, ObligationCause}; @@ -42,6 +43,7 @@ use super::SelectionCandidate::{self, *}; use super::SelectionContext; use std::iter; +use std::ops::ControlFlow; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] @@ -118,6 +120,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let data = self.confirm_builtin_unsize_candidate(obligation)?; Ok(ImplSource::Builtin(data)) } + + TraitUpcastingUnsizeCandidate(idx) => { + let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; + Ok(ImplSource::TraitUpcasting(data)) + } } } @@ -685,6 +692,114 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) } + fn confirm_trait_upcasting_unsize_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + idx: usize, + ) -> Result>, SelectionError<'tcx>> + { + let tcx = self.tcx(); + + // `assemble_candidates_for_unsizing` should ensure there are no late-bound + // regions here. See the comment there for more details. + let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); + let target = self.infcx.shallow_resolve(target); + + debug!(?source, ?target, "confirm_trait_upcasting_unsize_candidate"); + + let mut nested = vec![]; + let source_trait_ref; + let upcast_trait_ref; + match (source.kind(), target.kind()) { + // TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion). + (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { + // See `assemble_candidates_for_unsizing` for more info. + // We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`. + let principal_a = data_a.principal().unwrap(); + source_trait_ref = principal_a.with_self_ty(tcx, source); + upcast_trait_ref = util::supertraits(tcx, source_trait_ref).nth(idx).unwrap(); + assert_eq!(data_b.principal_def_id(), Some(upcast_trait_ref.def_id())); + let existential_predicate = upcast_trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( + tcx, trait_ref, + )) + }); + let iter = Some(existential_predicate) + .into_iter() + .chain( + data_a + .projection_bounds() + .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)), + ) + .chain( + data_b + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let existential_predicates = tcx.mk_poly_existential_predicates(iter); + let source_trait = tcx.mk_dynamic(existential_predicates, r_b); + + // Require that the traits involved in this upcast are **equal**; + // only the **lifetime bound** is changed. + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .sup(target, source_trait) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // Register one obligation for 'a: 'b. + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObjectCastObligation(target), + ); + let outlives = ty::OutlivesPredicate(r_a, r_b); + nested.push(Obligation::with_depth( + cause, + obligation.recursion_depth + 1, + obligation.param_env, + obligation.predicate.rebind(outlives).to_predicate(tcx), + )); + } + _ => bug!(), + }; + + let vtable_segment_callback = { + let mut vptr_offset = 0; + move |segment| { + match segment { + VtblSegment::MetadataDSA => { + vptr_offset += ty::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + vptr_offset += util::count_own_vtable_entries(tcx, trait_ref); + if trait_ref == upcast_trait_ref { + if emit_vptr { + return ControlFlow::Break(Some(vptr_offset)); + } else { + return ControlFlow::Break(None); + } + } + + if emit_vptr { + vptr_offset += 1; + } + } + } + ControlFlow::Continue(()) + } + }; + + let vtable_vptr_slot = + super::super::prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback) + .unwrap(); + + Ok(ImplSourceTraitUpcastingData { upcast_trait_ref, vtable_vptr_slot, nested }) + } + fn confirm_builtin_unsize_candidate( &mut self, obligation: &TraitObligation<'tcx>, @@ -701,58 +816,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut nested = vec![]; match (source.kind(), target.kind()) { - // Trait+Kx+'a -> Trait+Ky+'b (upcasts). + // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { - // Upcast coercions permit several things: - // - // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` - // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` - // 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar` - // - // Note that neither of the first two of these changes requires any - // change at runtime. The third needs to change pointer metadata at runtime. - // - // We always perform upcasting coercions when we can because of reason - // #2 (region bounds). - + // See `assemble_candidates_for_unsizing` for more info. // We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`. - - let principal_a = data_a.principal(); - let principal_def_id_b = data_b.principal_def_id(); - - let existential_predicate = if let Some(principal_a) = principal_a { - let source_trait_ref = principal_a.with_self_ty(tcx, source); - let target_trait_did = principal_def_id_b.ok_or_else(|| Unimplemented)?; - let upcast_idx = util::supertraits(tcx, source_trait_ref) - .position(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did) - .ok_or_else(|| Unimplemented)?; - // FIXME(crlf0710): This is less than ideal, for example, - // if the trait is defined as `trait Foo: Bar + Bar`, - // the coercion from Box to Box> is actually ambiguous. - // We currently make this coercion fail for now. - // - // see #65991 for more information. - if util::supertraits(tcx, source_trait_ref) - .skip(upcast_idx + 1) - .any(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did) - { - return Err(Unimplemented); - } - let target_trait_ref = - util::supertraits(tcx, source_trait_ref).nth(upcast_idx).unwrap(); - let existential_predicate = target_trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( - tcx, trait_ref, - )) - }); - Some(existential_predicate) - } else if principal_def_id_b.is_none() { - None - } else { - return Err(Unimplemented); - }; - - let iter = existential_predicate + let iter = data_a + .principal() + .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) .into_iter() .chain( data_a diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c9bcc44fab6..14d8aa494be 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1516,6 +1516,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate(..) | ObjectCandidate(_) @@ -1533,6 +1534,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate(..), ParamCandidate(ref cand), @@ -1562,6 +1564,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate(..), ) => true, @@ -1573,6 +1576,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate(..), ObjectCandidate(_) | ProjectionCandidate(_), @@ -1646,6 +1650,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate(..), ImplCandidate(_) @@ -1654,6 +1659,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate(..), ) => false, diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 469ac04e545..4c03abb38ca 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -381,7 +381,8 @@ fn resolve_associated_item<'tcx>( | traits::ImplSource::Param(..) | traits::ImplSource::TraitAlias(..) | traits::ImplSource::DiscriminantKind(..) - | traits::ImplSource::Pointee(..) => None, + | traits::ImplSource::Pointee(..) + | traits::ImplSource::TraitUpcasting(_) => None, }) } diff --git a/src/test/ui/traits/trait-upcasting/correct-supertrait-substitution.rs b/src/test/ui/traits/trait-upcasting/correct-supertrait-substitution.rs new file mode 100644 index 00000000000..8d0a9ef0ace --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/correct-supertrait-substitution.rs @@ -0,0 +1,39 @@ +// run-pass +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait Foo: Bar + Bar {} +trait Bar { + fn bar(&self) -> String { + T::default().to_string() + } +} + +struct S1; + +impl Bar for S1 {} +impl Foo for S1 {} + +struct S2; +impl Bar for S2 {} +impl Bar for S2 {} +impl Foo for S2 {} + +fn test1(x: &dyn Foo) { + let s = x as &dyn Bar; + assert_eq!("0", &s.bar().to_string()); +} + +fn test2(x: &dyn Foo) { + let p = x as &dyn Bar; + assert_eq!("0", &p.bar().to_string()); + let q = x as &dyn Bar; + assert_eq!("false", &q.bar().to_string()); +} + +fn main() { + let s1 = S1; + test1(&s1); + let s2 = S2; + test2(&s2); +} diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs index 1a0e5072843..79ddedd4187 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs @@ -9,12 +9,8 @@ trait Bar { } fn test_specific(x: &dyn Foo) { - let _ = x as &dyn Bar; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar` is not satisfied - let _ = x as &dyn Bar; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar` is not satisfied + let _ = x as &dyn Bar; // OK + let _ = x as &dyn Bar; // OK } fn test_unknown_version(x: &dyn Foo) { @@ -24,9 +20,7 @@ fn test_unknown_version(x: &dyn Foo) { } fn test_infer_version(x: &dyn Foo) { - let a = x as &dyn Bar<_>; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar` is not satisfied + let a = x as &dyn Bar<_>; // OK let _: Option = a.bar(); } diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr index da647f16c3d..44f32e0cec9 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr @@ -1,43 +1,5 @@ -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-1.rs:12:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar; // FIXME: OK, eventually - | + - -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-1.rs:15:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar; // FIXME: OK, eventually - | + - -error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-1.rs:12:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^ the trait `Bar` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar` - -error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-1.rs:15:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^ the trait `Bar` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar` - error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` - --> $DIR/type-checking-test-1.rs:21:13 + --> $DIR/type-checking-test-1.rs:17:13 | LL | let _ = x as &dyn Bar<_>; // Ambiguous | ^^^^^^^^^^^^^^^^ invalid cast @@ -48,33 +10,14 @@ LL | let _ = &x as &dyn Bar<_>; // Ambiguous | + error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied - --> $DIR/type-checking-test-1.rs:21:13 + --> $DIR/type-checking-test-1.rs:17:13 | LL | let _ = x as &dyn Bar<_>; // Ambiguous | ^ the trait `Bar<_>` is not implemented for `&dyn Foo` | = note: required for the cast to the object type `dyn Bar<_>` -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-1.rs:27:13 - | -LL | let a = x as &dyn Bar<_>; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let a = &x as &dyn Bar<_>; // FIXME: OK, eventually - | + - -error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-1.rs:27:13 - | -LL | let a = x as &dyn Bar<_>; // FIXME: OK, eventually - | ^ the trait `Bar` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar` - -error: aborting due to 8 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0605. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs index 326df74211e..32754c53803 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs @@ -13,9 +13,7 @@ fn test_specific(x: &dyn Foo) { } fn test_specific2(x: &dyn Foo) { - let _ = x as &dyn Bar; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar` is not satisfied + let _ = x as &dyn Bar; // OK } fn test_specific3(x: &dyn Foo) { diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr index 582eddc7aa0..4ae4c8552c1 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr @@ -1,24 +1,5 @@ -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-2.rs:16:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar; // FIXME: OK, eventually - | + - -error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-2.rs:16:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^ the trait `Bar` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar` - error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-2.rs:22:13 + --> $DIR/type-checking-test-2.rs:20:13 | LL | let _ = x as &dyn Bar; // Error | ^^^^^^^^^^^^^^^^^^ invalid cast @@ -29,7 +10,7 @@ LL | let _ = &x as &dyn Bar; // Error | + error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-2.rs:22:13 + --> $DIR/type-checking-test-2.rs:20:13 | LL | let _ = x as &dyn Bar; // Error | ^ the trait `Bar` is not implemented for `&dyn Foo` @@ -37,7 +18,7 @@ LL | let _ = x as &dyn Bar; // Error = note: required for the cast to the object type `dyn Bar` error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` - --> $DIR/type-checking-test-2.rs:28:13 + --> $DIR/type-checking-test-2.rs:26:13 | LL | let a = x as &dyn Bar<_>; // Ambiguous | ^^^^^^^^^^^^^^^^ invalid cast @@ -48,14 +29,14 @@ LL | let a = &x as &dyn Bar<_>; // Ambiguous | + error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied - --> $DIR/type-checking-test-2.rs:28:13 + --> $DIR/type-checking-test-2.rs:26:13 | LL | let a = x as &dyn Bar<_>; // Ambiguous | ^ the trait `Bar<_>` is not implemented for `&dyn Foo` | = note: required for the cast to the object type `dyn Bar<_>` -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0605. For more information about an error, try `rustc --explain E0277`.