1
Fork 0

Auto merge of #113393 - compiler-errors:next-solver-unsize-rhs, r=lcnr

Normalize the RHS of an `Unsize` goal in the new solver

`Unsize` goals are... tricky. Not only do they structurally match on their self type, but they're also structural on their other type parameter. I'm pretty certain that it is both incomplete and also just plain undesirable to not consider normalizing the RHS of an unsize goal. More practically, I'd like for this code to work:

```rust
trait A {}
trait B: A {}

impl A for usize {}
impl B for usize {}

trait Mirror {
    type Assoc: ?Sized;
}

impl<T: ?Sized> Mirror for T {
    type Assoc = T;
}

fn main() {
    // usize: Unsize<dyn B>
    let x = Box::new(1usize) as Box<<dyn B as Mirror>::Assoc>;
    // dyn A: Unsize<dyn B>
    let y = x as Box<<dyn A as Mirror>::Assoc>;
}
```

---

In order to achieve this, we add `EvalCtxt::normalize_non_self_ty` (naming modulo bikeshedding), which *must* be used for all non-self type arguments that are structurally matched in candidate assembly. Currently this is only necessary for `Unsize`'s argument, but I could see future traits requiring this (hopefully rarely) in the future. It uses `repeat_while_none` to limit infinite looping, and normalizes the self type until it is no longer an alias.

Also, we need to fix feature gate detection for `trait_upcasting` and `unsized_tuple_coercion` when HIR typeck has unnormalized types. We can do that by checking the `ImplSource` returned by selection, which necessitates adding a new impl source for tuple upcasting.
This commit is contained in:
bors 2023-07-25 17:10:31 +00:00
commit 8327047b23
27 changed files with 638 additions and 568 deletions

View file

@ -8,6 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*; use rustc_middle::mir::*;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt}; use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
use rustc_middle::ty::{GenericArgKind, GenericArgs}; use rustc_middle::ty::{GenericArgKind, GenericArgs};
use rustc_middle::ty::{TraitRef, TypeVisitableExt}; use rustc_middle::ty::{TraitRef, TypeVisitableExt};
@ -766,7 +767,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}; };
match implsrc { match implsrc {
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => { Ok(Some(ImplSource::Param(ty::BoundConstness::ConstIfConst, _))) => {
debug!( debug!(
"const_trait_impl: provided {:?} via where-clause in {:?}", "const_trait_impl: provided {:?} via where-clause in {:?}",
trait_ref, param_env trait_ref, param_env
@ -774,7 +775,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
return; return;
} }
// Closure: Fn{Once|Mut} // Closure: Fn{Once|Mut}
Ok(Some(ImplSource::Builtin(_))) Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _)))
if trait_ref.self_ty().is_closure() if trait_ref.self_ty().is_closure()
&& tcx.fn_trait_kind_from_def_id(trait_id).is_some() => && tcx.fn_trait_kind_from_def_id(trait_id).is_some() =>
{ {

View file

@ -7,6 +7,7 @@ use rustc_hir::LangItem;
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::mir::*; use rustc_middle::mir::*;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty}; use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
use rustc_trait_selection::traits::{ use rustc_trait_selection::traits::{
self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
@ -172,7 +173,8 @@ impl Qualif for NeedsNonConstDrop {
if !matches!( if !matches!(
impl_src, impl_src,
ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) ImplSource::Builtin(BuiltinImplSource::Misc, _)
| ImplSource::Param(ty::BoundConstness::ConstIfConst, _)
) { ) {
// If our const destruct candidate is not ConstDestruct or implied by the param env, // If our const destruct candidate is not ConstDestruct or implied by the param env,
// then it's bad // then it's bad

View file

@ -46,6 +46,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
use rustc_infer::traits::{Obligation, PredicateObligation}; use rustc_infer::traits::{Obligation, PredicateObligation};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::adjustment::{ use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
}; };
@ -636,22 +637,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)))
if traits.contains(&trait_pred.def_id()) => if traits.contains(&trait_pred.def_id()) =>
{ {
let trait_pred = self.resolve_vars_if_possible(trait_pred);
if unsize_did == trait_pred.def_id() {
let self_ty = trait_pred.self_ty();
let unsize_ty = trait_pred.trait_ref.args[1].expect_ty();
if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) =
(self_ty.kind(), unsize_ty.kind())
&& data_a.principal_def_id() != data_b.principal_def_id()
{
debug!("coerce_unsized: found trait upcasting coercion");
has_trait_upcasting_coercion = Some((self_ty, unsize_ty));
}
if let ty::Tuple(..) = unsize_ty.kind() {
debug!("coerce_unsized: found unsized tuple coercion");
has_unsized_tuple_coercion = true;
}
}
trait_pred trait_pred
} }
_ => { _ => {
@ -659,6 +644,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
continue; continue;
} }
}; };
let trait_pred = self.resolve_vars_if_possible(trait_pred);
match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) {
// Uncertain or unimplemented. // Uncertain or unimplemented.
Ok(None) => { Ok(None) => {
@ -701,20 +687,28 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// be silent, as it causes a type mismatch later. // be silent, as it causes a type mismatch later.
} }
Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), Ok(Some(impl_source)) => {
// Some builtin coercions are still unstable so we detect
// these here and emit a feature error if coercion doesn't fail
// due to another reason.
match impl_source {
traits::ImplSource::Builtin(
BuiltinImplSource::TraitUpcasting { .. },
_,
) => {
has_trait_upcasting_coercion =
Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1)));
}
traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
has_unsized_tuple_coercion = true;
}
_ => {}
}
queue.extend(impl_source.nested_obligations())
}
} }
} }
if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
feature_err(
&self.tcx.sess.parse_sess,
sym::unsized_tuple_coercion,
self.cause.span,
"unsized tuple coercion is not stable enough for use and is subject to change",
)
.emit();
}
if let Some((sub, sup)) = has_trait_upcasting_coercion if let Some((sub, sup)) = has_trait_upcasting_coercion
&& !self.tcx().features().trait_upcasting && !self.tcx().features().trait_upcasting
{ {
@ -730,6 +724,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
err.emit(); err.emit();
} }
if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
feature_err(
&self.tcx.sess.parse_sess,
sym::unsized_tuple_coercion,
self.cause.span,
"unsized tuple coercion is not stable enough for use and is subject to change",
)
.emit();
}
Ok(coercion) Ok(coercion)
} }

View file

