summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection/src/solve/project_goals
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection/src/solve/project_goals')
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals/inherent_projection.rs50
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals/mod.rs680
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs85
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals/weak_types.rs34
4 files changed, 849 insertions, 0 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/inherent_projection.rs b/compiler/rustc_trait_selection/src/solve/project_goals/inherent_projection.rs
new file mode 100644
index 000000000..28fe59b7f
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/project_goals/inherent_projection.rs
@@ -0,0 +1,50 @@
+//! Computes a normalizes-to (projection) goal for inherent associated types,
+//! `#![feature(inherent_associated_type)]`. Since astconv already determines
+//! which impl the IAT is being projected from, we just:
+//! 1. instantiate substs,
+//! 2. equate the self type, and
+//! 3. instantiate and register where clauses.
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+use super::EvalCtxt;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ pub(super) fn normalize_inherent_associated_type(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let inherent = goal.predicate.projection_ty;
+ let expected = goal.predicate.term.ty().expect("inherent consts are treated separately");
+
+ let impl_def_id = tcx.parent(inherent.def_id);
+ let impl_substs = self.fresh_args_for_item(impl_def_id);
+
+ // Equate impl header and add impl where clauses
+ self.eq(
+ goal.param_env,
+ inherent.self_ty(),
+ tcx.type_of(impl_def_id).instantiate(tcx, impl_substs),
+ )?;
+
+ // Equate IAT with the RHS of the project goal
+ let inherent_substs = inherent.rebase_inherent_args_onto_impl(impl_substs, tcx);
+ self.eq(
+ goal.param_env,
+ expected,
+ tcx.type_of(inherent.def_id).instantiate(tcx, inherent_substs),
+ )
+ .expect("expected goal term to be fully unconstrained");
+
+ // Check both where clauses on the impl and IAT
+ self.add_goals(
+ tcx.predicates_of(inherent.def_id)
+ .instantiate(tcx, inherent_substs)
+ .into_iter()
+ .map(|(pred, _)| goal.with(tcx, pred)),
+ );
+
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs
new file mode 100644
index 000000000..240141065
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs
@@ -0,0 +1,680 @@
+use crate::traits::{check_args_compatible, specialization_graph};
+
+use super::assembly::{self, structural_traits};
+use super::EvalCtxt;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_hir::LangItem;
+use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::specialization_graph::LeafDef;
+use rustc_infer::traits::Reveal;
+use rustc_middle::traits::solve::{
+ CandidateSource, 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};
+use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
+use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};
+
+mod inherent_projection;
+mod opaques;
+mod weak_types;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ #[instrument(level = "debug", skip(self), ret)]
+ pub(super) fn compute_projection_goal(
+ &mut self,
+ goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let def_id = goal.predicate.def_id();
+ match self.tcx().def_kind(def_id) {
+ DefKind::AssocTy | DefKind::AssocConst => {
+ // To only compute normalization once for each projection we only
+ // assemble normalization candidates if the expected term is an
+ // unconstrained inference variable.
+ //
+ // Why: For better cache hits, since if we have an unconstrained RHS then
+ // there are only as many cache keys as there are (canonicalized) alias
+ // types in each normalizes-to goal. This also weakens inference in a
+ // forwards-compatible way so we don't use the value of the RHS term to
+ // affect candidate assembly for projections.
+ //
+ // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
+ // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
+ // `U` and equate it with `u32`. This means that we don't need a separate
+ // projection cache in the solver, since we're piggybacking off of regular
+ // goal caching.
+ if self.term_is_fully_unconstrained(goal) {
+ match self.tcx().associated_item(def_id).container {
+ ty::AssocItemContainer::TraitContainer => {
+ let candidates = self.assemble_and_evaluate_candidates(goal);
+ self.merge_candidates(candidates)
+ }
+ ty::AssocItemContainer::ImplContainer => {
+ self.normalize_inherent_associated_type(goal)
+ }
+ }
+ } else {
+ self.set_normalizes_to_hack_goal(goal);
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+ }
+ DefKind::AnonConst => self.normalize_anon_const(goal),
+ DefKind::OpaqueTy => self.normalize_opaque_type(goal),
+ DefKind::TyAlias => self.normalize_weak_type(goal),
+ kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
+ }
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ fn normalize_anon_const(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ if let Some(normalized_const) = self.try_const_eval_resolve(
+ goal.param_env,
+ ty::UnevaluatedConst::new(
+ goal.predicate.projection_ty.def_id,
+ goal.predicate.projection_ty.args,
+ ),
+ self.tcx()
+ .type_of(goal.predicate.projection_ty.def_id)
+ .no_bound_vars()
+ .expect("const ty should not rely on other generics"),
+ ) {
+ self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ } else {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+ }
+ }
+}
+
+impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
+ fn self_ty(self) -> Ty<'tcx> {
+ self.self_ty()
+ }
+
+ fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
+ self.projection_ty.trait_ref(tcx)
+ }
+
+ fn polarity(self) -> ty::ImplPolarity {
+ ty::ImplPolarity::Positive
+ }
+
+ fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+ self.with_self_ty(tcx, self_ty)
+ }
+
+ fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
+ self.trait_def_id(tcx)
+ }
+
+ fn probe_and_match_goal_against_assumption(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ assumption: ty::Clause<'tcx>,
+ then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
+ ) -> QueryResult<'tcx> {
+ if let Some(projection_pred) = assumption.as_projection_clause() {
+ if projection_pred.projection_def_id() == goal.predicate.def_id() {
+ let tcx = ecx.tcx();
+ ecx.probe_misc_candidate("assumption").enter(|ecx| {
+ let assumption_projection_pred =
+ ecx.instantiate_binder_with_infer(projection_pred);
+ ecx.eq(
+ goal.param_env,
+ goal.predicate.projection_ty,
+ assumption_projection_pred.projection_ty,
+ )?;
+ ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
+ .expect("expected goal term to be fully unconstrained");
+
+ // Add GAT where clauses from the trait's definition
+ ecx.add_goals(
+ tcx.predicates_of(goal.predicate.def_id())
+ .instantiate_own(tcx, goal.predicate.projection_ty.args)
+ .map(|(pred, _)| goal.with(tcx, pred)),
+ );
+
+ then(ecx)
+ })
+ } else {
+ Err(NoSolution)
+ }
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ fn consider_impl_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
+ impl_def_id: DefId,
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
+
+ let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
+ if !drcx.args_refs_may_unify(goal_trait_ref.args, impl_trait_ref.skip_binder().args) {
+ return Err(NoSolution);
+ }
+
+ ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
+ let impl_args = ecx.fresh_args_for_item(impl_def_id);
+ let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
+
+ ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
+
+ let where_clause_bounds = tcx
+ .predicates_of(impl_def_id)
+ .instantiate(tcx, impl_args)
+ .predicates
+ .into_iter()
+ .map(|pred| goal.with(tcx, pred));
+ ecx.add_goals(where_clause_bounds);
+
+ // Add GAT where clauses from the trait's definition
+ ecx.add_goals(
+ tcx.predicates_of(goal.predicate.def_id())
+ .instantiate_own(tcx, goal.predicate.projection_ty.args)
+ .map(|(pred, _)| goal.with(tcx, pred)),
+ );
+
+ // In case the associated item is hidden due to specialization, we have to
+ // return ambiguity this would otherwise be incomplete, resulting in
+ // unsoundness during coherence (#105782).
+ let Some(assoc_def) = fetch_eligible_assoc_item_def(
+ ecx,
+ goal.param_env,
+ goal_trait_ref,
+ goal.predicate.def_id(),
+ impl_def_id,
+ )?
+ else {
+ return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ };
+
+ let error_response = |ecx: &mut EvalCtxt<'_, 'tcx>, reason| {
+ let guar = tcx.sess.delay_span_bug(tcx.def_span(assoc_def.item.def_id), reason);
+ let error_term = match assoc_def.item.kind {
+ ty::AssocKind::Const => ty::Const::new_error(
+ tcx,
+ guar,
+ tcx.type_of(goal.predicate.def_id())
+ .instantiate(tcx, goal.predicate.projection_ty.args),
+ )
+ .into(),
+ ty::AssocKind::Type => Ty::new_error(tcx, guar).into(),
+ ty::AssocKind::Fn => unreachable!(),
+ };
+ ecx.eq(goal.param_env, goal.predicate.term, error_term)
+ .expect("expected goal term to be fully unconstrained");
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ };
+
+ if !assoc_def.item.defaultness(tcx).has_value() {
+ return error_response(ecx, "missing value for assoc item in impl");
+ }
+
+ // Getting the right args here is complex, e.g. given:
+ // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
+ // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
+ // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
+ //
+ // We first rebase the goal args onto the impl, going from `[Vec<u32>, i32, u64]`
+ // to `[u32, u64]`.
+ //
+ // And then map these args to the args of the defining impl of `Assoc`, going
+ // from `[u32, u64]` to `[u32, i32, u64]`.
+ let impl_args_with_gat = goal.predicate.projection_ty.args.rebase_onto(
+ tcx,
+ goal_trait_ref.def_id,
+ impl_args,
+ );
+ let args = ecx.translate_args(
+ goal.param_env,
+ impl_def_id,
+ impl_args_with_gat,
+ assoc_def.defining_node,
+ );
+
+ if !check_args_compatible(tcx, assoc_def.item, args) {
+ return error_response(
+ ecx,
+ "associated item has mismatched generic item arguments",
+ );
+ }
+
+ // Finally we construct the actual value of the associated type.
+ let term = match assoc_def.item.kind {
+ ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()),
+ ty::AssocKind::Const => {
+ if tcx.features().associated_const_equality {
+ bug!("associated const projection is not supported yet")
+ } else {
+ ty::EarlyBinder::bind(
+ ty::Const::new_error_with_message(
+ tcx,
+ tcx.type_of(assoc_def.item.def_id).instantiate_identity(),
+ DUMMY_SP,
+ "associated const projection is not supported yet",
+ )
+ .into(),
+ )
+ }
+ }
+ ty::AssocKind::Fn => unreachable!("we should never project to a fn"),
+ };
+
+ ecx.eq(goal.param_env, goal.predicate.term, term.instantiate(tcx, args))
+ .expect("expected goal term to be fully unconstrained");
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+
+ /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error`
+ /// and succeed. Can experiment with this to figure out what results in better error messages.
+ fn consider_error_guaranteed_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ _guar: ErrorGuaranteed,
+ ) -> QueryResult<'tcx> {
+ Err(NoSolution)
+ }
+
+ fn consider_auto_trait_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ ecx.tcx().sess.delay_span_bug(
+ ecx.tcx().def_span(goal.predicate.def_id()),
+ "associated types not allowed on auto traits",
+ );
+ Err(NoSolution)
+ }
+
+ fn consider_trait_alias_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("trait aliases do not have associated types: {:?}", goal);
+ }
+
+ fn consider_builtin_sized_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`Sized` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_copy_clone_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_pointer_like_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`PointerLike` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_fn_ptr_trait_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`FnPtr` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_fn_trait_candidates(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ goal_kind: ty::ClosureKind,
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
+ let tupled_inputs_and_output =
+ match structural_traits::extract_tupled_inputs_and_output_from_callable(
+ tcx,
+ goal.predicate.self_ty(),
+ goal_kind,
+ )? {
+ Some(tupled_inputs_and_output) => tupled_inputs_and_output,
+ None => {
+ return ecx
+ .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ }
+ };
+ let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
+ ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
+ });
+
+ let pred = tupled_inputs_and_output
+ .map_bound(|(inputs, output)| ty::ProjectionPredicate {
+ projection_ty: ty::AliasTy::new(
+ tcx,
+ goal.predicate.def_id(),
+ [goal.predicate.self_ty(), inputs],
+ ),
+ term: output.into(),
+ })
+ .to_predicate(tcx);
+
+ // A built-in `Fn` impl only holds if the output is sized.
+ // (FIXME: technically we only need to check this if the type is a fn ptr...)
+ Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)])
+ }
+
+ fn consider_builtin_tuple_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`Tuple` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_pointee_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
+ ecx.probe_misc_candidate("builtin pointee").enter(|ecx| {
+ let metadata_ty = match goal.predicate.self_ty().kind() {
+ ty::Bool
+ | ty::Char
+ | ty::Int(..)
+ | ty::Uint(..)
+ | ty::Float(..)
+ | ty::Array(..)
+ | ty::RawPtr(..)
+ | ty::Ref(..)
+ | ty::FnDef(..)
+ | ty::FnPtr(..)
+ | ty::Closure(..)
+ | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
+ | ty::Coroutine(..)
+ | ty::CoroutineWitness(..)
+ | ty::Never
+ | ty::Foreign(..) => tcx.types.unit,
+
+ ty::Error(e) => Ty::new_error(tcx, *e),
+
+ ty::Str | ty::Slice(_) => tcx.types.usize,
+
+ ty::Dynamic(_, _, _) => {
+ let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
+ tcx.type_of(dyn_metadata)
+ .instantiate(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
+ }
+
+ ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
+ // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
+ let sized_predicate = ty::TraitRef::from_lang_item(
+ tcx,
+ LangItem::Sized,
+ DUMMY_SP,
+ [ty::GenericArg::from(goal.predicate.self_ty())],
+ );
+ ecx.add_goal(goal.with(tcx, sized_predicate));
+ tcx.types.unit
+ }
+
+ ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
+ None => tcx.types.unit,
+ Some(field_def) => {
+ let self_ty = field_def.ty(tcx, args);
+ ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)));
+ return ecx
+ .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+ }
+ },
+ ty::Adt(_, _) => tcx.types.unit,
+
+ ty::Tuple(elements) => match elements.last() {
+ None => tcx.types.unit,
+ Some(&self_ty) => {
+ ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)));
+ return ecx
+ .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+ }
+ },
+
+ ty::Infer(
+ ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
+ )
+ | ty::Bound(..) => bug!(
+ "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
+ goal.predicate.self_ty()
+ ),
+ };
+
+ ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())
+ .expect("expected goal term to be fully unconstrained");
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+
+ fn consider_builtin_future_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let self_ty = goal.predicate.self_ty();
+ let ty::Coroutine(def_id, args, _) = *self_ty.kind() else {
+ return Err(NoSolution);
+ };
+
+ // Coroutines are not futures unless they come from `async` desugaring
+ let tcx = ecx.tcx();
+ if !tcx.coroutine_is_async(def_id) {
+ return Err(NoSolution);
+ }
+
+ let term = args.as_coroutine().return_ty().into();
+
+ Self::consider_implied_clause(
+ ecx,
+ goal,
+ ty::ProjectionPredicate {
+ projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]),
+ term,
+ }
+ .to_predicate(tcx),
+ // Technically, we need to check that the future type is Sized,
+ // but that's already proven by the coroutine being WF.
+ [],
+ )
+ }
+
+ fn consider_builtin_iterator_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let self_ty = goal.predicate.self_ty();
+ let ty::Coroutine(def_id, args, _) = *self_ty.kind() else {
+ return Err(NoSolution);
+ };
+
+ // Coroutines are not Iterators unless they come from `gen` desugaring
+ let tcx = ecx.tcx();
+ if !tcx.coroutine_is_gen(def_id) {
+ return Err(NoSolution);
+ }
+
+ let term = args.as_coroutine().yield_ty().into();
+
+ Self::consider_implied_clause(
+ ecx,
+ goal,
+ ty::ProjectionPredicate {
+ projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]),
+ term,
+ }
+ .to_predicate(tcx),
+ // Technically, we need to check that the iterator type is Sized,
+ // but that's already proven by the generator being WF.
+ [],
+ )
+ }
+
+ fn consider_builtin_coroutine_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let self_ty = goal.predicate.self_ty();
+ let ty::Coroutine(def_id, args, _) = *self_ty.kind() else {
+ return Err(NoSolution);
+ };
+
+ // `async`-desugared coroutines do not implement the coroutine trait
+ let tcx = ecx.tcx();
+ if !tcx.is_general_coroutine(def_id) {
+ return Err(NoSolution);
+ }
+
+ let coroutine = args.as_coroutine();
+
+ let name = tcx.associated_item(goal.predicate.def_id()).name;
+ let term = if name == sym::Return {
+ coroutine.return_ty().into()
+ } else if name == sym::Yield {
+ coroutine.yield_ty().into()
+ } else {
+ bug!("unexpected associated item `<{self_ty} as Coroutine>::{name}`")
+ };
+
+ Self::consider_implied_clause(
+ ecx,
+ goal,
+ ty::ProjectionPredicate {
+ projection_ty: ty::AliasTy::new(
+ ecx.tcx(),
+ goal.predicate.def_id(),
+ [self_ty, coroutine.resume_ty()],
+ ),
+ term,
+ }
+ .to_predicate(tcx),
+ // Technically, we need to check that the coroutine type is Sized,
+ // but that's already proven by the coroutine being WF.
+ [],
+ )
+ }
+
+ fn consider_unsize_to_dyn_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`Unsize` does not have an associated type: {:?}", goal)
+ }
+
+ fn consider_structural_builtin_unsize_candidates(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
+ bug!("`Unsize` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_discriminant_kind_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let self_ty = goal.predicate.self_ty();
+ let discriminant_ty = match *self_ty.kind() {
+ ty::Bool
+ | ty::Char
+ | ty::Int(..)
+ | ty::Uint(..)
+ | ty::Float(..)
+ | ty::Array(..)
+ | ty::RawPtr(..)
+ | ty::Ref(..)
+ | ty::FnDef(..)
+ | ty::FnPtr(..)
+ | ty::Closure(..)
+ | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
+ | ty::Coroutine(..)
+ | ty::CoroutineWitness(..)
+ | ty::Never
+ | ty::Foreign(..)
+ | ty::Adt(_, _)
+ | ty::Str
+ | ty::Slice(_)
+ | ty::Dynamic(_, _, _)
+ | ty::Tuple(_)
+ | ty::Error(_) => self_ty.discriminant_ty(ecx.tcx()),
+
+ // We do not call `Ty::discriminant_ty` on alias, param, or placeholder
+ // types, which return `<self_ty as DiscriminantKind>::Discriminant`
+ // (or ICE in the case of placeholders). Projecting a type to itself
+ // is never really productive.
+ ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
+ return Err(NoSolution);
+ }
+
+ ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+ | ty::Bound(..) => bug!(
+ "unexpected self ty `{:?}` when normalizing `<T as DiscriminantKind>::Discriminant`",
+ goal.predicate.self_ty()
+ ),
+ };
+
+ ecx.probe_misc_candidate("builtin discriminant kind").enter(|ecx| {
+ ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
+ .expect("expected goal term to be fully unconstrained");
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+
+ fn consider_builtin_destruct_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`Destruct` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_transmute_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal)
+ }
+}
+
+/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
+///
+/// FIXME: We should merge these 3 implementations as it's likely that they otherwise
+/// diverge.
+#[instrument(level = "debug", skip(ecx, param_env), ret)]
+fn fetch_eligible_assoc_item_def<'tcx>(
+ ecx: &EvalCtxt<'_, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ goal_trait_ref: ty::TraitRef<'tcx>,
+ trait_assoc_def_id: DefId,
+ impl_def_id: DefId,
+) -> Result<Option<LeafDef>, NoSolution> {
+ let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id)
+ .map_err(|ErrorGuaranteed { .. }| NoSolution)?;
+
+ let eligible = 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 param_env.reveal() == Reveal::All {
+ let poly_trait_ref = ecx.resolve_vars_if_possible(goal_trait_ref);
+ !poly_trait_ref.still_further_specializable()
+ } else {
+ debug!(?node_item.item.def_id, "not eligible due to default");
+ false
+ }
+ };
+
+ if eligible { Ok(Some(node_item)) } else { Ok(None) }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs b/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs
new file mode 100644
index 000000000..ebd129f32
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs
@@ -0,0 +1,85 @@
+//! Computes a normalizes-to (projection) goal for opaque types. This goal
+//! behaves differently depending on the param-env's reveal mode and whether
+//! the opaque is in a defining scope.
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::traits::Reveal;
+use rustc_middle::ty;
+use rustc_middle::ty::util::NotUniqueParam;
+
+use crate::solve::{EvalCtxt, SolverMode};
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ pub(super) fn normalize_opaque_type(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let opaque_ty = goal.predicate.projection_ty;
+ let expected = goal.predicate.term.ty().expect("no such thing as an opaque const");
+
+ match (goal.param_env.reveal(), self.solver_mode()) {
+ (Reveal::UserFacing, SolverMode::Normal) => {
+ let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else {
+ return Err(NoSolution);
+ };
+ // FIXME: at some point we should call queries without defining
+ // new opaque types but having the existing opaque type definitions.
+ // This will require moving this below "Prefer opaques registered already".
+ if !self.can_define_opaque_ty(opaque_ty_def_id) {
+ return Err(NoSolution);
+ }
+ // FIXME: This may have issues when the args contain aliases...
+ match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.args) {
+ Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
+ return self.evaluate_added_goals_and_make_canonical_response(
+ Certainty::AMBIGUOUS,
+ );
+ }
+ Err(_) => {
+ return Err(NoSolution);
+ }
+ Ok(()) => {}
+ }
+ // Prefer opaques registered already.
+ let opaque_type_key =
+ ty::OpaqueTypeKey { def_id: opaque_ty_def_id, args: opaque_ty.args };
+ let matches =
+ self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected);
+ if !matches.is_empty() {
+ if let Some(response) = self.try_merge_responses(&matches) {
+ return Ok(response);
+ } else {
+ return self.flounder(&matches);
+ }
+ }
+ // Otherwise, define a new opaque type
+ self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
+ self.add_item_bounds_for_hidden_type(
+ opaque_ty.def_id,
+ opaque_ty.args,
+ goal.param_env,
+ expected,
+ );
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+ (Reveal::UserFacing, SolverMode::Coherence) => {
+ // An impossible opaque type bound is the only way this goal will fail
+ // e.g. assigning `impl Copy := NotCopy`
+ self.add_item_bounds_for_hidden_type(
+ opaque_ty.def_id,
+ opaque_ty.args,
+ goal.param_env,
+ expected,
+ );
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+ }
+ (Reveal::All, _) => {
+ // FIXME: Add an assertion that opaque type storage is empty.
+ let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, opaque_ty.args);
+ self.eq(goal.param_env, expected, actual)?;
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/weak_types.rs b/compiler/rustc_trait_selection/src/solve/project_goals/weak_types.rs
new file mode 100644
index 000000000..54de32cf6
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/project_goals/weak_types.rs
@@ -0,0 +1,34 @@
+//! Computes a normalizes-to (projection) goal for inherent associated types,
+//! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`.
+//!
+//! Since a weak alias is not ambiguous, this just computes the `type_of` of
+//! the alias and registers the where-clauses of the type alias.
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+use super::EvalCtxt;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ pub(super) fn normalize_weak_type(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let weak_ty = goal.predicate.projection_ty;
+ let expected = goal.predicate.term.ty().expect("no such thing as a const alias");
+
+ let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
+ self.eq(goal.param_env, expected, actual)?;
+
+ // Check where clauses
+ self.add_goals(
+ tcx.predicates_of(weak_ty.def_id)
+ .instantiate(tcx, weak_ty.args)
+ .predicates
+ .into_iter()
+ .map(|pred| goal.with(tcx, pred)),
+ );
+
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+}