Make everything builtin!

This commit is contained in:
Michael Goulet 2023-07-24 22:02:52 +00:00
parent de81007d13
commit a7ed9c1da7
23 changed files with 345 additions and 444 deletions

View File

@ -8,6 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
use rustc_middle::ty::{GenericArgKind, GenericArgs};
use rustc_middle::ty::{TraitRef, TypeVisitableExt};
@ -766,7 +767,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
};
match implsrc {
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
Ok(Some(ImplSource::Param(ty::BoundConstness::ConstIfConst, _))) => {
debug!(
"const_trait_impl: provided {:?} via where-clause in {:?}",
trait_ref, param_env
@ -774,7 +775,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
return;
}
// Closure: Fn{Once|Mut}
Ok(Some(ImplSource::Builtin(_)))
Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _)))
if trait_ref.self_ty().is_closure()
&& 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_middle::mir;
use rustc_middle::mir::*;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
use rustc_trait_selection::traits::{
self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
@ -172,7 +173,8 @@ impl Qualif for NeedsNonConstDrop {
if !matches!(
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,
// 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::traits::{Obligation, PredicateObligation};
use rustc_middle::lint::in_external_macro;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
};
@ -687,12 +688,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
}
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::TraitUpcasting(..) => {
traits::ImplSource::Builtin(
BuiltinImplSource::TraitUpcasting { .. },
_,
) => {
has_trait_upcasting_coercion =
Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1)));
}
traits::ImplSource::TupleUnsizing(_) => {
traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
has_unsized_tuple_coercion = true;
}
_ => {}

View File

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

View File

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

View File

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

View File

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

View File

@ -649,46 +649,31 @@ pub enum ImplSource<'tcx, N> {
/// for some type parameter. The `Vec<N>` represents the
/// obligations incurred from normalizing the where-clause (if
/// any).
Param(Vec<N>, ty::BoundConstness),
Param(ty::BoundConstness, Vec<N>),
/// Virtual calls through an object.
Object(ImplSourceObjectData<N>),
/// Successful resolution for a builtin trait.
Builtin(Vec<N>),
// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`
TupleUnsizing(Vec<N>),
/// ImplSource for trait upcasting coercion
TraitUpcasting(ImplSourceTraitUpcastingData<N>),
/// Successful resolution for a builtin impl.
Builtin(BuiltinImplSource, Vec<N>),
}
impl<'tcx, N> ImplSource<'tcx, N> {
pub fn nested_obligations(self) -> Vec<N> {
match self {
ImplSource::UserDefined(i) => i.nested,
ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n,
ImplSource::Object(d) => d.nested,
ImplSource::TraitUpcasting(d) => d.nested,
ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n,
}
}
pub fn borrow_nested_obligations(&self) -> &[N] {
match self {
ImplSource::UserDefined(i) => &i.nested,
ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => &n,
ImplSource::Object(d) => &d.nested,
ImplSource::TraitUpcasting(d) => &d.nested,
ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => &n,
}
}
pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] {
match self {
ImplSource::UserDefined(i) => &mut i.nested,
ImplSource::Param(n, _) | ImplSource::Builtin(n) | ImplSource::TupleUnsizing(n) => n,
ImplSource::Object(d) => &mut d.nested,
ImplSource::TraitUpcasting(d) => &mut d.nested,
ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n,
}
}
@ -702,20 +687,9 @@ impl<'tcx, N> ImplSource<'tcx, N> {
args: i.args,
nested: i.nested.into_iter().map(f).collect(),
}),
ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct),
ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()),
ImplSource::TupleUnsizing(n) => {
ImplSource::TupleUnsizing(n.into_iter().map(f).collect())
}
ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData {
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(),
})
ImplSource::Param(ct, n) => ImplSource::Param(ct, n.into_iter().map(f).collect()),
ImplSource::Builtin(source, n) => {
ImplSource::Builtin(source, n.into_iter().map(f).collect())
}
}
}
@ -739,30 +713,32 @@ pub struct ImplSourceUserDefinedData<'tcx, N> {
pub nested: Vec<N>,
}
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct ImplSourceTraitUpcastingData<N> {
/// 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<usize>,
pub nested: Vec<N>,
}
#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, Lift)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct ImplSourceObjectData<N> {
#[derive(Copy, Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum BuiltinImplSource {
/// Some builtin impl we don't need to differentiate. This should be used
/// unless more specific information is necessary.
Misc,
/// A builtin impl for trait objects.
///
/// 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 start of `upcast_trait_ref`'s methods
/// in that vtable.
pub vtable_base: usize,
pub nested: Vec<N>,
Object { vtable_base: usize },
/// 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.
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)]
pub enum ObjectSafetyViolation {
/// `Self: Sized` declared on the trait.

View File

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

View File

@ -6,20 +6,16 @@ use std::fmt;
impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
super::ImplSource::UserDefined(ref v) => write!(f, "{:?}", v),
match self {
super::ImplSource::UserDefined(v) => write!(f, "{:?}", v),
super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d),
super::ImplSource::Object(ref d) => write!(f, "{:?}", d),
super::ImplSource::Param(ref n, ct) => {
write!(f, "ImplSourceParamData({:?}, {:?})", n, ct)
super::ImplSource::Builtin(source, d) => {
write!(f, "Builtin({source:?}, {d:?})")
}
super::ImplSource::TupleUnsizing(ref d) => write!(f, "{:?}", d),
super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
super::ImplSource::Param(ct, n) => {
write!(f, "ImplSourceParamData({:?}, {:?})", n, ct)
}
}
}
}
@ -33,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! {
NotConstEvaluatable,
}
TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable }
pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>;