@ -43,7 +43,7 @@ macro_rules! span_bug {
#[macro_export] #[macro_export]
macro_rules! CloneLiftImpls { macro_rules! CloneLiftImpls {
($($ty:ty,)+) => { ($($ty:ty),+ $(,)?) => {
$( $(
impl<'tcx> $crate::ty::Lift<'tcx> for $ty { impl<'tcx> $crate::ty::Lift<'tcx> for $ty {
type Lifted = Self; type Lifted = Self;
@ -59,7 +59,7 @@ macro_rules! CloneLiftImpls {
/// allocated data** (i.e., don't need to be folded). /// allocated data** (i.e., don't need to be folded).
#[macro_export] #[macro_export]
macro_rules! TrivialTypeTraversalImpls { macro_rules! TrivialTypeTraversalImpls {
($($ty:ty,)+) => { ($($ty:ty),+ $(,)?) => {
$( $(
impl<'tcx> $crate::ty::fold::TypeFoldable<$crate::ty::TyCtxt<'tcx>> for $ty { impl<'tcx> $crate::ty::fold::TypeFoldable<$crate::ty::TyCtxt<'tcx>> for $ty {
fn try_fold_with<F: $crate::ty::fold::FallibleTypeFolder<$crate::ty::TyCtxt<'tcx>>>( fn try_fold_with<F: $crate::ty::fold::FallibleTypeFolder<$crate::ty::TyCtxt<'tcx>>>(

View file

@ -178,9 +178,7 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> {
} }
} }
TrivialTypeTraversalAndLiftImpls! { TrivialTypeTraversalAndLiftImpls! { Cache }
Cache,
}
impl<S: Encoder> Encodable<S> for Cache { impl<S: Encoder> Encodable<S> for Cache {
#[inline] #[inline]

View file

@ -67,9 +67,7 @@ impl Into<ErrorGuaranteed> for ReportedErrorInfo {
} }
} }
TrivialTypeTraversalAndLiftImpls! { TrivialTypeTraversalAndLiftImpls! { ErrorHandled }
ErrorHandled,
}
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>; pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;

View file

@ -706,9 +706,7 @@ pub enum BindingForm<'tcx> {
RefForGuard, RefForGuard,
} }
TrivialTypeTraversalAndLiftImpls! { TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx> }
BindingForm<'tcx>,
}
mod binding_form_impl { mod binding_form_impl {
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher};

View file

@ -649,43 +649,31 @@ pub enum ImplSource<'tcx, N> {
/// for some type parameter. The `Vec<N>` represents the /// for some type parameter. The `Vec<N>` represents the
/// obligations incurred from normalizing the where-clause (if /// obligations incurred from normalizing the where-clause (if
/// any). /// any).
Param(Vec<N>, ty::BoundConstness), Param(ty::BoundConstness, Vec<N>),
/// Virtual calls through an object. /// Successful resolution for a builtin impl.
Object(ImplSourceObjectData<N>), Builtin(BuiltinImplSource, Vec<N>),
/// Successful resolution for a builtin trait.
Builtin(Vec<N>),
/// ImplSource for trait upcasting coercion
TraitUpcasting(ImplSourceTraitUpcastingData<N>),
} }
impl<'tcx, N> ImplSource<'tcx, N> { impl<'tcx, N> ImplSource<'tcx, N> {
pub fn nested_obligations(self) -> Vec<N> { pub fn nested_obligations(self) -> Vec<N> {
match self { match self {
ImplSource::UserDefined(i) => i.nested, ImplSource::UserDefined(i) => i.nested,
ImplSource::Param(n, _) | ImplSource::Builtin(n) => n, ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n,
ImplSource::Object(d) => d.nested,
ImplSource::TraitUpcasting(d) => d.nested,
} }
} }
pub fn borrow_nested_obligations(&self) -> &[N] { pub fn borrow_nested_obligations(&self) -> &[N] {
match self { match self {
ImplSource::UserDefined(i) => &i.nested, ImplSource::UserDefined(i) => &i.nested,
ImplSource::Param(n, _) | ImplSource::Builtin(n) => &n, ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => &n,
ImplSource::Object(d) => &d.nested,
ImplSource::TraitUpcasting(d) => &d.nested,
} }
} }
pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] {
match self { match self {
ImplSource::UserDefined(i) => &mut i.nested, ImplSource::UserDefined(i) => &mut i.nested,
ImplSource::Param(n, _) | ImplSource::Builtin(n) => n, ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n,
ImplSource::Object(d) => &mut d.nested,
ImplSource::TraitUpcasting(d) => &mut d.nested,
} }
} }
@ -699,17 +687,9 @@ impl<'tcx, N> ImplSource<'tcx, N> {
args: i.args, args: i.args,
nested: i.nested.into_iter().map(f).collect(), nested: i.nested.into_iter().map(f).collect(),
}), }),
ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct), ImplSource::Param(ct, n) => ImplSource::Param(ct, n.into_iter().map(f).collect()),
ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()), ImplSource::Builtin(source, n) => {
ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData { ImplSource::Builtin(source, n.into_iter().map(f).collect())
vtable_base: o.vtable_base,
nested: o.nested.into_iter().map(f).collect(),
}),
ImplSource::TraitUpcasting(d) => {
ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData {
vtable_vptr_slot: d.vtable_vptr_slot,
nested: d.nested.into_iter().map(f).collect(),
})
} }
} }
} }
@ -733,30 +713,32 @@ pub struct ImplSourceUserDefinedData<'tcx, N> {
pub nested: Vec<N>, pub nested: Vec<N>,
} }
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] #[derive(Copy, Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Debug)]
#[derive(TypeFoldable, TypeVisitable)] pub enum BuiltinImplSource {
pub struct ImplSourceTraitUpcastingData<N> { /// Some builtin impl we don't need to differentiate. This should be used
/// The vtable is formed by concatenating together the method lists of /// unless more specific information is necessary.
/// the base object trait and all supertraits, pointers to supertrait vtable will Misc,
/// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable /// A builtin impl for trait objects.
/// within that vtable. ///
pub vtable_vptr_slot: Option<usize>,
pub nested: Vec<N>,
}
#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, Lift)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct ImplSourceObjectData<N> {
/// The vtable is formed by concatenating together the method lists of /// The vtable is formed by concatenating together the method lists of
/// the base object trait and all supertraits, pointers to supertrait vtable will /// 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 /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods
/// in that vtable. /// in that vtable.
pub vtable_base: usize, Object { vtable_base: usize },
/// The vtable is formed by concatenating together the method lists of
pub nested: Vec<N>, /// 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.
TraitUpcasting { vtable_vptr_slot: Option<usize> },
/// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`.
///
/// This needs to be a separate variant as it is still unstable and we need to emit
/// a feature error when using it on stable.
TupleUnsizing,
} }
TrivialTypeTraversalAndLiftImpls! { BuiltinImplSource }
#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)]
pub enum ObjectSafetyViolation { pub enum ObjectSafetyViolation {
/// `Self: Sized` declared on the trait. /// `Self: Sized` declared on the trait.

View file

@ -304,9 +304,7 @@ impl From<ErrorGuaranteed> for OverflowError {
} }
} }
TrivialTypeTraversalAndLiftImpls! { TrivialTypeTraversalAndLiftImpls! { OverflowError }
OverflowError,
}
impl<'tcx> From<OverflowError> for SelectionError<'tcx> { impl<'tcx> From<OverflowError> for SelectionError<'tcx> {
fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> {

View file

@ -73,8 +73,12 @@ pub struct GoalCandidate<'tcx> {
pub enum CandidateKind<'tcx> { pub enum CandidateKind<'tcx> {
/// Probe entered when normalizing the self ty during candidate assembly /// Probe entered when normalizing the self ty during candidate assembly
NormalizedSelfTyAssembly, NormalizedSelfTyAssembly,
DynUpcastingAssembly,
/// A normal candidate for proving a goal /// A normal candidate for proving a goal
Candidate { name: String, result: QueryResult<'tcx> }, Candidate {
name: String,
result: QueryResult<'tcx>,
},
} }
impl Debug for GoalCandidate<'_> { impl Debug for GoalCandidate<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

View file

@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
CandidateKind::NormalizedSelfTyAssembly => { CandidateKind::NormalizedSelfTyAssembly => {
writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
} }
CandidateKind::DynUpcastingAssembly => {
writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:")
}
CandidateKind::Candidate { name, result } => { CandidateKind::Candidate { name, result } => {
writeln!(self.f, "CANDIDATE {}: {:?}", name, result) writeln!(self.f, "CANDIDATE {}: {:?}", name, result)
} }

View file

@ -6,18 +6,16 @@ use std::fmt;
impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match self {
super::ImplSource::UserDefined(ref v) => write!(f, "{:?}", v), super::ImplSource::UserDefined(v) => write!(f, "{:?}", v),
super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d), super::ImplSource::Builtin(source, d) => {
write!(f, "Builtin({source:?}, {d:?})")
super::ImplSource::Object(ref d) => write!(f, "{:?}", d),
super::ImplSource::Param(ref n, ct) => {
write!(f, "ImplSourceParamData({:?}, {:?})", n, ct)
} }
super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), super::ImplSource::Param(ct, n) => {
write!(f, "ImplSourceParamData({:?}, {:?})", n, ct)
}
} }
} }
} }
@ -31,23 +29,3 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx,
) )
} }
} }
impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitUpcastingData<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ImplSourceTraitUpcastingData(vtable_vptr_slot={:?}, nested={:?})",
self.vtable_vptr_slot, self.nested
)
}
}
impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceObjectData<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ImplSourceObjectData(vtable_base={}, nested={:?})",
self.vtable_base, self.nested
)
}
}

View file

