//! Code for projecting associated types out of trait references. use std::ops::ControlFlow; use super::specialization_graph; use super::translate_args; use super::util; use super::MismatchedProjectionTypes; use super::Obligation; use super::ObligationCause; use super::PredicateObligation; 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; use crate::infer::{BoundRegionConversionTime, InferOk}; use crate::traits::normalize::normalize_with_depth; use crate::traits::normalize::normalize_with_depth_to; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::select::ProjectionMatchesProjection; use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; pub use rustc_middle::traits::Reveal; pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; pub type ProjectionObligation<'tcx> = Obligation<'tcx, ty::ProjectionPredicate<'tcx>>; pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::AliasTy<'tcx>>; pub(super) struct InProgress; /// When attempting to resolve `::Name` ... #[derive(Debug)] pub enum ProjectionError<'tcx> { /// ...we found multiple sources of information and couldn't resolve the ambiguity. TooManyCandidates, /// ...an error occurred matching `T : TraitRef` TraitSelectionError(SelectionError<'tcx>), } #[derive(PartialEq, Eq, Debug)] enum ProjectionCandidate<'tcx> { /// From a where-clause in the env or object type ParamEnv(ty::PolyProjectionPredicate<'tcx>), /// From the definition of `Trait` when you have something like /// `<::B as Trait2>::C`. TraitDef(ty::PolyProjectionPredicate<'tcx>), /// Bounds specified on an object type Object(ty::PolyProjectionPredicate<'tcx>), /// From an "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), } enum ProjectionCandidateSet<'tcx> { None, Single(ProjectionCandidate<'tcx>), Ambiguous, Error(SelectionError<'tcx>), } impl<'tcx> ProjectionCandidateSet<'tcx> { fn mark_ambiguous(&mut self) { *self = ProjectionCandidateSet::Ambiguous; } fn mark_error(&mut self, err: SelectionError<'tcx>) { *self = ProjectionCandidateSet::Error(err); } // Returns true if the push was successful, or false if the candidate // was discarded -- this could be because of ambiguity, or because // a higher-priority candidate is already there. fn push_candidate(&mut self, candidate: ProjectionCandidate<'tcx>) -> bool { use self::ProjectionCandidate::*; use self::ProjectionCandidateSet::*; // This wacky variable is just used to try and // make code readable and avoid confusing paths. // It is assigned a "value" of `()` only on those // paths in which we wish to convert `*self` to // ambiguous (and return false, because the candidate // was not used). On other paths, it is not assigned, // and hence if those paths *could* reach the code that // comes after the match, this fn would not compile. let convert_to_ambiguous; match self { None => { *self = Single(candidate); return true; } Single(current) => { // Duplicates can happen inside ParamEnv. In the case, we // perform a lazy deduplication. if current == &candidate { return false; } // Prefer where-clauses. As in select, if there are multiple // candidates, we prefer where-clause candidates over impls. This // may seem a bit surprising, since impls are the source of // "truth" in some sense, but in fact some of the impls that SEEM // applicable are not, because of nested obligations. Where // clauses are the safer choice. See the comment on // `select::SelectionCandidate` and #21974 for more details. match (current, candidate) { (ParamEnv(..), ParamEnv(..)) => convert_to_ambiguous = (), (ParamEnv(..), _) => return false, (_, ParamEnv(..)) => bug!( "should never prefer non-param-env candidates over param-env candidates" ), (_, _) => convert_to_ambiguous = (), } } Ambiguous | Error(..) => { return false; } } // We only ever get here when we moved from a single candidate // to ambiguous. let () = convert_to_ambiguous; *self = Ambiguous; false } } /// States returned from `poly_project_and_unify_type`. Takes the place /// of the old return type, which was: /// ```ignore (not-rust) /// Result< /// Result>>, InProgress>, /// MismatchedProjectionTypes<'tcx>, /// > /// ``` pub(super) enum ProjectAndUnifyResult<'tcx> { /// The projection bound holds subject to the given obligations. If the /// projection cannot be normalized because the required trait bound does /// not hold, this is returned, with `obligations` being a predicate that /// cannot be proven. Holds(Vec>), /// The projection cannot be normalized due to ambiguity. Resolving some /// inference variables in the projection may fix this. FailedNormalization, /// The project cannot be normalized because `poly_project_and_unify_type` /// is called recursively while normalizing the same projection. Recursive, // the projection can be normalized, but is not equal to the expected type. // Returns the type error that arose from the mismatch. MismatchedProjectionTypes(MismatchedProjectionTypes<'tcx>), } /// Evaluates constraints of the form: /// ```ignore (not-rust) /// for<...> ::U == V /// ``` /// If successful, this may result in additional obligations. Also returns /// the projection cache key used to track these additional obligations. #[instrument(level = "debug", skip(selcx))] pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &PolyProjectionObligation<'tcx>, ) -> ProjectAndUnifyResult<'tcx> { let infcx = selcx.infcx; let r = infcx.commit_if_ok(|_snapshot| { let old_universe = infcx.universe(); let placeholder_predicate = infcx.enter_forall_and_leak_universe(obligation.predicate); let new_universe = infcx.universe(); let placeholder_obligation = obligation.with(infcx.tcx, placeholder_predicate); match project_and_unify_type(selcx, &placeholder_obligation) { ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e), ProjectAndUnifyResult::Holds(obligations) if old_universe != new_universe && selcx.tcx().features().generic_associated_types_extended => { // If the `generic_associated_types_extended` feature is active, then we ignore any // obligations references lifetimes from any universe greater than or equal to the // universe just created. Otherwise, we can end up with something like `for<'a> I: 'a`, // which isn't quite what we want. Ideally, we want either an implied // `for<'a where I: 'a> I: 'a` or we want to "lazily" check these hold when we // instantiate concrete regions. There is design work to be done here; until then, // however, this allows experimenting potential GAT features without running into // well-formedness issues. let new_obligations = obligations .into_iter() .filter(|obligation| { let mut visitor = MaxUniverse::new(); obligation.predicate.visit_with(&mut visitor); visitor.max_universe() < new_universe }) .collect(); Ok(ProjectAndUnifyResult::Holds(new_obligations)) } other => Ok(other), } }); match r { Ok(inner) => inner, Err(err) => ProjectAndUnifyResult::MismatchedProjectionTypes(err), } } /// Evaluates constraints of the form: /// ```ignore (not-rust) /// ::U == V /// ``` /// If successful, this may result in additional obligations. /// /// See [poly_project_and_unify_type] for an explanation of the return value. #[instrument(level = "debug", skip(selcx))] fn project_and_unify_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionObligation<'tcx>, ) -> ProjectAndUnifyResult<'tcx> { let mut obligations = vec![]; let infcx = selcx.infcx; let normalized = match opt_normalize_projection_type( selcx, obligation.param_env, obligation.predicate.projection_ty, obligation.cause.clone(), obligation.recursion_depth, &mut obligations, ) { Ok(Some(n)) => n, Ok(None) => return ProjectAndUnifyResult::FailedNormalization, Err(InProgress) => return ProjectAndUnifyResult::Recursive, }; debug!(?normalized, ?obligations, "project_and_unify_type result"); let actual = obligation.predicate.term; // For an example where this is necessary see tests/ui/impl-trait/nested-return-type2.rs // This allows users to omit re-mentioning all bounds on an associated type and just use an // `impl Trait` for the assoc type to add more bounds. let InferOk { value: actual, obligations: new } = selcx.infcx.replace_opaque_types_with_inference_vars( actual, obligation.cause.body_id, obligation.cause.span, obligation.param_env, ); obligations.extend(new); // Need to define opaque types to support nested opaque types like `impl Fn() -> impl Trait` match infcx.at(&obligation.cause, obligation.param_env).eq( DefineOpaqueTypes::Yes, normalized, actual, ) { Ok(InferOk { obligations: inferred_obligations, value: () }) => { obligations.extend(inferred_obligations); ProjectAndUnifyResult::Holds(obligations) } Err(err) => { debug!("equating types encountered error {:?}", err); ProjectAndUnifyResult::MismatchedProjectionTypes(MismatchedProjectionTypes { err }) } } } /// The guts of `normalize`: normalize a specific projection like `::Item`. The result is always a type (and possibly /// additional obligations). If ambiguity arises, which implies that /// there are unresolved type variables in the projection, we will /// instantiate it with a fresh type variable `$X` and generate a new /// obligation `::Item == $X` for later. pub fn normalize_projection_type<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::AliasTy<'tcx>, cause: ObligationCause<'tcx>, depth: usize, obligations: &mut Vec>, ) -> Term<'tcx> { opt_normalize_projection_type( selcx, param_env, projection_ty, cause.clone(), depth, obligations, ) .ok() .flatten() .unwrap_or_else(move || { // if we bottom out in ambiguity, create a type variable // and a deferred predicate to resolve this when more type // information is available. selcx.infcx.infer_projection(param_env, projection_ty, cause, depth + 1, obligations).into() }) } /// The guts of `normalize`: normalize a specific projection like `::Item`. The result is always a type (and possibly /// additional obligations). Returns `None` in the case of ambiguity, /// which indicates that there are unbound type variables. /// /// This function used to return `Option>`, which contains a /// `Ty<'tcx>` and an obligations vector. But that obligation vector was very /// often immediately appended to another obligations vector. So now this /// function takes an obligations vector and appends to it directly, which is /// slightly uglier but avoids the need for an extra short-lived allocation. #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::AliasTy<'tcx>, cause: ObligationCause<'tcx>, depth: usize, obligations: &mut Vec>, ) -> Result>, InProgress> { let infcx = selcx.infcx; debug_assert!(!selcx.infcx.next_trait_solver()); // Don't use the projection cache in intercrate mode - // the `infcx` may be re-used between intercrate in non-intercrate // mode, which could lead to using incorrect cache results. let use_cache = !selcx.is_intercrate(); let projection_ty = infcx.resolve_vars_if_possible(projection_ty); let cache_key = ProjectionCacheKey::new(projection_ty); // FIXME(#20304) For now, I am caching here, which is good, but it // means we don't capture the type variables that are created in // the case of ambiguity. Which means we may create a large stream // of such variables. OTOH, if we move the caching up a level, we // would not benefit from caching when proving `T: Trait` // bounds. It might be the case that we want two distinct caches, // or else another kind of cache entry. let cache_result = if use_cache { infcx.inner.borrow_mut().projection_cache().try_start(cache_key) } else { Ok(()) }; match cache_result { Ok(()) => debug!("no cache"), Err(ProjectionCacheEntry::Ambiguous) => { // If we found ambiguity the last time, that means we will continue // to do so until some type in the key changes (and we know it // hasn't, because we just fully resolved it). debug!("found cache entry: ambiguous"); return Ok(None); } Err(ProjectionCacheEntry::InProgress) => { // Under lazy normalization, this can arise when // bootstrapping. That is, imagine an environment with a // where-clause like `A::B == u32`. Now, if we are asked // to normalize `A::B`, we will want to check the // where-clauses in scope. So we will try to unify `A::B` // with `A::B`, which can trigger a recursive // normalization. debug!("found cache entry: in-progress"); // Cache that normalizing this projection resulted in a cycle. This // should ensure that, unless this happens within a snapshot that's // rolled back, fulfillment or evaluation will notice the cycle. if use_cache { infcx.inner.borrow_mut().projection_cache().recur(cache_key); } return Err(InProgress); } Err(ProjectionCacheEntry::Recur) => { debug!("recur cache"); return Err(InProgress); } Err(ProjectionCacheEntry::NormalizedTy { ty, complete: _ }) => { // This is the hottest path in this function. // // If we find the value in the cache, then return it along // with the obligations that went along with it. Note // that, when using a fulfillment context, these // obligations could in principle be ignored: they have // already been registered when the cache entry was // created (and hence the new ones will quickly be // discarded as duplicated). But when doing trait // evaluation this is not the case, and dropping the trait // evaluations can causes ICEs (e.g., #43132). debug!(?ty, "found normalized ty"); obligations.extend(ty.obligations); return Ok(Some(ty.value)); } Err(ProjectionCacheEntry::Error) => { debug!("opt_normalize_projection_type: found error"); let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); obligations.extend(result.obligations); return Ok(Some(result.value.into())); } } let obligation = Obligation::with_depth(selcx.tcx(), cause.clone(), depth, param_env, projection_ty); match project(selcx, &obligation) { Ok(Projected::Progress(Progress { term: projected_term, obligations: mut projected_obligations, })) => { // if projection succeeded, then what we get out of this // is also non-normalized (consider: it was derived from // an impl, where-clause etc) and hence we must // re-normalize it let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); let mut result = if projected_term.has_aliases() { let normalized_ty = normalize_with_depth_to( selcx, param_env, cause, depth + 1, projected_term, &mut projected_obligations, ); Normalized { value: normalized_ty, obligations: projected_obligations } } else { Normalized { value: projected_term, obligations: projected_obligations } }; let mut deduped = SsoHashSet::with_capacity(result.obligations.len()); result.obligations.retain(|obligation| deduped.insert(obligation.clone())); if use_cache { infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); } obligations.extend(result.obligations); Ok(Some(result.value)) } Ok(Projected::NoProgress(projected_ty)) => { let result = Normalized { value: projected_ty, obligations: vec![] }; if use_cache { infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); } // No need to extend `obligations`. Ok(Some(result.value)) } Err(ProjectionError::TooManyCandidates) => { debug!("opt_normalize_projection_type: too many candidates"); if use_cache { infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key); } Ok(None) } Err(ProjectionError::TraitSelectionError(_)) => { debug!("opt_normalize_projection_type: ERROR"); // if we got an error processing the `T as Trait` part, // just return `ty::err` but add the obligation `T : // Trait`, which when processed will cause the error to be // reported later if use_cache { infcx.inner.borrow_mut().projection_cache().error(cache_key); } let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); obligations.extend(result.obligations); Ok(Some(result.value.into())) } } } /// If we are projecting `::Item`, but `T: Trait` does not /// hold. In various error cases, we cannot generate a valid /// normalized projection. Therefore, we create an inference variable /// return an associated obligation that, when fulfilled, will lead to /// an error. /// /// Note that we used to return `Error` here, but that was quite /// dubious -- the premise was that an error would *eventually* be /// reported, when the obligation was processed. But in general once /// you see an `Error` you are supposed to be able to assume that an /// error *has been* reported, so that you can take whatever heuristic /// paths you want to take. To make things worse, it was possible for /// cycles to arise, where you basically had a setup like ` /// as Trait>::Foo == $0`. Here, normalizing ` as /// Trait>::Foo>` to `[type error]` would lead to an obligation of /// ` as Trait>::Foo`. We are supposed to report /// an error for this obligation, but we legitimately should not, /// because it contains `[type error]`. Yuck! (See issue #29857 for /// one case where this arose.) fn normalize_to_error<'a, 'tcx>( selcx: &SelectionContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::AliasTy<'tcx>, cause: ObligationCause<'tcx>, depth: usize, ) -> NormalizedTy<'tcx> { let trait_ref = ty::Binder::dummy(projection_ty.trait_ref(selcx.tcx())); let trait_obligation = Obligation { cause, recursion_depth: depth, param_env, predicate: trait_ref.to_predicate(selcx.tcx()), }; let tcx = selcx.infcx.tcx; let new_value = selcx.infcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span: tcx.def_span(projection_ty.def_id), }); Normalized { value: new_value, obligations: vec![trait_obligation] } } /// Confirm and normalize the given inherent projection. #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] pub fn normalize_inherent_projection<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, alias_ty: ty::AliasTy<'tcx>, cause: ObligationCause<'tcx>, depth: usize, obligations: &mut Vec>, ) -> Ty<'tcx> { let tcx = selcx.tcx(); if !tcx.recursion_limit().value_within_limit(depth) { // Halt compilation because it is important that overflows never be masked. tcx.dcx().emit_fatal(InherentProjectionNormalizationOverflow { span: cause.span, ty: alias_ty.to_string(), }); } let args = compute_inherent_assoc_ty_args( selcx, param_env, alias_ty, cause.clone(), depth, obligations, ); // Register the obligations arising from the impl and from the associated type itself. let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, args); for (predicate, span) in predicates { let predicate = normalize_with_depth_to( selcx, param_env, cause.clone(), depth + 1, predicate, obligations, ); let nested_cause = ObligationCause::new( cause.span, cause.body_id, // FIXME(inherent_associated_types): Since we can't pass along the self type to the // cause code, inherent projections will be printed with identity instantiation in // diagnostics which is not ideal. // Consider creating separate cause codes for this specific situation. if span.is_dummy() { super::ItemObligation(alias_ty.def_id) } else { super::BindingObligation(alias_ty.def_id, span) }, ); obligations.push(Obligation::with_depth( tcx, nested_cause, depth + 1, param_env, predicate, )); } let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args); let mut ty = selcx.infcx.resolve_vars_if_possible(ty); if ty.has_aliases() { ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations); } ty } pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, alias_ty: ty::AliasTy<'tcx>, cause: ObligationCause<'tcx>, depth: usize, obligations: &mut Vec>, ) -> ty::GenericArgsRef<'tcx> { let tcx = selcx.tcx(); let impl_def_id = tcx.parent(alias_ty.def_id); let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id); let mut impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args); if !selcx.infcx.next_trait_solver() { impl_ty = normalize_with_depth_to( selcx, param_env, cause.clone(), depth + 1, impl_ty, obligations, ); } // Infer the generic parameters of the impl by unifying the // impl type with the self type of the projection. let mut self_ty = alias_ty.self_ty(); if !selcx.infcx.next_trait_solver() { self_ty = normalize_with_depth_to( selcx, param_env, cause.clone(), depth + 1, self_ty, obligations, ); } match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { Ok(mut ok) => obligations.append(&mut ok.obligations), Err(_) => { tcx.dcx().span_bug( cause.span, format!("{self_ty:?} was equal to {impl_ty:?} during selection but now it is not"), ); } } alias_ty.rebase_inherent_args_onto_impl(impl_args, tcx) } enum Projected<'tcx> { Progress(Progress<'tcx>), NoProgress(ty::Term<'tcx>), } struct Progress<'tcx> { term: ty::Term<'tcx>, obligations: Vec>, } impl<'tcx> Progress<'tcx> { fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self { Progress { term: Ty::new_error(tcx, guar).into(), obligations: vec![] } } fn with_addl_obligations(mut self, mut obligations: Vec>) -> Self { self.obligations.append(&mut obligations); self } } /// Computes the result of a projection type (if we can). /// /// IMPORTANT: /// - `obligation` must be fully normalized #[instrument(level = "info", skip(selcx))] fn project<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, ) -> Result, ProjectionError<'tcx>> { if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) { // This should really be an immediate error, but some existing code // relies on being able to recover from this. return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow( OverflowError::Canonical, ))); } if let Err(guar) = obligation.predicate.error_reported() { return Ok(Projected::Progress(Progress::error(selcx.tcx(), guar))); } let mut candidates = ProjectionCandidateSet::None; // Make sure that the following procedures are kept in order. ParamEnv // needs to be first because it has highest priority, and Select checks // the return value of push_candidate which assumes it's ran at last. assemble_candidates_from_param_env(selcx, obligation, &mut candidates); assemble_candidates_from_trait_def(selcx, obligation, &mut candidates); assemble_candidates_from_object_ty(selcx, obligation, &mut candidates); if let ProjectionCandidateSet::Single(ProjectionCandidate::Object(_)) = candidates { // Avoid normalization cycle from selection (see // `assemble_candidates_from_object_ty`). // FIXME(lazy_normalization): Lazy normalization should save us from // having to special case this. } else { assemble_candidates_from_impls(selcx, obligation, &mut candidates); }; match candidates { ProjectionCandidateSet::Single(candidate) => { Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate))) } ProjectionCandidateSet::None => { let tcx = selcx.tcx(); let term = match tcx.def_kind(obligation.predicate.def_id) { DefKind::AssocTy => { Ty::new_projection(tcx, obligation.predicate.def_id, obligation.predicate.args) .into() } DefKind::AssocConst => ty::Const::new_unevaluated( tcx, ty::UnevaluatedConst::new( obligation.predicate.def_id, obligation.predicate.args, ), tcx.type_of(obligation.predicate.def_id) .instantiate(tcx, obligation.predicate.args), ) .into(), kind => { bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id)) } }; Ok(Projected::NoProgress(term)) } // Error occurred while trying to processing impls. ProjectionCandidateSet::Error(e) => Err(ProjectionError::TraitSelectionError(e)), // Inherent ambiguity that prevents us from even enumerating the // candidates. ProjectionCandidateSet::Ambiguous => Err(ProjectionError::TooManyCandidates), } } /// The first thing we have to do is scan through the parameter /// environment to see whether there are any projection predicates /// there that can answer this question. fn assemble_candidates_from_param_env<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { assemble_candidates_from_predicates( selcx, obligation, candidate_set, ProjectionCandidate::ParamEnv, obligation.param_env.caller_bounds().iter(), false, ); } /// In the case of a nested projection like `<::FooT as Bar>::BarT`, we may find /// that the definition of `Foo` has some clues: /// /// ```ignore (illustrative) /// trait Foo { /// type FooT : Bar /// } /// ``` /// /// Here, for example, we could conclude that the result is `i32`. fn assemble_candidates_from_trait_def<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { debug!("assemble_candidates_from_trait_def(..)"); let mut ambiguous = false; selcx.for_each_item_bound( obligation.predicate.self_ty(), |selcx, clause, _| { let Some(clause) = clause.as_projection_clause() else { return ControlFlow::Continue(()); }; if clause.projection_def_id() != obligation.predicate.def_id { return ControlFlow::Continue(()); } let is_match = selcx.infcx.probe(|_| selcx.match_projection_projections(obligation, clause, true)); match is_match { ProjectionMatchesProjection::Yes => { candidate_set.push_candidate(ProjectionCandidate::TraitDef(clause)); if !obligation.predicate.has_non_region_infer() { // HACK: Pick the first trait def candidate for a fully // inferred predicate. This is to allow duplicates that // differ only in normalization. return ControlFlow::Break(()); } } ProjectionMatchesProjection::Ambiguous => { candidate_set.mark_ambiguous(); } ProjectionMatchesProjection::No => {} } ControlFlow::Continue(()) }, // `ProjectionCandidateSet` is borrowed in the above closure, // so just mark ambiguous outside of the closure. || ambiguous = true, ); if ambiguous { candidate_set.mark_ambiguous(); } } /// In the case of a trait object like /// ` as Iterator>::Item` we can use the existential /// predicate in the trait object. /// /// We don't go through the select candidate for these bounds to avoid cycles: /// In the above case, `dyn Iterator: Iterator` would create a /// nested obligation of ` as Iterator>::Item: Sized`, /// this then has to be normalized without having to prove /// `dyn Iterator: Iterator` again. fn assemble_candidates_from_object_ty<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { debug!("assemble_candidates_from_object_ty(..)"); let tcx = selcx.tcx(); if !tcx.trait_def(obligation.predicate.trait_def_id(tcx)).implement_via_object { return; } let self_ty = obligation.predicate.self_ty(); let object_ty = selcx.infcx.shallow_resolve(self_ty); let data = match object_ty.kind() { ty::Dynamic(data, ..) => data, ty::Infer(ty::TyVar(_)) => { // If the self-type is an inference variable, then it MAY wind up // being an object type, so induce an ambiguity. candidate_set.mark_ambiguous(); return; } _ => return, }; let env_predicates = data .projection_bounds() .filter(|bound| bound.item_def_id() == obligation.predicate.def_id) .map(|p| p.with_self_ty(tcx, object_ty).to_predicate(tcx)); assemble_candidates_from_predicates( selcx, obligation, candidate_set, ProjectionCandidate::Object, env_predicates, false, ); } #[instrument( level = "debug", skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates) )] fn assemble_candidates_from_predicates<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionCandidateSet<'tcx>, ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionCandidate<'tcx>, env_predicates: impl Iterator>, potentially_unnormalized_candidates: bool, ) { let infcx = selcx.infcx; for predicate in env_predicates { let bound_predicate = predicate.kind(); if let ty::ClauseKind::Projection(data) = predicate.kind().skip_binder() { let data = bound_predicate.rebind(data); if data.projection_def_id() != obligation.predicate.def_id { continue; } let is_match = infcx.probe(|_| { selcx.match_projection_projections( obligation, data, potentially_unnormalized_candidates, ) }); match is_match { ProjectionMatchesProjection::Yes => { candidate_set.push_candidate(ctor(data)); if potentially_unnormalized_candidates && !obligation.predicate.has_non_region_infer() { // HACK: Pick the first trait def candidate for a fully // inferred predicate. This is to allow duplicates that // differ only in normalization. return; } } ProjectionMatchesProjection::Ambiguous => { candidate_set.mark_ambiguous(); } ProjectionMatchesProjection::No => {} } } } } #[instrument(level = "debug", skip(selcx, obligation, candidate_set))] fn assemble_candidates_from_impls<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { // If we are resolving `>::Item == Type`, // start out by selecting the predicate `T as TraitRef<...>`: let trait_ref = obligation.predicate.trait_ref(selcx.tcx()); let trait_obligation = obligation.with(selcx.tcx(), trait_ref); let _ = selcx.infcx.commit_if_ok(|_| { let impl_source = match selcx.select(&trait_obligation) { Ok(Some(impl_source)) => impl_source, Ok(None) => { candidate_set.mark_ambiguous(); return Err(()); } Err(e) => { debug!(error = ?e, "selection error"); candidate_set.mark_error(e); return Err(()); } }; let eligible = match &impl_source { 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 // impl's type is declared as default, then we disable // projection (even if the trait ref is fully // monomorphic). In the case where trait ref is not // fully monomorphic (i.e., includes type parameters), // this is because those type parameters may // ultimately be bound to types from other crates that // may have specialized impls we can't see. In the // case where the trait ref IS fully monomorphic, this // is a policy decision that we made in the RFC in // order to preserve flexibility for the crate that // defined the specializable impl to specialize later // for existing types. // // In either case, we handle this by not adding a // candidate for an impl if it contains a `default` // type. // // NOTE: This should be kept in sync with the similar code in // `rustc_ty_utils::instance::resolve_associated_item()`. let node_item = specialization_graph::assoc_def(selcx.tcx(), impl_data.impl_def_id, obligation.predicate.def_id) .map_err(|ErrorGuaranteed { .. }| ())?; if node_item.is_final() { // Non-specializable items are always projectable. true } else { // Only reveal a specializable default if we're past type-checking // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. if obligation.param_env.reveal() == Reveal::All { // NOTE(eddyb) inference variables can resolve to parameters, so // assume `poly_trait_ref` isn't monomorphic, if it contains any. let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(trait_ref); !poly_trait_ref.still_further_specializable() } else { debug!( assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), ?obligation.predicate, "assemble_candidates_from_impls: not eligible due to default", ); false } } } 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. let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let lang_items = selcx.tcx().lang_items(); if [ lang_items.coroutine_trait(), lang_items.future_trait(), lang_items.iterator_trait(), lang_items.async_iterator_trait(), lang_items.fn_trait(), lang_items.fn_mut_trait(), lang_items.fn_once_trait(), lang_items.async_fn_trait(), lang_items.async_fn_mut_trait(), lang_items.async_fn_once_trait(), ].contains(&Some(trait_ref.def_id)) { true } else if lang_items.async_fn_kind_helper() == Some(trait_ref.def_id) { // FIXME(async_closures): Validity constraints here could be cleaned up. if obligation.predicate.args.type_at(0).is_ty_var() || obligation.predicate.args.type_at(4).is_ty_var() || obligation.predicate.args.type_at(5).is_ty_var() { candidate_set.mark_ambiguous(); true } else { obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some() && obligation.predicate.args.type_at(1).to_opt_closure_kind().is_some() } } else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) { match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Adt(..) | ty::Foreign(_) | ty::Str | ty::Array(..) | ty::Pat(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Dynamic(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Never | ty::Tuple(..) // Integers and floats always have `u8` as their discriminant. | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, // type parameters, opaques, and unnormalized projections don't have // a known discriminant and may need to be normalized further or rely // on param env for discriminant projections ty::Param(_) | ty::Alias(..) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) | ty::Error(_) => false, } } else if lang_items.async_destruct_trait() == Some(trait_ref.def_id) { match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Adt(..) | ty::Str | ty::Array(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Dynamic(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Pat(..) | ty::Never | ty::Tuple(..) | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, // type parameters, opaques, and unnormalized projections don't have // a known discriminant and may need to be normalized further or rely // on param env for async destructor projections ty::Param(_) | ty::Foreign(_) | ty::Alias(..) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) | ty::Error(_) => false, } } else if lang_items.pointee_trait() == Some(trait_ref.def_id) { let tail = selcx.tcx().struct_tail_with_normalize( self_ty, |ty| { // We throw away any obligations we get from this, since we normalize // and confirm these obligations once again during confirmation normalize_with_depth( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, ty, ) .value }, || {}, ); match tail.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Array(..) | ty::Pat(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Dynamic(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Never // Extern types have unit metadata, according to RFC 2850 | ty::Foreign(_) // If returned by `struct_tail_without_normalization` this is a unit struct // without any fields, or not a struct, and therefore is Sized. | ty::Adt(..) // If returned by `struct_tail_without_normalization` this is the empty tuple. | ty::Tuple(..) // Integers and floats are always Sized, and so have unit type metadata. | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, // We normalize from `Wrapper::Metadata` to `Tail::Metadata` if able. // Otherwise, type parameters, opaques, and unnormalized projections have // unit metadata if they're known (e.g. by the param_env) to be sized. ty::Param(_) | ty::Alias(..) if self_ty != tail || selcx.infcx.predicate_must_hold_modulo_regions( &obligation.with( selcx.tcx(), ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]), ), ) => { true } // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? ty::Param(_) | ty::Alias(..) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) | ty::Error(_) => { if tail.has_infer_types() { candidate_set.mark_ambiguous(); } false } } } else { bug!("unexpected builtin trait with associated type: {trait_ref:?}") } } ImplSource::Param(..) => { // This case tell us nothing about the value of an // associated type. Consider: // // ``` // trait SomeTrait { type Foo; } // fn foo(...) { } // ``` // // If the user writes `::Foo`, then the `T // : SomeTrait` binding does not help us decide what the // type `Foo` is (at least, not more specifically than // what we already knew). // // But wait, you say! What about an example like this: // // ``` // fn bar>(...) { ... } // ``` // // Doesn't the `T : SomeTrait` predicate help // resolve `T::Foo`? And of course it does, but in fact // that single predicate is desugared into two predicates // in the compiler: a trait predicate (`T : SomeTrait`) and a // projection. And the projection where clause is handled // in `assemble_candidates_from_param_env`. false } 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 } ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // These traits have no associated types. selcx.tcx().dcx().span_delayed_bug( obligation.cause.span, format!("Cannot project an associated type from `{impl_source:?}`"), ); return Err(()) } }; if eligible { if candidate_set.push_candidate(ProjectionCandidate::Select(impl_source)) { Ok(()) } else { Err(()) } } else { Err(()) } }); } fn confirm_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, candidate: ProjectionCandidate<'tcx>, ) -> Progress<'tcx> { debug!(?obligation, ?candidate, "confirm_candidate"); let mut progress = match candidate { ProjectionCandidate::ParamEnv(poly_projection) | ProjectionCandidate::Object(poly_projection) => { confirm_param_env_candidate(selcx, obligation, poly_projection, false) } ProjectionCandidate::TraitDef(poly_projection) => { confirm_param_env_candidate(selcx, obligation, poly_projection, true) } ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } }; // When checking for cycle during evaluation, we compare predicates with // "syntactic" equality. Since normalization generally introduces a type // with new region variables, we need to resolve them to existing variables // when possible for this to work. See `auto-trait-projection-recursion.rs` // for a case where this matters. if progress.term.has_infer_regions() { progress.term = progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx)); } progress } fn confirm_select_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, impl_source: Selection<'tcx>, ) -> Progress<'tcx> { match impl_source { 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.coroutine_trait() == Some(trait_def_id) { confirm_coroutine_candidate(selcx, obligation, data) } else if lang_items.future_trait() == Some(trait_def_id) { confirm_future_candidate(selcx, obligation, data) } else if lang_items.iterator_trait() == Some(trait_def_id) { confirm_iterator_candidate(selcx, obligation, data) } else if lang_items.async_iterator_trait() == Some(trait_def_id) { confirm_async_iterator_candidate(selcx, obligation, data) } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() { if obligation.predicate.self_ty().is_closure() || obligation.predicate.self_ty().is_coroutine_closure() { confirm_closure_candidate(selcx, obligation, data) } else { confirm_fn_pointer_candidate(selcx, obligation, data) } } else if selcx.tcx().async_fn_trait_kind_from_def_id(trait_def_id).is_some() { confirm_async_closure_candidate(selcx, obligation, data) } else if lang_items.async_fn_kind_helper() == Some(trait_def_id) { confirm_async_fn_kind_helper_candidate(selcx, obligation, data) } else { confirm_builtin_candidate(selcx, obligation, data) } } 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, "Cannot project an associated type from `{:?}`", impl_source ) } } } fn confirm_coroutine_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, nested: Vec>, ) -> Progress<'tcx> { let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let ty::Coroutine(_, args) = self_ty.kind() else { unreachable!( "expected coroutine self type for built-in coroutine candidate, found {self_ty}" ) }; let coroutine_sig = args.as_coroutine().sig(); let Normalized { value: coroutine_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, coroutine_sig, ); debug!(?obligation, ?coroutine_sig, ?obligations, "confirm_coroutine_candidate"); let tcx = selcx.tcx(); let coroutine_def_id = tcx.require_lang_item(LangItem::Coroutine, None); let (trait_ref, yield_ty, return_ty) = super::util::coroutine_trait_ref_and_outputs( tcx, coroutine_def_id, obligation.predicate.self_ty(), coroutine_sig, ); let name = tcx.associated_item(obligation.predicate.def_id).name; let ty = if name == sym::Return { return_ty } else if name == sym::Yield { yield_ty } else { span_bug!( tcx.def_span(obligation.predicate.def_id), "unexpected associated type: `Coroutine::{name}`" ); }; let predicate = ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), term: ty.into(), }; confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(nested) .with_addl_obligations(obligations) } fn confirm_future_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, nested: Vec>, ) -> Progress<'tcx> { let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let ty::Coroutine(_, args) = self_ty.kind() else { unreachable!( "expected coroutine self type for built-in async future candidate, found {self_ty}" ) }; let coroutine_sig = args.as_coroutine().sig(); let Normalized { value: coroutine_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, coroutine_sig, ); debug!(?obligation, ?coroutine_sig, ?obligations, "confirm_future_candidate"); let tcx = selcx.tcx(); let fut_def_id = tcx.require_lang_item(LangItem::Future, None); let (trait_ref, return_ty) = super::util::future_trait_ref_and_outputs( tcx, fut_def_id, obligation.predicate.self_ty(), coroutine_sig, ); debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Output); let predicate = ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), term: return_ty.into(), }; confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(nested) .with_addl_obligations(obligations) } fn confirm_iterator_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, nested: Vec>, ) -> Progress<'tcx> { let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let ty::Coroutine(_, args) = self_ty.kind() else { unreachable!("expected coroutine self type for built-in gen candidate, found {self_ty}") }; let gen_sig = args.as_coroutine().sig(); let Normalized { value: gen_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, gen_sig, ); debug!(?obligation, ?gen_sig, ?obligations, "confirm_iterator_candidate"); let tcx = selcx.tcx(); let iter_def_id = tcx.require_lang_item(LangItem::Iterator, None); let (trait_ref, yield_ty) = super::util::iterator_trait_ref_and_outputs( tcx, iter_def_id, obligation.predicate.self_ty(), gen_sig, ); debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); let predicate = ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), term: yield_ty.into(), }; confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(nested) .with_addl_obligations(obligations) } fn confirm_async_iterator_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, nested: Vec>, ) -> Progress<'tcx> { let ty::Coroutine(_, args) = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() else { unreachable!() }; let gen_sig = args.as_coroutine().sig(); let Normalized { value: gen_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, gen_sig, ); debug!(?obligation, ?gen_sig, ?obligations, "confirm_async_iterator_candidate"); let tcx = selcx.tcx(); let iter_def_id = tcx.require_lang_item(LangItem::AsyncIterator, None); let (trait_ref, yield_ty) = super::util::async_iterator_trait_ref_and_outputs( tcx, iter_def_id, obligation.predicate.self_ty(), gen_sig, ); debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); let ty::Adt(_poll_adt, args) = *yield_ty.kind() else { bug!(); }; let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else { bug!(); }; let item_ty = args.type_at(0); let predicate = ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), term: item_ty.into(), }; confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(nested) .with_addl_obligations(obligations) } fn confirm_builtin_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, data: Vec>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); let self_ty = obligation.predicate.self_ty(); let lang_items = tcx.lang_items(); let item_def_id = obligation.predicate.def_id; let trait_def_id = tcx.trait_of_item(item_def_id).unwrap(); let args = tcx.mk_args(&[self_ty.into()]); let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) { let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); assert_eq!(discriminant_def_id, item_def_id); (self_ty.discriminant_ty(tcx).into(), Vec::new()) } else if lang_items.async_destruct_trait() == Some(trait_def_id) { let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0]; assert_eq!(destructor_def_id, item_def_id); (self_ty.async_destructor_ty(tcx, obligation.param_env).into(), Vec::new()) } else if lang_items.pointee_trait() == Some(trait_def_id) { let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); assert_eq!(metadata_def_id, item_def_id); let mut obligations = Vec::new(); let normalize = |ty| { normalize_with_depth_to( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, ty, &mut obligations, ) }; let metadata_ty = self_ty.ptr_metadata_ty_or_tail(tcx, normalize).unwrap_or_else(|tail| { if tail == self_ty { // This is the "fallback impl" for type parameters, unnormalizable projections // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't // exist. Instead, `Pointee` should be a supertrait of `Sized`. let sized_predicate = ty::TraitRef::from_lang_item( tcx, LangItem::Sized, obligation.cause.span(), [self_ty], ); obligations.push(obligation.with(tcx, sized_predicate)); tcx.types.unit } else { // We know that `self_ty` has the same metadata as `tail`. This allows us // to prove predicates like `Wrapper::Metadata == Tail::Metadata`. Ty::new_projection(tcx, metadata_def_id, [tail]) } }); (metadata_ty.into(), obligations) } else { bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate); }; let predicate = ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, item_def_id, args), term }; confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(obligations) .with_addl_obligations(data) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, nested: Vec>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let sig = fn_type.fn_sig(tcx); let Normalized { value: sig, obligations } = normalize_with_depth( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, sig, ); let host_effect_param = match *fn_type.kind() { ty::FnDef(def_id, args) => tcx .generics_of(def_id) .host_effect_index .map_or(tcx.consts.true_, |idx| args.const_at(idx)), ty::FnPtr(_) => tcx.consts.true_, _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"), }; confirm_callable_candidate( selcx, obligation, sig, util::TupleArgumentsFlag::Yes, host_effect_param, ) .with_addl_obligations(nested) .with_addl_obligations(obligations) } fn confirm_closure_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, nested: Vec>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let closure_sig = match *self_ty.kind() { ty::Closure(_, args) => args.as_closure().sig(), // Construct a "normal" `FnOnce` signature for coroutine-closure. This is // basically duplicated with the `AsyncFnOnce::CallOnce` confirmation, but // I didn't see a good way to unify those. ty::CoroutineClosure(def_id, args) => { let args = args.as_coroutine_closure(); let kind_ty = args.kind_ty(); args.coroutine_closure_sig().map_bound(|sig| { // If we know the kind and upvars, use that directly. // Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay // the projection, like the `AsyncFn*` traits do. let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() // Fall back to projection if upvars aren't constrained && !args.tupled_upvars_ty().is_ty_var() { sig.to_coroutine_given_kind_and_upvars( tcx, args.parent_args(), tcx.coroutine_for_closure(def_id), ty::ClosureKind::FnOnce, tcx.lifetimes.re_static, args.tupled_upvars_ty(), args.coroutine_captures_by_ref_ty(), ) } else { let async_fn_kind_trait_def_id = tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); let upvars_projection_def_id = tcx .associated_items(async_fn_kind_trait_def_id) .filter_by_name_unhygienic(sym::Upvars) .next() .unwrap() .def_id; let tupled_upvars_ty = Ty::new_projection( tcx, upvars_projection_def_id, [ ty::GenericArg::from(kind_ty), Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(), tcx.lifetimes.re_static.into(), sig.tupled_inputs_ty.into(), args.tupled_upvars_ty().into(), args.coroutine_captures_by_ref_ty().into(), ], ); sig.to_coroutine( tcx, args.parent_args(), Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce), tcx.coroutine_for_closure(def_id), tupled_upvars_ty, ) }; tcx.mk_fn_sig( [sig.tupled_inputs_ty], output_ty, sig.c_variadic, sig.unsafety, sig.abi, ) }) } _ => { unreachable!("expected closure self type for closure candidate, found {self_ty}"); } }; let Normalized { value: closure_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, closure_sig, ); debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate"); confirm_callable_candidate( selcx, obligation, closure_sig, util::TupleArgumentsFlag::No, // FIXME(effects): This doesn't handle const closures correctly! selcx.tcx().consts.true_, ) .with_addl_obligations(nested) .with_addl_obligations(obligations) } fn confirm_callable_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, flag: util::TupleArgumentsFlag, fn_host_effect: ty::Const<'tcx>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); debug!(?obligation, ?fn_sig, "confirm_callable_candidate"); let fn_once_def_id = tcx.require_lang_item(LangItem::FnOnce, None); let fn_once_output_def_id = tcx.require_lang_item(LangItem::FnOnceOutput, None); let predicate = super::util::closure_trait_ref_and_return_type( tcx, fn_once_def_id, obligation.predicate.self_ty(), fn_sig, flag, fn_host_effect, ) .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, fn_once_output_def_id, trait_ref.args), term: ret_type.into(), }); confirm_param_env_candidate(selcx, obligation, predicate, true) } fn confirm_async_closure_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, nested: Vec>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let goal_kind = tcx.async_fn_trait_kind_from_def_id(obligation.predicate.trait_def_id(tcx)).unwrap(); let env_region = match goal_kind { ty::ClosureKind::Fn | ty::ClosureKind::FnMut => obligation.predicate.args.region_at(2), ty::ClosureKind::FnOnce => tcx.lifetimes.re_static, }; let item_name = tcx.item_name(obligation.predicate.def_id); let poly_cache_entry = match *self_ty.kind() { ty::CoroutineClosure(def_id, args) => { let args = args.as_coroutine_closure(); let kind_ty = args.kind_ty(); let sig = args.coroutine_closure_sig().skip_binder(); let term = match item_name { sym::CallOnceFuture | sym::CallRefFuture => { if let Some(closure_kind) = kind_ty.to_opt_closure_kind() // Fall back to projection if upvars aren't constrained && !args.tupled_upvars_ty().is_ty_var() { if !closure_kind.extends(goal_kind) { bug!("we should not be confirming if the closure kind is not met"); } sig.to_coroutine_given_kind_and_upvars( tcx, args.parent_args(), tcx.coroutine_for_closure(def_id), goal_kind, env_region, args.tupled_upvars_ty(), args.coroutine_captures_by_ref_ty(), ) } else { let async_fn_kind_trait_def_id = tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); let upvars_projection_def_id = tcx .associated_items(async_fn_kind_trait_def_id) .filter_by_name_unhygienic(sym::Upvars) .next() .unwrap() .def_id; // When we don't know the closure kind (and therefore also the closure's upvars, // which are computed at the same time), we must delay the computation of the // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait // goal functions similarly to the old `ClosureKind` predicate, and ensures that // the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars` // will project to the right upvars for the generator, appending the inputs and // coroutine upvars respecting the closure kind. // N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`. let tupled_upvars_ty = Ty::new_projection( tcx, upvars_projection_def_id, [ ty::GenericArg::from(kind_ty), Ty::from_closure_kind(tcx, goal_kind).into(), env_region.into(), sig.tupled_inputs_ty.into(), args.tupled_upvars_ty().into(), args.coroutine_captures_by_ref_ty().into(), ], ); sig.to_coroutine( tcx, args.parent_args(), Ty::from_closure_kind(tcx, goal_kind), tcx.coroutine_for_closure(def_id), tupled_upvars_ty, ) } } sym::Output => sig.return_ty, name => bug!("no such associated type: {name}"), }; let projection_ty = match item_name { sym::CallOnceFuture | sym::Output => ty::AliasTy::new( tcx, obligation.predicate.def_id, [self_ty, sig.tupled_inputs_ty], ), sym::CallRefFuture => ty::AliasTy::new( tcx, obligation.predicate.def_id, [ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()], ), name => bug!("no such associated type: {name}"), }; args.coroutine_closure_sig() .rebind(ty::ProjectionPredicate { projection_ty, term: term.into() }) } ty::FnDef(..) | ty::FnPtr(..) => { let bound_sig = self_ty.fn_sig(tcx); let sig = bound_sig.skip_binder(); let term = match item_name { sym::CallOnceFuture | sym::CallRefFuture => sig.output(), sym::Output => { let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); let future_output_def_id = tcx .associated_items(future_trait_def_id) .filter_by_name_unhygienic(sym::Output) .next() .unwrap() .def_id; Ty::new_projection(tcx, future_output_def_id, [sig.output()]) } name => bug!("no such associated type: {name}"), }; let projection_ty = match item_name { sym::CallOnceFuture | sym::Output => ty::AliasTy::new( tcx, obligation.predicate.def_id, [self_ty, Ty::new_tup(tcx, sig.inputs())], ), sym::CallRefFuture => ty::AliasTy::new( tcx, obligation.predicate.def_id, [ ty::GenericArg::from(self_ty), Ty::new_tup(tcx, sig.inputs()).into(), env_region.into(), ], ), name => bug!("no such associated type: {name}"), }; bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() }) } ty::Closure(_, args) => { let args = args.as_closure(); let bound_sig = args.sig(); let sig = bound_sig.skip_binder(); let term = match item_name { sym::CallOnceFuture | sym::CallRefFuture => sig.output(), sym::Output => { let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); let future_output_def_id = tcx .associated_items(future_trait_def_id) .filter_by_name_unhygienic(sym::Output) .next() .unwrap() .def_id; Ty::new_projection(tcx, future_output_def_id, [sig.output()]) } name => bug!("no such associated type: {name}"), }; let projection_ty = match item_name { sym::CallOnceFuture | sym::Output => { ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]]) } sym::CallRefFuture => ty::AliasTy::new( tcx, obligation.predicate.def_id, [ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()], ), name => bug!("no such associated type: {name}"), }; bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() }) } _ => bug!("expected callable type for AsyncFn candidate"), }; confirm_param_env_candidate(selcx, obligation, poly_cache_entry, true) .with_addl_obligations(nested) } fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, nested: Vec>, ) -> Progress<'tcx> { let [ // We already checked that the goal_kind >= closure_kind _closure_kind_ty, goal_kind_ty, borrow_region, tupled_inputs_ty, tupled_upvars_ty, coroutine_captures_by_ref_ty, ] = **obligation.predicate.args else { bug!(); }; let predicate = ty::ProjectionPredicate { projection_ty: ty::AliasTy::new( selcx.tcx(), obligation.predicate.def_id, obligation.predicate.args, ), term: ty::CoroutineClosureSignature::tupled_upvars_by_closure_kind( selcx.tcx(), goal_kind_ty.expect_ty().to_opt_closure_kind().unwrap(), tupled_inputs_ty.expect_ty(), tupled_upvars_ty.expect_ty(), coroutine_captures_by_ref_ty.expect_ty(), borrow_region.expect_region(), ) .into(), }; confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(nested) } fn confirm_param_env_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, poly_cache_entry: ty::PolyProjectionPredicate<'tcx>, potentially_unnormalized_candidate: bool, ) -> Progress<'tcx> { let infcx = selcx.infcx; let cause = &obligation.cause; let param_env = obligation.param_env; let cache_entry = infcx.instantiate_binder_with_fresh_vars( cause.span, BoundRegionConversionTime::HigherRankedType, poly_cache_entry, ); let cache_projection = cache_entry.projection_ty; let mut nested_obligations = Vec::new(); let obligation_projection = obligation.predicate; let obligation_projection = ensure_sufficient_stack(|| { normalize_with_depth_to( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, obligation_projection, &mut nested_obligations, ) }); let cache_projection = if potentially_unnormalized_candidate { ensure_sufficient_stack(|| { normalize_with_depth_to( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, cache_projection, &mut nested_obligations, ) }) } else { cache_projection }; debug!(?cache_projection, ?obligation_projection); match infcx.at(cause, param_env).eq( DefineOpaqueTypes::Yes, cache_projection, obligation_projection, ) { Ok(InferOk { value: _, obligations }) => { nested_obligations.extend(obligations); assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations); // FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take // a term instead. Progress { term: cache_entry.term, obligations: nested_obligations } } Err(e) => { let msg = format!( "Failed to unify obligation `{obligation:?}` with poly_projection `{poly_cache_entry:?}`: {e:?}", ); debug!("confirm_param_env_candidate: {}", msg); let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg); Progress { term: err.into(), obligations: vec![] } } } } fn confirm_impl_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); let ImplSourceUserDefinedData { impl_def_id, args, mut nested } = impl_impl_source; let assoc_item_id = obligation.predicate.def_id; let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let param_env = obligation.param_env; let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) { Ok(assoc_ty) => assoc_ty, Err(guar) => return Progress::error(tcx, guar), }; if !assoc_ty.item.defaultness(tcx).has_value() { // This means that the impl is missing a definition for the // associated type. This error will be reported by the type // checker method `check_impl_items_against_trait`, so here we // just return Error. debug!( "confirm_impl_candidate: no associated type {:?} for {:?}", assoc_ty.item.name, obligation.predicate ); return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested }; } // If we're trying to normalize ` as X>::A` using //`impl X for Vec { type A = Box; }`, then: // // * `obligation.predicate.args` is `[Vec, S]` // * `args` is `[u32]` // * `args` ends up as `[u32, S]` let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args); let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node); let ty = tcx.type_of(assoc_ty.item.def_id); let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst); let term: ty::EarlyBinder> = if is_const { let did = assoc_ty.item.def_id; let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did); let uv = ty::UnevaluatedConst::new(did, identity_args); ty.map_bound(|ty| ty::Const::new_unevaluated(tcx, uv, ty).into()) } else { ty.map_bound(|ty| ty.into()) }; if !tcx.check_args_compatible(assoc_ty.item.def_id, args) { let err = Ty::new_error_with_message( tcx, obligation.cause.span, "impl item and trait item have different parameters", ); Progress { term: err.into(), obligations: nested } } else { assoc_ty_own_obligations(selcx, obligation, &mut nested); Progress { term: term.instantiate(tcx, args), obligations: nested } } } // Get obligations corresponding to the predicates from the where-clause of the // associated type itself. fn assoc_ty_own_obligations<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, nested: &mut Vec>, ) { let tcx = selcx.tcx(); let predicates = tcx .predicates_of(obligation.predicate.def_id) .instantiate_own(tcx, obligation.predicate.args); for (predicate, span) in predicates { let normalized = normalize_with_depth_to( selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, predicate, nested, ); let nested_cause = if matches!( obligation.cause.code(), super::CompareImplItemObligation { .. } | super::CheckAssociatedTypeBounds { .. } | super::AscribeUserTypeProvePredicate(..) ) { obligation.cause.clone() } else if span.is_dummy() { ObligationCause::new( obligation.cause.span, obligation.cause.body_id, super::ItemObligation(obligation.predicate.def_id), ) } else { ObligationCause::new( obligation.cause.span, obligation.cause.body_id, super::BindingObligation(obligation.predicate.def_id, span), ) }; nested.push(Obligation::with_depth( tcx, nested_cause, obligation.recursion_depth + 1, obligation.param_env, normalized, )); } } pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized { fn from_poly_projection_predicate( selcx: &mut SelectionContext<'cx, 'tcx>, predicate: ty::PolyProjectionPredicate<'tcx>, ) -> Option; } impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> { fn from_poly_projection_predicate( selcx: &mut SelectionContext<'cx, 'tcx>, predicate: ty::PolyProjectionPredicate<'tcx>, ) -> Option { let infcx = selcx.infcx; // We don't do cross-snapshot caching of obligations with escaping regions, // so there's no cache key to use predicate.no_bound_vars().map(|predicate| { ProjectionCacheKey::new( // We don't attempt to match up with a specific type-variable state // from a specific call to `opt_normalize_projection_type` - if // there's no precise match, the original cache entry is "stranded" // anyway. infcx.resolve_vars_if_possible(predicate.projection_ty), ) }) } }