View File

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

View File

@ -3,17 +3,16 @@
use super::search_graph::OverflowHandler;
use super::{EvalCtxt, SolverMode};
use crate::traits::coherence;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def_id::DefId;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::elaborate;
use rustc_infer::traits::Reveal;
use rustc_middle::traits::solve::inspect::CandidateKind;
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::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{fast_reject, TypeFoldable};
use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use std::fmt::Debug;
@ -89,17 +88,6 @@ pub(super) enum CandidateSource {
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,
TupleUnsize,
Object,
Misc,
Ambiguity,
}
/// Methods used to assemble candidates for either trait or projection goals.
pub(super) trait GoalKind<'tcx>:
TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
@ -282,20 +270,6 @@ pub(super) trait GoalKind<'tcx>:
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
/// Consider (possibly several) goals 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`.
fn consider_builtin_unsize_and_upcast_candidates(
_ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
fn consider_builtin_discriminant_kind_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
@ -310,6 +284,25 @@ pub(super) trait GoalKind<'tcx>:
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> 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> {
@ -343,7 +336,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
) -> Option<Vec<Candidate<'tcx>>> {
goal.predicate.self_ty().is_ty_var().then(|| {
vec![Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result: self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
.unwrap(),
@ -412,7 +405,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Certainty::Maybe(MaybeCause::Overflow),
)?;
Ok(vec![Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result,
}])
},
@ -848,29 +841,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
ty::Dynamic(bounds, ..) => bounds,
};
let own_bounds: FxIndexSet<_> =
bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect();
for assumption in elaborate(tcx, own_bounds.iter().copied())
// we only care about bounds that match the `Self` type
.filter_only_self()
{
// FIXME: Predicates are fully elaborated in the object type's existential bounds
// list. We want to only consider these pre-elaborated projections, and not other
// projection predicates that we reach by elaborating the principal trait ref,
// since that'll cause ambiguity.
//
// We can remove this when we have implemented lifetime intersections in responses.
if assumption.as_projection_clause().is_some() && !own_bounds.contains(&assumption) {
continue;
// Consider all of the auto-trait and projection bounds, which don't
// need to be recorded as a `BuiltinImplSource::Object` since they don't
// really have a vtable base...
for bound in bounds {
match bound.skip_binder() {
ty::ExistentialPredicate::Trait(_) => {
// Skip principal
}
ty::ExistentialPredicate::Projection(_)
| ty::ExistentialPredicate::AutoTrait(_) => {
match G::consider_object_bound_candidate(
self,
goal,
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) {
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
result,
}),
Err(NoSolution) => (),
}
// FIXME: We only need to do *any* of this if we're considering a trait goal,
// since we don't need to look at any supertrait or anything if we are doing
// a projection goal.
if let Some(principal) = bounds.principal() {
let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
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) => (),
}
});
}
}
@ -890,7 +901,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
{
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result,
}),
// 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 crate::traits::specialization_graph;
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
use super::inspect::ProofTreeBuilder;
use super::search_graph::{self, OverflowHandler};
@ -920,4 +921,39 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
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::def_id::DefId;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
use rustc_infer::traits::util::supertraits;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt};
use rustc_infer::traits::{
Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine,
};
use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
use rustc_middle::traits::{
ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
ObligationCause, SelectionError,
BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError,
};
use rustc_middle::ty::{self, Ty, TyCtxt};
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::inspect::ProofTreeBuilder;
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::TraitEngineExt;
@ -105,47 +100,26 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
rematch_impl(self, goal, def_id, nested_obligations)
}
// Rematching the dyn upcast or object goal will instantiate the same nested
// 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 | BuiltinImplSource::TupleUnsize,
),
) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => {
rematch_unsize(self, goal, nested_obligations)
}
(Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::TupleUnsize))
(Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
{
Ok(Some(ImplSource::TupleUnsizing(nested_obligations)))
rematch_unsize(self, goal, nested_obligations, src)
}
// Technically some builtin impls have nested obligations, but if
// `Certainty::Yes`, then they should've all been verified and don't
// need re-checking.
(Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
Ok(Some(ImplSource::Builtin(nested_obligations)))
(Certainty::Yes, CandidateSource::BuiltinImpl(src)) => {
Ok(Some(ImplSource::Builtin(src, nested_obligations)))
}
// It's fine not to do anything to rematch these, since there are no
// nested obligations.
(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),
}
}
}
@ -192,11 +166,12 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>(
}
(_, 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,
(_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true,
(_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true,
(CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
tcx.specializes((other_def_id, victim_def_id))
@ -234,108 +209,6 @@ fn rematch_impl<'tcx>(
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 {
// If source and target trait def ids are identical,
// then we are simply removing auto traits.
if source_trait_ref.def_id() == target_trait_ref.def_id() {
ImplSource::Builtin(nested)
} else {
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.
/// 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.
@ -343,6 +216,7 @@ fn rematch_unsize<'tcx>(
infcx: &InferCtxt<'tcx>,
goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
mut nested: Vec<PredicateObligation<'tcx>>,
source: BuiltinImplSource,
) -> SelectionResult<'tcx, Selection<'tcx>> {
let tcx = infcx.tcx;
let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
@ -379,6 +253,8 @@ fn rematch_unsize<'tcx>(
goal.param_env,
ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)),
));
Ok(Some(ImplSource::Builtin(source, nested)))
}
// `[T; n]` -> `[T]` unsizing
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
@ -389,6 +265,8 @@ fn rematch_unsize<'tcx>(
.expect("expected rematch to succeed")
.into_obligations(),
);
Ok(Some(ImplSource::Builtin(source, nested)))
}
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
(&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
@ -439,6 +317,8 @@ fn rematch_unsize<'tcx>(
goal.param_env,
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>`
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
@ -467,15 +347,18 @@ fn rematch_unsize<'tcx>(
));
// We need to be able to detect tuple unsizing to require its feature gate.
return Ok(Some(ImplSource::TupleUnsizing(nested)));
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:
// 1. the certainty is `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>(

View File

@ -1,6 +1,6 @@
use crate::traits::specialization_graph;
use super::assembly::{self, structural_traits, BuiltinImplSource};
use super::assembly::{self, structural_traits};
use super::EvalCtxt;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
@ -10,6 +10,7 @@ use rustc_infer::traits::specialization_graph::LeafDef;
use rustc_infer::traits::Reveal;
use rustc_middle::traits::solve::inspect::CandidateKind;
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::ProjectionPredicate;
use rustc_middle::ty::{self, Ty, TyCtxt};

View File

@ -1,15 +1,14 @@
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
use super::assembly::{self, structural_traits, BuiltinImplSource};
use super::assembly::{self, structural_traits};
use super::search_graph::OverflowHandler;
use super::{EvalCtxt, SolverMode};
use rustc_hir::def_id::DefId;
use rustc_hir::{LangItem, Movability};
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::Reveal;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
use rustc_middle::traits::{BuiltinImplSource, Reveal};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
@ -382,37 +381,48 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
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 !b_ty.is_ty_var() => b_ty,
Ok(_) => {
return vec![(
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
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::Ambiguity,
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 mut results = vec![];
results.extend(ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty));
results.extend(
ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty)
ecx.consider_builtin_unsize_candidate(goal.with(ecx.tcx(), (a_ty, b_ty)))
.into_iter()
.map(|resp| (resp, BuiltinImplSource::TraitUpcasting)),
);
results.extend(
ecx.consider_builtin_unsize_candidate(goal.param_env, a_ty, b_ty).into_iter().map(
|resp| {
.map(|resp| {
// If we're unsizing from tuple -> tuple, detect
let source =
if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..)))
{
BuiltinImplSource::TupleUnsize
BuiltinImplSource::TupleUnsizing
} else {
BuiltinImplSource::Misc
};
(resp, source)
},
),
}),
);
results
@ -484,10 +494,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
impl<'tcx> EvalCtxt<'_, 'tcx> {
fn consider_builtin_unsize_candidate(
&mut self,
param_env: ty::ParamEnv<'tcx>,
a_ty: Ty<'tcx>,
b_ty: Ty<'tcx>,
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()) {
@ -614,7 +623,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
a_ty: Ty<'tcx>,
b_ty: Ty<'tcx>,
) -> Vec<CanonicalResponse<'tcx>> {
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
if a_ty.is_ty_var() || b_ty.is_ty_var() {
bug!("unexpected type variable in unsize goal")
}
@ -634,57 +643,64 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
return vec![];
}
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
self.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);
// 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)
})
};
// 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(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);
}
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