@ -27,9 +27,7 @@ impl From<ErrorGuaranteed> for NotConstEvaluatable {
} }
} }
TrivialTypeTraversalAndLiftImpls! { TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable }
NotConstEvaluatable,
}
pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>; pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>;

View file

@ -6,7 +6,7 @@ pub enum BindingMode {
BindByValue(Mutability), BindByValue(Mutability),
} }
TrivialTypeTraversalAndLiftImpls! { BindingMode, } TrivialTypeTraversalAndLiftImpls! { BindingMode }
impl BindingMode { impl BindingMode {
pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode { pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode {

View file

@ -3,17 +3,16 @@
use super::search_graph::OverflowHandler; use super::search_graph::OverflowHandler;
use super::{EvalCtxt, SolverMode}; use super::{EvalCtxt, SolverMode};
use crate::traits::coherence; use crate::traits::coherence;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::elaborate;
use rustc_infer::traits::Reveal; use rustc_infer::traits::Reveal;
use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{fast_reject, TypeFoldable}; use rustc_middle::ty::{fast_reject, TypeFoldable};
use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
use rustc_span::ErrorGuaranteed; use rustc_span::ErrorGuaranteed;
use std::fmt::Debug; use std::fmt::Debug;
@ -89,16 +88,6 @@ pub(super) enum CandidateSource {
AliasBound, AliasBound,
} }
/// Records additional information about what kind of built-in impl this is.
/// This should only be used by selection.
#[derive(Debug, Clone, Copy)]
pub(super) enum BuiltinImplSource {
TraitUpcasting,
Object,
Misc,
Ambiguity,
}
/// Methods used to assemble candidates for either trait or projection goals. /// Methods used to assemble candidates for either trait or projection goals.
pub(super) trait GoalKind<'tcx>: pub(super) trait GoalKind<'tcx>:
TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
@ -281,21 +270,6 @@ pub(super) trait GoalKind<'tcx>:
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>; ) -> QueryResult<'tcx>;
/// The most common forms of unsizing are array to slice, and concrete (Sized)
/// type into a `dyn Trait`. ADTs and Tuples can also have their final field
/// unsized if it's generic.
fn consider_builtin_unsize_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
/// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
/// if `Trait2` is a (transitive) supertrait of `Trait2`.
fn consider_builtin_dyn_upcast_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<CanonicalResponse<'tcx>>;
fn consider_builtin_discriminant_kind_candidate( fn consider_builtin_discriminant_kind_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>, ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
@ -310,6 +284,25 @@ pub(super) trait GoalKind<'tcx>:
ecx: &mut EvalCtxt<'_, 'tcx>, ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>; ) -> QueryResult<'tcx>;
/// Consider (possibly several) candidates to upcast or unsize a type to another
/// type.
///
/// The most common forms of unsizing are array to slice, and concrete (Sized)
/// type into a `dyn Trait`. ADTs and Tuples can also have their final field
/// unsized if it's generic.
///
/// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
/// if `Trait2` is a (transitive) supertrait of `Trait2`.
///
/// We return the `BuiltinImplSource` for each candidate as it is needed
/// for unsize coercion in hir typeck and because it is difficult to
/// otherwise recompute this for codegen. This is a bit of a mess but the
/// easiest way to maintain the existing behavior for now.
fn consider_builtin_unsize_and_upcast_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
} }
impl<'tcx> EvalCtxt<'_, 'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> {
@ -343,7 +336,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
) -> Option<Vec<Candidate<'tcx>>> { ) -> Option<Vec<Candidate<'tcx>>> {
goal.predicate.self_ty().is_ty_var().then(|| { goal.predicate.self_ty().is_ty_var().then(|| {
vec![Candidate { vec![Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result: self result: self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
.unwrap(), .unwrap(),
@ -412,7 +405,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow),
)?; )?;
Ok(vec![Candidate { Ok(vec![Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result, result,
}]) }])
}, },
@ -610,8 +603,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
G::consider_builtin_future_candidate(self, goal) G::consider_builtin_future_candidate(self, goal)
} else if lang_items.gen_trait() == Some(trait_def_id) { } else if lang_items.gen_trait() == Some(trait_def_id) {
G::consider_builtin_generator_candidate(self, goal) G::consider_builtin_generator_candidate(self, goal)
} else if lang_items.unsize_trait() == Some(trait_def_id) {
G::consider_builtin_unsize_candidate(self, goal)
} else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
G::consider_builtin_discriminant_kind_candidate(self, goal) G::consider_builtin_discriminant_kind_candidate(self, goal)
} else if lang_items.destruct_trait() == Some(trait_def_id) { } else if lang_items.destruct_trait() == Some(trait_def_id) {
@ -633,11 +624,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// There may be multiple unsize candidates for a trait with several supertraits: // There may be multiple unsize candidates for a trait with several supertraits:
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>` // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
if lang_items.unsize_trait() == Some(trait_def_id) { if lang_items.unsize_trait() == Some(trait_def_id) {
for result in G::consider_builtin_dyn_upcast_candidates(self, goal) { for (result, source) in G::consider_builtin_unsize_and_upcast_candidates(self, goal) {
candidates.push(Candidate { candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
result,
});
} }
} }
} }
@ -853,29 +841,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
ty::Dynamic(bounds, ..) => bounds, ty::Dynamic(bounds, ..) => bounds,
}; };
let own_bounds: FxIndexSet<_> = // Consider all of the auto-trait and projection bounds, which don't
bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect(); // need to be recorded as a `BuiltinImplSource::Object` since they don't
for assumption in elaborate(tcx, own_bounds.iter().copied()) // really have a vtable base...
// we only care about bounds that match the `Self` type for bound in bounds {
.filter_only_self() match bound.skip_binder() {
{ ty::ExistentialPredicate::Trait(_) => {
// FIXME: Predicates are fully elaborated in the object type's existential bounds // Skip principal
// list. We want to only consider these pre-elaborated projections, and not other }
// projection predicates that we reach by elaborating the principal trait ref, ty::ExistentialPredicate::Projection(_)
// since that'll cause ambiguity. | ty::ExistentialPredicate::AutoTrait(_) => {
// match G::consider_object_bound_candidate(
// We can remove this when we have implemented lifetime intersections in responses. self,
if assumption.as_projection_clause().is_some() && !own_bounds.contains(&assumption) { goal,
continue; bound.with_self_ty(tcx, self_ty),
) {
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result,
}),
Err(NoSolution) => (),
}
}
} }
}
match G::consider_object_bound_candidate(self, goal, assumption) { // FIXME: We only need to do *any* of this if we're considering a trait goal,
Ok(result) => candidates.push(Candidate { // since we don't need to look at any supertrait or anything if we are doing
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object), // a projection goal.
result, if let Some(principal) = bounds.principal() {
}), let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
Err(NoSolution) => (), self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| {
} match G::consider_object_bound_candidate(ecx, goal, assumption.to_predicate(tcx)) {
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object {
vtable_base,
}),
result,
}),
Err(NoSolution) => (),
}
});
} }
} }
@ -895,7 +901,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
{ {
Ok(result) => candidates.push(Candidate { Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result, result,
}), }),
// FIXME: This will be reachable at some point if we're in // FIXME: This will be reachable at some point if we're in

View file

@ -25,6 +25,7 @@ use std::io::Write;
use std::ops::ControlFlow; use std::ops::ControlFlow;
use crate::traits::specialization_graph; use crate::traits::specialization_graph;
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
use super::inspect::ProofTreeBuilder; use super::inspect::ProofTreeBuilder;
use super::search_graph::{self, OverflowHandler}; use super::search_graph::{self, OverflowHandler};
@ -920,4 +921,39 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Err(ErrorHandled::TooGeneric) => None, Err(ErrorHandled::TooGeneric) => None,
} }
} }
/// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor`
/// for every trait ref encountered (including the principal). Passes both the vtable
/// base and the (optional) vptr slot.
pub(super) fn walk_vtable(
&mut self,
principal: ty::PolyTraitRef<'tcx>,
mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option<usize>),
) {
let tcx = self.tcx();
let mut offset = 0;
prepare_vtable_segments::<()>(tcx, principal, |segment| {
match segment {
VtblSegment::MetadataDSA => {
offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
let own_vtable_entries = count_own_vtable_entries(tcx, trait_ref);
supertrait_visitor(
self,
trait_ref,
offset,
emit_vptr.then(|| offset + own_vtable_entries),
);
offset += own_vtable_entries;
if emit_vptr {
offset += 1;
}
}
}
ControlFlow::Continue(())
});
}
} }

View file

@ -1,25 +1,20 @@
use std::ops::ControlFlow;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt};
use rustc_infer::traits::util::supertraits;
use rustc_infer::traits::{ use rustc_infer::traits::{
Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine, Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine,
}; };
use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal}; use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
use rustc_middle::traits::{ use rustc_middle::traits::{
ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError,
ObligationCause, SelectionError,
}; };
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource}; use crate::solve::assembly::{Candidate, CandidateSource};
use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
use crate::solve::inspect::ProofTreeBuilder; use crate::solve::inspect::ProofTreeBuilder;
use crate::solve::search_graph::OverflowHandler; use crate::solve::search_graph::OverflowHandler;
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
use crate::traits::StructurallyNormalizeExt; use crate::traits::StructurallyNormalizeExt;
use crate::traits::TraitEngineExt; use crate::traits::TraitEngineExt;
@ -105,38 +100,26 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
rematch_impl(self, goal, def_id, nested_obligations) rematch_impl(self, goal, def_id, nested_obligations)
} }
// Rematching the dyn upcast or object goal will instantiate the same nested (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
// goals that would have caused the ambiguity, so we can still make progress here
// regardless.
// FIXME: This doesn't actually check the object bounds hold here.
(
_,
CandidateSource::BuiltinImpl(
BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting,
),
) => rematch_object(self, goal, nested_obligations),
(Certainty::Maybe(_), CandidateSource::BuiltinImpl(BuiltinImplSource::Misc))
if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
{ {
rematch_unsize(self, goal, nested_obligations) rematch_unsize(self, goal, nested_obligations, src)
} }
// Technically some builtin impls have nested obligations, but if // Technically some builtin impls have nested obligations, but if
// `Certainty::Yes`, then they should've all been verified and don't // `Certainty::Yes`, then they should've all been verified and don't
// need re-checking. // need re-checking.
(Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => { (Certainty::Yes, CandidateSource::BuiltinImpl(src)) => {
Ok(Some(ImplSource::Builtin(nested_obligations))) Ok(Some(ImplSource::Builtin(src, nested_obligations)))
} }
// It's fine not to do anything to rematch these, since there are no // It's fine not to do anything to rematch these, since there are no
// nested obligations. // nested obligations.
(Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => { (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst))) Ok(Some(ImplSource::Param(ty::BoundConstness::NotConst, nested_obligations)))
} }
(_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity)) (Certainty::Maybe(_), _) => Ok(None),
| (Certainty::Maybe(_), _) => Ok(None),
} }
} }
} }
@ -183,11 +166,12 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>(
} }
(_, CandidateSource::ParamEnv(_)) => true, (_, CandidateSource::ParamEnv(_)) => true,
// FIXME: we could prefer earlier vtable bases perhaps...
( (
CandidateSource::BuiltinImpl(BuiltinImplSource::Object), CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
CandidateSource::BuiltinImpl(BuiltinImplSource::Object), CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
) => false, ) => false,
(_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true, (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true,
(CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => { (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
tcx.specializes((other_def_id, victim_def_id)) tcx.specializes((other_def_id, victim_def_id))
@ -225,102 +209,6 @@ fn rematch_impl<'tcx>(
Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested }))) Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested })))
} }
fn rematch_object<'tcx>(
infcx: &InferCtxt<'tcx>,
goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
mut nested: Vec<PredicateObligation<'tcx>>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
let ty::Dynamic(data, _, source_kind) = *a_ty.kind() else { bug!() };
let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, a_ty);
let (is_upcasting, target_trait_ref_unnormalized) =
if Some(goal.predicate.def_id()) == infcx.tcx.lang_items().unsize_trait() {
assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*");
let b_ty = structurally_normalize(
goal.predicate.trait_ref.args.type_at(1),
infcx,
goal.param_env,
&mut nested,
);
if let ty::Dynamic(data, _, ty::Dyn) = *b_ty.kind() {
// FIXME: We also need to ensure that the source lifetime outlives the
// target lifetime. This doesn't matter for codegen, though, and only
// *really* matters if the goal's certainty is ambiguous.
(true, data.principal().unwrap().with_self_ty(infcx.tcx, a_ty))
} else {
bug!()
}
} else {
(false, ty::Binder::dummy(goal.predicate.trait_ref))
};
let mut target_trait_ref = None;
for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) {
let result = infcx.commit_if_ok(|_| {
infcx.at(&ObligationCause::dummy(), goal.param_env).eq(
DefineOpaqueTypes::No,
target_trait_ref_unnormalized,
candidate_trait_ref,
)
// FIXME: We probably should at least shallowly verify these...
});
match result {
Ok(InferOk { value: (), obligations }) => {
target_trait_ref = Some(candidate_trait_ref);
nested.extend(obligations);
break;
}
Err(_) => continue,
}
}
let target_trait_ref = target_trait_ref.unwrap();
let mut offset = 0;
let Some((vtable_base, vtable_vptr_slot)) =
prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| {
match segment {
VtblSegment::MetadataDSA => {
offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref);
if trait_ref == target_trait_ref {
if emit_vptr {
return ControlFlow::Break((
offset,
Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)),
));
} else {
return ControlFlow::Break((offset, None));
}
}
offset += own_vtable_entries;
if emit_vptr {
offset += 1;
}
}
}
ControlFlow::Continue(())
})
else {
bug!();
};
// If we're upcasting, get the offset of the vtable pointer, otherwise get
// the base of the vtable.
Ok(Some(if is_upcasting {
ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
} else {
ImplSource::Object(ImplSourceObjectData { vtable_base, nested })
}))
}
/// The `Unsize` trait is particularly important to coercion, so we try rematch it. /// The `Unsize` trait is particularly important to coercion, so we try rematch it.
/// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait /// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait
/// goal assembly in the solver, both for soundness and in order to avoid ICEs. /// goal assembly in the solver, both for soundness and in order to avoid ICEs.
@ -328,11 +216,16 @@ fn rematch_unsize<'tcx>(
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
mut nested: Vec<PredicateObligation<'tcx>>, mut nested: Vec<PredicateObligation<'tcx>>,
source: BuiltinImplSource,
) -> SelectionResult<'tcx, Selection<'tcx>> { ) -> SelectionResult<'tcx, Selection<'tcx>> {
let tcx = infcx.tcx; let tcx = infcx.tcx;
let a_ty = goal.predicate.self_ty(); let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
let b_ty = goal.predicate.trait_ref.args.type_at(1); let b_ty = structurally_normalize(
goal.predicate.trait_ref.args.type_at(1),
infcx,
goal.param_env,
&mut nested,
);
match (a_ty.kind(), b_ty.kind()) { match (a_ty.kind(), b_ty.kind()) {
(_, &ty::Dynamic(data, region, ty::Dyn)) => { (_, &ty::Dynamic(data, region, ty::Dyn)) => {
// Check that the type implements all of the predicates of the def-id. // Check that the type implements all of the predicates of the def-id.
@ -360,6 +253,8 @@ fn rematch_unsize<'tcx>(
goal.param_env, goal.param_env,
ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)),
)); ));
Ok(Some(ImplSource::Builtin(source, nested)))
} }
// `[T; n]` -> `[T]` unsizing // `[T; n]` -> `[T]` unsizing
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
@ -370,6 +265,8 @@ fn rematch_unsize<'tcx>(
.expect("expected rematch to succeed") .expect("expected rematch to succeed")
.into_obligations(), .into_obligations(),
); );
Ok(Some(ImplSource::Builtin(source, nested)))
} }
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>` // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
(&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
@ -420,6 +317,8 @@ fn rematch_unsize<'tcx>(
goal.param_env, goal.param_env,
ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
)); ));
Ok(Some(ImplSource::Builtin(source, nested)))
} }
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>` // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
(&ty::Tuple(a_tys), &ty::Tuple(b_tys)) (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
@ -446,14 +345,20 @@ fn rematch_unsize<'tcx>(
goal.param_env, goal.param_env,
ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
)); ));
// We need to be able to detect tuple unsizing to require its feature gate.
assert_eq!(
source,
BuiltinImplSource::TupleUnsizing,
"compiler-errors wants to know if this can ever be triggered..."
);
Ok(Some(ImplSource::Builtin(source, nested)))
} }
// FIXME: We *could* ICE here if either: // FIXME: We *could* ICE here if either:
// 1. the certainty is `Certainty::Yes`, // 1. the certainty is `Certainty::Yes`,
// 2. we're in codegen (which should mean `Certainty::Yes`). // 2. we're in codegen (which should mean `Certainty::Yes`).
_ => return Ok(None), _ => Ok(None),
} }
Ok(Some(ImplSource::Builtin(nested)))
} }
fn structurally_normalize<'tcx>( fn structurally_normalize<'tcx>(

View file

@ -10,6 +10,7 @@ use rustc_infer::traits::specialization_graph::LeafDef;
use rustc_infer::traits::Reveal; use rustc_infer::traits::Reveal;
use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::ProjectionPredicate; use rustc_middle::ty::ProjectionPredicate;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
@ -502,17 +503,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
) )
} }
fn consider_builtin_unsize_candidate( fn consider_builtin_unsize_and_upcast_candidates(
_ecx: &mut EvalCtxt<'_, 'tcx>, _ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> { ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
bug!("`Unsize` does not have an associated type: {:?}", goal);
}
fn consider_builtin_dyn_upcast_candidates(
_ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<CanonicalResponse<'tcx>> {
bug!("`Unsize` does not have an associated type: {:?}", goal); bug!("`Unsize` does not have an associated type: {:?}", goal);
} }

View file

@ -1,13 +1,14 @@
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`. //! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
use super::assembly::{self, structural_traits}; use super::assembly::{self, structural_traits};
use super::search_graph::OverflowHandler;
use super::{EvalCtxt, SolverMode}; use super::{EvalCtxt, SolverMode};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{LangItem, Movability}; use rustc_hir::{LangItem, Movability};
use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::supertraits; use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
use rustc_middle::traits::Reveal; use rustc_middle::traits::{BuiltinImplSource, Reveal};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
@ -365,198 +366,67 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
) )
} }
fn consider_builtin_unsize_candidate( fn consider_builtin_unsize_and_upcast_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>, ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> { ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
if goal.predicate.polarity != ty::ImplPolarity::Positive {
return Err(NoSolution);
}
let tcx = ecx.tcx();
let a_ty = goal.predicate.self_ty();
let b_ty = goal.predicate.trait_ref.args.type_at(1);
if b_ty.is_ty_var() {
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}
ecx.probe_candidate("builtin unsize").enter(|ecx| {
match (a_ty.kind(), b_ty.kind()) {
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
// Dyn upcasting is handled separately, since due to upcasting,
// when there are two supertraits that differ by args, we
// may return more than one query response.
Err(NoSolution)
}
// `T` -> `dyn Trait` unsizing
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
// Can only unsize to an object-safe type
if data
.principal_def_id()
.is_some_and(|def_id| !tcx.check_is_object_safe(def_id))
{
return Err(NoSolution);
}
let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
return Err(NoSolution);
};
// Check that the type implements all of the predicates of the def-id.
// (i.e. the principal, all of the associated types match, and any auto traits)
ecx.add_goals(
data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
);
// The type must be Sized to be unsized.
ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
// The type must outlive the lifetime of the `dyn` we're unsizing into.
ecx.add_goal(
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))),
);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// `[T; n]` -> `[T]` unsizing
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
// We just require that the element type stays the same
ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
(&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
if a_def.is_struct() && a_def.did() == b_def.did() =>
{
let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
// We must be unsizing some type parameters. This also implies
// that the struct has a tail field.
if unsizing_params.is_empty() {
return Err(NoSolution);
}
let tail_field = a_def.non_enum_variant().tail();
let tail_field_ty = tcx.type_of(tail_field.did);
let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
// Substitute just the unsizing params from B into A. The type after
// this substitution must be equal to B. This is so we don't unsize
// unrelated type parameters.
let new_a_args =
tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| {
if unsizing_params.contains(i as u32) { b_args[i] } else { a }
}));
let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args);
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
// types.
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
ecx.add_goal(goal.with(
tcx,
ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
{
let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
let b_last_ty = b_tys.last().unwrap();
// Substitute just the tail field of B., and require that they're equal.
let unsized_a_ty =
Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
// Similar to ADTs, require that the rest of the fields are equal.
ecx.add_goal(goal.with(
tcx,
ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
_ => Err(NoSolution),
}
})
}
fn consider_builtin_dyn_upcast_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<CanonicalResponse<'tcx>> {
if goal.predicate.polarity != ty::ImplPolarity::Positive { if goal.predicate.polarity != ty::ImplPolarity::Positive {
return vec![]; return vec![];
} }
let tcx = ecx.tcx(); ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| {
let a_ty = goal.predicate.self_ty();
// We need to normalize the b_ty since it's matched structurally
// in the other functions below.
let b_ty = match ecx
.normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env)
{
Ok(Some(b_ty)) => {
// If we have a type var, then bail with ambiguity.
if b_ty.is_ty_var() {
return vec![(
ecx.evaluate_added_goals_and_make_canonical_response(
Certainty::AMBIGUOUS,
)
.unwrap(),
BuiltinImplSource::Misc,
)];
} else {
b_ty
}
}
Ok(None) => {
return vec![(
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe(
MaybeCause::Overflow,
))
.unwrap(),
BuiltinImplSource::Misc,
)];
}
Err(_) => return vec![],
};
let a_ty = goal.predicate.self_ty(); let mut results = vec![];
let b_ty = goal.predicate.trait_ref.args.type_at(1); results.extend(ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty));
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { results.extend(
return vec![]; ecx.consider_builtin_unsize_candidate(goal.with(ecx.tcx(), (a_ty, b_ty)))
};
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
return vec![];
};
// All of a's auto traits need to be in b's auto traits.
let auto_traits_compatible =
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
if !auto_traits_compatible {
return vec![];
}
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> {
// Require that all of the trait predicates from A match B, except for
// the auto traits. We do this by constructing a new A type with B's
// auto traits, and equating these types.
let new_a_data = principal
.into_iter() .into_iter()
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) .map(|resp| {
.chain(a_data.iter().filter(|a| { // If we're unsizing from tuple -> tuple, detect
matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) let source =
})) if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..)))
.chain( {
b_data BuiltinImplSource::TupleUnsizing
.auto_traits() } else {
.map(ty::ExistentialPredicate::AutoTrait) BuiltinImplSource::Misc
.map(ty::Binder::dummy), };
); (resp, source)
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); }),
let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); );
// We also require that A's lifetime outlives B's lifetime. results
ecx.eq(goal.param_env, new_a_ty, b_ty)?; })
ecx.add_goal(
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
};
let mut responses = vec![];
// If the principal def ids match (or are both none), then we're not doing
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
if a_data.principal_def_id() == b_data.principal_def_id() {
if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) {
responses.push(response);
}
} else if let Some(a_principal) = a_data.principal()
&& let Some(b_principal) = b_data.principal()
{
for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) {
if super_trait_ref.def_id() != b_principal.def_id() {
continue;
}
let erased_trait_ref = super_trait_ref
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
responses.push(response);
}
}
}
responses
} }
fn consider_builtin_discriminant_kind_candidate( fn consider_builtin_discriminant_kind_candidate(
@ -622,6 +492,220 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
} }
impl<'tcx> EvalCtxt<'_, 'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> {
fn consider_builtin_unsize_candidate(
&mut self,
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
) -> QueryResult<'tcx> {
let Goal { param_env, predicate: (a_ty, b_ty) } = goal;
self.probe_candidate("builtin unsize").enter(|ecx| {
let tcx = ecx.tcx();
match (a_ty.kind(), b_ty.kind()) {
(ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => {
bug!("unexpected type variable in unsize goal")
}
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
// Dyn upcasting is handled separately, since due to upcasting,
// when there are two supertraits that differ by args, we
// may return more than one query response.
Err(NoSolution)
}
// `T` -> `dyn Trait` unsizing
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
// Can only unsize to an object-safe type
if data
.principal_def_id()
.is_some_and(|def_id| !tcx.check_is_object_safe(def_id))
{
return Err(NoSolution);
}
let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
return Err(NoSolution);
};
// Check that the type implements all of the predicates of the def-id.
// (i.e. the principal, all of the associated types match, and any auto traits)
ecx.add_goals(
data.iter()
.map(|pred| Goal::new(tcx, param_env, pred.with_self_ty(tcx, a_ty))),
);
// The type must be Sized to be unsized.
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::TraitRef::new(tcx, sized_def_id, [a_ty]),
));
// The type must outlive the lifetime of the `dyn` we're unsizing into.
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// `[T; n]` -> `[T]` unsizing
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
// We just require that the element type stays the same
ecx.eq(param_env, a_elem_ty, b_elem_ty)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
(&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
if a_def.is_struct() && a_def.did() == b_def.did() =>
{
let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
// We must be unsizing some type parameters. This also implies
// that the struct has a tail field.
if unsizing_params.is_empty() {
return Err(NoSolution);
}
let tail_field = a_def.non_enum_variant().tail();
let tail_field_ty = tcx.type_of(tail_field.did);
let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
// Substitute just the unsizing params from B into A. The type after
// this substitution must be equal to B. This is so we don't unsize
// unrelated type parameters.
let new_a_args =
tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| {
if unsizing_params.contains(i as u32) { b_args[i] } else { a }
}));
let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args);
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
// types.
ecx.eq(param_env, unsized_a_ty, b_ty)?;
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::TraitRef::new(
tcx,
tcx.lang_items().unsize_trait().unwrap(),
[a_tail_ty, b_tail_ty],
),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
{
let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
let b_last_ty = b_tys.last().unwrap();
// Substitute just the tail field of B., and require that they're equal.
let unsized_a_ty =
Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
ecx.eq(param_env, unsized_a_ty, b_ty)?;
// Similar to ADTs, require that the rest of the fields are equal.
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::TraitRef::new(
tcx,
tcx.lang_items().unsize_trait().unwrap(),
[*a_last_ty, *b_last_ty],
),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
_ => Err(NoSolution),
}
})
}
fn consider_builtin_dyn_upcast_candidates(
&mut self,
param_env: ty::ParamEnv<'tcx>,
a_ty: Ty<'tcx>,
b_ty: Ty<'tcx>,
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
if a_ty.is_ty_var() || b_ty.is_ty_var() {
bug!("unexpected type variable in unsize goal")
}
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
return vec![];
};
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
return vec![];
};
let tcx = self.tcx();
// All of a's auto traits need to be in b's auto traits.
let auto_traits_compatible =
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
if !auto_traits_compatible {
return vec![];
}
// Try to match `a_ty` against `b_ty`, replacing `a_ty`'s principal trait ref with
// the supertrait principal and subtyping the types.
let unsize_dyn_to_principal =
|ecx: &mut Self, principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
ecx.probe_candidate("upcast dyn to principle").enter(
|ecx| -> Result<_, NoSolution> {
// Require that all of the trait predicates from A match B, except for
// the auto traits. We do this by constructing a new A type with B's
// auto traits, and equating these types.
let new_a_data = principal
.into_iter()
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
.chain(a_data.iter().filter(|a| {
matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
}))
.chain(
b_data
.auto_traits()
.map(ty::ExistentialPredicate::AutoTrait)
.map(ty::Binder::dummy),
);
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
// We also require that A's lifetime outlives B's lifetime.
ecx.eq(param_env, new_a_ty, b_ty)?;
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
},
)
};
let mut responses = vec![];
// If the principal def ids match (or are both none), then we're not doing
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
if a_data.principal_def_id() == b_data.principal_def_id() {
if let Ok(resp) = unsize_dyn_to_principal(self, a_data.principal()) {
responses.push((resp, BuiltinImplSource::Misc));
}
} else if let Some(a_principal) = a_data.principal() {
self.walk_vtable(
a_principal.with_self_ty(tcx, a_ty),
|ecx, new_a_principal, _, vtable_vptr_slot| {
if let Ok(resp) = unsize_dyn_to_principal(
ecx,
Some(new_a_principal.map_bound(|trait_ref| {
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
})),
) {
responses
.push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }));
}
},
);
}
responses
}
// Return `Some` if there is an impl (built-in or user provided) that may // Return `Some` if there is an impl (built-in or user provided) that may
// hold for the self type of the goal, which for coherence and soundness // hold for the self type of the goal, which for coherence and soundness
// purposes must disqualify the built-in auto impl assembled by considering // purposes must disqualify the built-in auto impl assembled by considering
@ -750,4 +834,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let candidates = self.assemble_and_evaluate_candidates(goal); let candidates = self.assemble_and_evaluate_candidates(goal);
self.merge_candidates(candidates) self.merge_candidates(candidates)
} }
/// Normalize a non-self type when it is structually matched on when solving
/// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty`
/// for the self type, but for other goals, additional normalization of other
/// arguments may be needed to completely implement the semantics of the trait.
///
/// This is required when structurally matching on any trait argument that is
/// not the self type.
fn normalize_non_self_ty(
&mut self,
mut ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<Option<Ty<'tcx>>, NoSolution> {
if !matches!(ty.kind(), ty::Alias(..)) {
return Ok(Some(ty));
}
self.repeat_while_none(
|_| Ok(None),
|ecx| {
let ty::Alias(_, projection_ty) = *ty.kind() else {
return Some(Ok(Some(ty)));
};
let normalized_ty = ecx.next_ty_infer();
let normalizes_to_goal = Goal::new(
ecx.tcx(),
param_env,
ty::Binder::dummy(ty::ProjectionPredicate {
projection_ty,
term: normalized_ty.into(),
}),
);
ecx.add_goal(normalizes_to_goal);
if let Err(err) = ecx.try_evaluate_added_goals() {
return Some(Err(err));
}
ty = ecx.resolve_vars_if_possible(normalized_ty);
None
},
)
}
} }