View File

@ -4,7 +4,6 @@ use super::check_args_compatible;
use super::specialization_graph;
use super::translate_args;
use super::util;
use super::ImplSourceUserDefinedData;
use super::MismatchedProjectionTypes;
use super::Obligation;
use super::ObligationCause;
@ -13,6 +12,9 @@ use super::Selection;
use super::SelectionContext;
use super::SelectionError;
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::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@ -1717,7 +1719,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
};
let eligible = match &impl_source {
super::ImplSource::UserDefined(impl_data) => {
ImplSource::UserDefined(impl_data) => {
// We have to be careful when projecting out of an
// impl because of specialization. If we are not in
// 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
// be known. Any type with multiple potential associated types is therefore
// not eligible.
@ -1891,7 +1893,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
bug!("unexpected builtin trait with associated type: {trait_ref:?}")
}
}
super::ImplSource::Param(..) => {
ImplSource::Param(..) => {
// This case tell us nothing about the value of an
// associated type. Consider:
//
@ -1919,14 +1921,14 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// in `assemble_candidates_from_param_env`.
false
}
super::ImplSource::Object(_) => {
ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) => {
// Handled by the `Object` projection candidate. See
// `assemble_candidates_from_object_ty` for an explanation of
// why we special case object types.
false
}
super::ImplSource::TraitUpcasting(_)
| super::ImplSource::TupleUnsizing(_) => {
ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
| ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
// These traits have no associated types.
selcx.tcx().sess.delay_span_bug(
obligation.cause.span,
@ -1986,8 +1988,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
impl_source: Selection<'tcx>,
) -> Progress<'tcx> {
match impl_source {
super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
super::ImplSource::Builtin(data) => {
ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
ImplSource::Builtin(BuiltinImplSource::Misc, data) => {
let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx());
let lang_items = selcx.tcx().lang_items();
if lang_items.gen_trait() == Some(trait_def_id) {
@ -2004,10 +2006,10 @@ fn confirm_select_candidate<'cx, 'tcx>(
confirm_builtin_candidate(selcx, obligation, data)
}
}
super::ImplSource::Object(_)
| super::ImplSource::Param(..)
| super::ImplSource::TraitUpcasting(_)
| super::ImplSource::TupleUnsizing(_) => {
ImplSource::Builtin(BuiltinImplSource::Object { .. }, _)
| ImplSource::Param(..)
| ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
| ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
// we don't create Select candidates with this kind of resolution
span_bug!(
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_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch};
use rustc_middle::ty::{
self, Binder, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt,
@ -26,9 +26,9 @@ use crate::traits::vtable::{
};
use crate::traits::{
BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized,
Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation,
PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented,
ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause,
OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection,
SelectionError, TraitNotObjectSafe, Unimplemented,
};
use super::BuiltinImplConditions;
@ -48,18 +48,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let mut impl_src = match candidate {
BuiltinCandidate { has_nested } => {
let data = self.confirm_builtin_candidate(obligation, has_nested);
ImplSource::Builtin(data)
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
TransmutabilityCandidate => {
let data = self.confirm_transmutability_candidate(obligation)?;
ImplSource::Builtin(data)
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
ParamCandidate(param) => {
let obligations =
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) => {
@ -68,76 +68,57 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
AutoImplCandidate => {
let data = self.confirm_auto_impl_candidate(obligation)?;
ImplSource::Builtin(data)
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
ProjectionCandidate(idx, constness) => {
let obligations = self.confirm_projection_candidate(obligation, idx)?;
ImplSource::Param(obligations, constness)
ImplSource::Param(constness, obligations)
}
ObjectCandidate(idx) => {
let data = self.confirm_object_candidate(obligation, idx)?;
ImplSource::Object(data)
}
ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?,
ClosureCandidate { .. } => {
let vtable_closure = self.confirm_closure_candidate(obligation)?;
ImplSource::Builtin(vtable_closure)
ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure)
}
GeneratorCandidate => {
let vtable_generator = self.confirm_generator_candidate(obligation)?;
ImplSource::Builtin(vtable_generator)
ImplSource::Builtin(BuiltinImplSource::Misc, vtable_generator)
}
FutureCandidate => {
let vtable_future = self.confirm_future_candidate(obligation)?;
ImplSource::Builtin(vtable_future)
ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future)
}
FnPointerCandidate { is_const } => {
let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
ImplSource::Builtin(data)
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
TraitAliasCandidate => {
let data = self.confirm_trait_alias_candidate(obligation);
ImplSource::Builtin(data)
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
BuiltinObjectCandidate => {
// 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
// no additional obligations to prove and no types in particular to unify, etc.
ImplSource::Builtin(Vec::new())
ImplSource::Builtin(BuiltinImplSource::Misc, Vec::new())
}
BuiltinUnsizeCandidate => {
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 = self.infcx.shallow_resolve(target);
let data = self.confirm_builtin_unsize_candidate(obligation, source, target)?;
// If the source and target are both unsize goals, then we need to signify that
// this is tuple unsizing so that during unsized coercion we require the proper
// feature gate.
if matches!(source.kind(), ty::Tuple(..)) && matches!(target.kind(), ty::Tuple(..))
{
ImplSource::TupleUnsizing(data)
} else {
ImplSource::Builtin(data)
}
}
BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?,
TraitUpcastingUnsizeCandidate(idx) => {
let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?;
ImplSource::TraitUpcasting(data)
self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?
}
ConstDestructCandidate(def_id) => {
let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
ImplSource::Builtin(data)
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
};
@ -496,7 +477,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
obligation: &PolyTraitObligation<'tcx>,
index: usize,
) -> Result<ImplSourceObjectData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
debug!(?obligation, ?index, "confirm_object_candidate");
@ -660,7 +641,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
(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(
@ -909,7 +890,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
obligation: &PolyTraitObligation<'tcx>,
idx: usize,
) -> Result<ImplSourceTraitUpcastingData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
@ -1006,19 +987,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let vtable_vptr_slot =
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(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
source: Ty<'tcx>,
target: Ty<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, 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.args.type_at(1);
let target = self.infcx.shallow_resolve(target);
debug!(?source, ?target, "confirm_builtin_unsize_candidate");
let mut nested = vec![];
let src;
match (source.kind(), target.kind()) {
// 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))
@ -1062,6 +1048,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.param_env,
obligation.predicate.rebind(outlives),
));
src = BuiltinImplSource::Misc;
}
// `T` -> `Trait`
@ -1108,6 +1096,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
nested.push(predicate_to_obligation(
ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx),
));
src = BuiltinImplSource::Misc;
}
// `[T; n]` -> `[T]`
@ -1118,6 +1108,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.eq(DefineOpaqueTypes::No, b, a)
.map_err(|_| Unimplemented)?;
nested.extend(obligations);
src = BuiltinImplSource::Misc;
}
// `Struct<T>` -> `Struct<U>`
@ -1174,6 +1166,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
),
);
nested.push(tail_unsize_obligation);
src = BuiltinImplSource::Misc;
}
// `(.., T)` -> `(.., U)`
@ -1201,12 +1195,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]),
);
nested.push(last_unsize_obligation);
src = BuiltinImplSource::TupleUnsizing;
}
_ => bug!("source: {source}, target: {target}"),
};
Ok(nested)
Ok(ImplSource::Builtin(src, nested))
}
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
/// index of the method `method_def_id` (which should be part of
/// `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>,
object: &super::ImplSourceObjectData<N>,
vtable_base: usize,
method_def_id: DefId,
) -> Option<usize> {
// 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()
.copied()
.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>(

View File

@ -5,6 +5,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_infer::traits::util::PredicateSet;
use rustc_infer::traits::ImplSource;
use rustc_middle::query::Providers;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::GenericArgs;
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]);
match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => {
implsrc_traitcasting.vtable_vptr_slot
Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => {
*vtable_vptr_slot
}
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_infer::infer::TyCtxtInferExt;
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::{self, Instance, TyCtxt, TypeVisitableExt};
use rustc_span::sym;
@ -177,12 +177,15 @@ fn resolve_associated_item<'tcx>(
Some(ty::Instance::new(leaf_def.item.def_id, args))
}
traits::ImplSource::Object(ref data) => {
traits::get_vtable_index_of_object_method(tcx, data, trait_item_id).map(|index| {
Instance { def: ty::InstanceDef::Virtual(trait_item_id, index), args: rcvr_args }
})
traits::ImplSource::Builtin(BuiltinImplSource::Object { vtable_base }, _) => {
traits::get_vtable_index_of_object_method(tcx, *vtable_base, trait_item_id).map(
|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();
if Some(trait_ref.def_id) == lang_items.clone_trait() {
// FIXME(eddyb) use lang items for methods instead of names.
@ -291,8 +294,8 @@ fn resolve_associated_item<'tcx>(
}
}
traits::ImplSource::Param(..)
| traits::ImplSource::TraitUpcasting(_)
| traits::ImplSource::TupleUnsizing(_) => None,
| 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,
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::{self, BoundConstness, GenericArgKind, TraitRef, Ty, TyCtxt};
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!(
impl_src,
ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(ty::BoundConstness::ConstIfConst, _)
) {
return false;
}