View file

@ -4,7 +4,6 @@ use super::check_args_compatible;
use super::specialization_graph; use super::specialization_graph;
use super::translate_args; use super::translate_args;
use super::util; use super::util;
use super::ImplSourceUserDefinedData;
use super::MismatchedProjectionTypes; use super::MismatchedProjectionTypes;
use super::Obligation; use super::Obligation;
use super::ObligationCause; use super::ObligationCause;
@ -13,6 +12,9 @@ use super::Selection;
use super::SelectionContext; use super::SelectionContext;
use super::SelectionError; use super::SelectionError;
use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::traits::ImplSource;
use rustc_middle::traits::ImplSourceUserDefinedData;
use crate::errors::InherentProjectionNormalizationOverflow; use crate::errors::InherentProjectionNormalizationOverflow;
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@ -1717,7 +1719,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
}; };
let eligible = match &impl_source { let eligible = match &impl_source {
super::ImplSource::UserDefined(impl_data) => { ImplSource::UserDefined(impl_data) => {
// We have to be careful when projecting out of an // We have to be careful when projecting out of an
// impl because of specialization. If we are not in // impl because of specialization. If we are not in
// codegen (i.e., projection mode is not "any"), and the // codegen (i.e., projection mode is not "any"), and the
@ -1767,7 +1769,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
} }
} }
} }
super::ImplSource::Builtin(..) => { ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
// While a builtin impl may be known to exist, the associated type may not yet // While a builtin impl may be known to exist, the associated type may not yet
// be known. Any type with multiple potential associated types is therefore // be known. Any type with multiple potential associated types is therefore
// not eligible. // not eligible.
@ -1891,7 +1893,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
bug!("unexpected builtin trait with associated type: {trait_ref:?}") bug!("unexpected builtin trait with associated type: {trait_ref:?}")
} }
} }
super::ImplSource::Param(..) => { ImplSource::Param(..) => {
// This case tell us nothing about the value of an // This case tell us nothing about the value of an
// associated type. Consider: // associated type. Consider:
// //
@ -1919,13 +1921,14 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// in `assemble_candidates_from_param_env`. // in `assemble_candidates_from_param_env`.
false false
} }
super::ImplSource::Object(_) => { ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) => {
// Handled by the `Object` projection candidate. See // Handled by the `Object` projection candidate. See
// `assemble_candidates_from_object_ty` for an explanation of // `assemble_candidates_from_object_ty` for an explanation of
// why we special case object types. // why we special case object types.
false false
} }
| super::ImplSource::TraitUpcasting(_) => { ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
| ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
// These traits have no associated types. // These traits have no associated types.
selcx.tcx().sess.delay_span_bug( selcx.tcx().sess.delay_span_bug(
obligation.cause.span, obligation.cause.span,
@ -1985,8 +1988,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
impl_source: Selection<'tcx>, impl_source: Selection<'tcx>,
) -> Progress<'tcx> { ) -> Progress<'tcx> {
match impl_source { match impl_source {
super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
super::ImplSource::Builtin(data) => { ImplSource::Builtin(BuiltinImplSource::Misc, data) => {
let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx()); let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx());
let lang_items = selcx.tcx().lang_items(); let lang_items = selcx.tcx().lang_items();
if lang_items.gen_trait() == Some(trait_def_id) { if lang_items.gen_trait() == Some(trait_def_id) {
@ -2003,9 +2006,10 @@ fn confirm_select_candidate<'cx, 'tcx>(
confirm_builtin_candidate(selcx, obligation, data) confirm_builtin_candidate(selcx, obligation, data)
} }
} }
super::ImplSource::Object(_) ImplSource::Builtin(BuiltinImplSource::Object { .. }, _)
| super::ImplSource::Param(..) | ImplSource::Param(..)
| super::ImplSource::TraitUpcasting(_) => { | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
| ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
// we don't create Select candidates with this kind of resolution // we don't create Select candidates with this kind of resolution
span_bug!( span_bug!(
obligation.cause.span, obligation.cause.span,

View file

@ -11,7 +11,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_middle::traits::SelectionOutputTypeParameterMismatch; use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch};
use rustc_middle::ty::{ use rustc_middle::ty::{
self, Binder, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, self, Binder, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt, TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt,
@ -26,9 +26,9 @@ use crate::traits::vtable::{
}; };
use crate::traits::{ use crate::traits::{
BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause,
Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation, OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection,
PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented, SelectionError, TraitNotObjectSafe, Unimplemented,
}; };
use super::BuiltinImplConditions; use super::BuiltinImplConditions;
@ -48,18 +48,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let mut impl_src = match candidate { let mut impl_src = match candidate {
BuiltinCandidate { has_nested } => { BuiltinCandidate { has_nested } => {
let data = self.confirm_builtin_candidate(obligation, has_nested); let data = self.confirm_builtin_candidate(obligation, has_nested);
ImplSource::Builtin(data) ImplSource::Builtin(BuiltinImplSource::Misc, data)
} }
TransmutabilityCandidate => { TransmutabilityCandidate => {
let data = self.confirm_transmutability_candidate(obligation)?; let data = self.confirm_transmutability_candidate(obligation)?;
ImplSource::Builtin(data) ImplSource::Builtin(BuiltinImplSource::Misc, data)
} }
ParamCandidate(param) => { ParamCandidate(param) => {
let obligations = let obligations =
self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
ImplSource::Param(obligations, param.skip_binder().constness) ImplSource::Param(param.skip_binder().constness, obligations)
} }
ImplCandidate(impl_def_id) => { ImplCandidate(impl_def_id) => {
@ -68,64 +68,57 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
AutoImplCandidate => { AutoImplCandidate => {
let data = self.confirm_auto_impl_candidate(obligation)?; let data = self.confirm_auto_impl_candidate(obligation)?;
ImplSource::Builtin(data) ImplSource::Builtin(BuiltinImplSource::Misc, data)
} }
ProjectionCandidate(idx, constness) => { ProjectionCandidate(idx, constness) => {
let obligations = self.confirm_projection_candidate(obligation, idx)?; let obligations = self.confirm_projection_candidate(obligation, idx)?;
ImplSource::Param(obligations, constness) ImplSource::Param(constness, obligations)
} }
ObjectCandidate(idx) => { ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?,
let data = self.confirm_object_candidate(obligation, idx)?;
ImplSource::Object(data)
}
ClosureCandidate { .. } => { ClosureCandidate { .. } => {
let vtable_closure = self.confirm_closure_candidate(obligation)?; let vtable_closure = self.confirm_closure_candidate(obligation)?;
ImplSource::Builtin(vtable_closure) ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure)
} }
GeneratorCandidate => { GeneratorCandidate => {
let vtable_generator = self.confirm_generator_candidate(obligation)?; let vtable_generator = self.confirm_generator_candidate(obligation)?;
ImplSource::Builtin(vtable_generator) ImplSource::Builtin(BuiltinImplSource::Misc, vtable_generator)
} }
FutureCandidate => { FutureCandidate => {
let vtable_future = self.confirm_future_candidate(obligation)?; let vtable_future = self.confirm_future_candidate(obligation)?;
ImplSource::Builtin(vtable_future) ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future)
} }
FnPointerCandidate { is_const } => { FnPointerCandidate { is_const } => {
let data = self.confirm_fn_pointer_candidate(obligation, is_const)?; let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
ImplSource::Builtin(data) ImplSource::Builtin(BuiltinImplSource::Misc, data)
} }
TraitAliasCandidate => { TraitAliasCandidate => {
let data = self.confirm_trait_alias_candidate(obligation); let data = self.confirm_trait_alias_candidate(obligation);
ImplSource::Builtin(data) ImplSource::Builtin(BuiltinImplSource::Misc, data)
} }
BuiltinObjectCandidate => { BuiltinObjectCandidate => {
// This indicates something like `Trait + Send: Send`. In this case, we know that // This indicates something like `Trait + Send: Send`. In this case, we know that
// this holds because that's what the object type is telling us, and there's really // this holds because that's what the object type is telling us, and there's really
// no additional obligations to prove and no types in particular to unify, etc. // no additional obligations to prove and no types in particular to unify, etc.
ImplSource::Builtin(Vec::new()) ImplSource::Builtin(BuiltinImplSource::Misc, Vec::new())
} }
BuiltinUnsizeCandidate => { BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?,
let data = self.confirm_builtin_unsize_candidate(obligation)?;
ImplSource::Builtin(data)
}
TraitUpcastingUnsizeCandidate(idx) => { TraitUpcastingUnsizeCandidate(idx) => {
let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?
ImplSource::TraitUpcasting(data)
} }
ConstDestructCandidate(def_id) => { ConstDestructCandidate(def_id) => {
let data = self.confirm_const_destruct_candidate(obligation, def_id)?; let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
ImplSource::Builtin(data) ImplSource::Builtin(BuiltinImplSource::Misc, data)
} }
}; };
@ -484,7 +477,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self, &mut self,
obligation: &PolyTraitObligation<'tcx>, obligation: &PolyTraitObligation<'tcx>,
index: usize, index: usize,
) -> Result<ImplSourceObjectData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx(); let tcx = self.tcx();
debug!(?obligation, ?index, "confirm_object_candidate"); debug!(?obligation, ?index, "confirm_object_candidate");
@ -648,7 +641,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
(unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)), (unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)),
); );
Ok(ImplSourceObjectData { vtable_base, nested }) Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested))
} }
fn confirm_fn_pointer_candidate( fn confirm_fn_pointer_candidate(
@ -897,7 +890,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self, &mut self,
obligation: &PolyTraitObligation<'tcx>, obligation: &PolyTraitObligation<'tcx>,
idx: usize, idx: usize,
) -> Result<ImplSourceTraitUpcastingData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx(); let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound // `assemble_candidates_for_unsizing` should ensure there are no late-bound
@ -994,13 +987,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let vtable_vptr_slot = let vtable_vptr_slot =
prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap(); prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap();
Ok(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested))
} }
fn confirm_builtin_unsize_candidate( fn confirm_builtin_unsize_candidate(
&mut self, &mut self,
obligation: &PolyTraitObligation<'tcx>, obligation: &PolyTraitObligation<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx(); let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound // `assemble_candidates_for_unsizing` should ensure there are no late-bound
@ -1008,10 +1001,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap());
let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
let target = self.infcx.shallow_resolve(target); let target = self.infcx.shallow_resolve(target);
debug!(?source, ?target, "confirm_builtin_unsize_candidate"); debug!(?source, ?target, "confirm_builtin_unsize_candidate");
let mut nested = vec![]; let mut nested = vec![];
let src;
match (source.kind(), target.kind()) { match (source.kind(), target.kind()) {
// Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping).
(&ty::Dynamic(ref data_a, r_a, dyn_a), &ty::Dynamic(ref data_b, r_b, dyn_b)) (&ty::Dynamic(ref data_a, r_a, dyn_a), &ty::Dynamic(ref data_b, r_b, dyn_b))
@ -1055,6 +1048,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.param_env, obligation.param_env,
obligation.predicate.rebind(outlives), obligation.predicate.rebind(outlives),
)); ));
src = BuiltinImplSource::Misc;
} }
// `T` -> `Trait` // `T` -> `Trait`
@ -1101,6 +1096,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
nested.push(predicate_to_obligation( nested.push(predicate_to_obligation(
ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx), ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx),
)); ));
src = BuiltinImplSource::Misc;
} }
// `[T; n]` -> `[T]` // `[T; n]` -> `[T]`
@ -1111,6 +1108,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.eq(DefineOpaqueTypes::No, b, a) .eq(DefineOpaqueTypes::No, b, a)
.map_err(|_| Unimplemented)?; .map_err(|_| Unimplemented)?;
nested.extend(obligations); nested.extend(obligations);
src = BuiltinImplSource::Misc;
} }
// `Struct<T>` -> `Struct<U>` // `Struct<T>` -> `Struct<U>`
@ -1167,6 +1166,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
), ),
); );
nested.push(tail_unsize_obligation); nested.push(tail_unsize_obligation);
src = BuiltinImplSource::Misc;
} }
// `(.., T)` -> `(.., U)` // `(.., T)` -> `(.., U)`
@ -1194,12 +1195,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]), ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]),
); );
nested.push(last_unsize_obligation); nested.push(last_unsize_obligation);
src = BuiltinImplSource::TupleUnsizing;
} }
_ => bug!("source: {source}, target: {target}"), _ => bug!("source: {source}, target: {target}"),
}; };
Ok(nested) Ok(ImplSource::Builtin(src, nested))
} }
fn confirm_const_destruct_candidate( fn confirm_const_destruct_candidate(

View file

@ -241,9 +241,9 @@ pub fn upcast_choices<'tcx>(
/// Given an upcast trait object described by `object`, returns the /// Given an upcast trait object described by `object`, returns the
/// index of the method `method_def_id` (which should be part of /// index of the method `method_def_id` (which should be part of
/// `object.upcast_trait_ref`) within the vtable for `object`. /// `object.upcast_trait_ref`) within the vtable for `object`.
pub fn get_vtable_index_of_object_method<'tcx, N>( pub fn get_vtable_index_of_object_method<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
object: &super::ImplSourceObjectData<N>, vtable_base: usize,
method_def_id: DefId, method_def_id: DefId,
) -> Option<usize> { ) -> Option<usize> {
// Count number of methods preceding the one we are selecting and // Count number of methods preceding the one we are selecting and
@ -252,7 +252,7 @@ pub fn get_vtable_index_of_object_method<'tcx, N>(
.iter() .iter()
.copied() .copied()
.position(|def_id| def_id == method_def_id) .position(|def_id| def_id == method_def_id)
.map(|index| object.vtable_base + index) .map(|index| vtable_base + index)
} }
pub fn closure_trait_ref_and_return_type<'tcx>( pub fn closure_trait_ref_and_return_type<'tcx>(

View file

@ -5,6 +5,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_infer::traits::util::PredicateSet; use rustc_infer::traits::util::PredicateSet;
use rustc_infer::traits::ImplSource; use rustc_infer::traits::ImplSource;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::GenericArgs; use rustc_middle::ty::GenericArgs;
use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry};
@ -384,8 +385,8 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]); let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]);
match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) { match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => { Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => {
implsrc_traitcasting.vtable_vptr_slot *vtable_vptr_slot
} }
otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"), otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"),
} }

View file

@ -2,7 +2,7 @@ use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
use rustc_middle::traits::CodegenObligationError; use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError};
use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt};
use rustc_span::sym; use rustc_span::sym;
@ -177,12 +177,15 @@ fn resolve_associated_item<'tcx>(
Some(ty::Instance::new(leaf_def.item.def_id, args)) Some(ty::Instance::new(leaf_def.item.def_id, args))
} }
traits::ImplSource::Object(ref data) => { traits::ImplSource::Builtin(BuiltinImplSource::Object { vtable_base }, _) => {
traits::get_vtable_index_of_object_method(tcx, data, trait_item_id).map(|index| { traits::get_vtable_index_of_object_method(tcx, *vtable_base, trait_item_id).map(
Instance { def: ty::InstanceDef::Virtual(trait_item_id, index), args: rcvr_args } |index| Instance {
}) def: ty::InstanceDef::Virtual(trait_item_id, index),
args: rcvr_args,
},
)
} }
traits::ImplSource::Builtin(..) => { traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
let lang_items = tcx.lang_items(); let lang_items = tcx.lang_items();
if Some(trait_ref.def_id) == lang_items.clone_trait() { if Some(trait_ref.def_id) == lang_items.clone_trait() {
// FIXME(eddyb) use lang items for methods instead of names. // FIXME(eddyb) use lang items for methods instead of names.
@ -290,7 +293,9 @@ fn resolve_associated_item<'tcx>(
None None
} }
} }
traits::ImplSource::Param(..) | traits::ImplSource::TraitUpcasting(_) => None, traits::ImplSource::Param(..)
| traits::ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
| traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => None,
}) })
} }

View file

@ -14,7 +14,7 @@ use rustc_middle::mir::{
Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind,
Terminator, TerminatorKind, Terminator, TerminatorKind,
}; };
use rustc_middle::traits::{ImplSource, ObligationCause}; use rustc_middle::traits::{ImplSource, ObligationCause, BuiltinImplSource};
use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::{self, BoundConstness, GenericArgKind, TraitRef, Ty, TyCtxt}; use rustc_middle::ty::{self, BoundConstness, GenericArgKind, TraitRef, Ty, TyCtxt};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
@ -411,7 +411,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>
if !matches!( if !matches!(
impl_src, impl_src,
ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(ty::BoundConstness::ConstIfConst, _)
) { ) {
return false; return false;
} }

View file

@ -0,0 +1,23 @@
// compile-flags: -Ztrait-solver=next
// check-pass
#![feature(trait_upcasting)]
trait A {}
trait B: A {}
impl A for usize {}
impl B for usize {}
trait Mirror {
type Assoc: ?Sized;
}
impl<T: ?Sized> Mirror for T {
type Assoc = T;
}
fn main() {
let x = Box::new(1usize) as Box<<dyn B as Mirror>::Assoc>;
let y = x as Box<<dyn A as Mirror>::Assoc>;
}

View file

@ -1,6 +1,8 @@
// check-pass // check-pass
// compile-flags: -Ztrait-solver=next // compile-flags: -Ztrait-solver=next
#![feature(trait_upcasting)]
pub trait A {} pub trait A {}
pub trait B: A {} pub trait B: A {}