//! Dealing with trait goals, i.e. `T: Trait<'a, U>`. use std::iter; use super::assembly; use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; use rustc_span::DUMMY_SP; pub mod structural_traits; impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn self_ty(self) -> Ty<'tcx> { self.self_ty() } 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, _: TyCtxt<'tcx>) -> DefId { self.def_id() } fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, TraitPredicate<'tcx>>, impl_def_id: DefId, ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs) .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) { return Err(NoSolution); } ecx.probe(|ecx| { let impl_substs = ecx.fresh_substs_for_item(impl_def_id); let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); let mut nested_goals = ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; let where_clause_bounds = tcx .predicates_of(impl_def_id) .instantiate(tcx, impl_substs) .predicates .into_iter() .map(|pred| goal.with(tcx, pred)); nested_goals.extend(where_clause_bounds); ecx.evaluate_all_and_make_canonical_response(nested_goals) }) } fn consider_implied_clause( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, requirements: impl IntoIterator>>, ) -> QueryResult<'tcx> { if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() && poly_trait_pred.def_id() == goal.predicate.def_id() { // FIXME: Constness and polarity ecx.probe(|ecx| { let assumption_trait_pred = ecx.instantiate_binder_with_infer(poly_trait_pred); let mut nested_goals = ecx.eq( goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref, )?; nested_goals.extend(requirements); ecx.evaluate_all_and_make_canonical_response(nested_goals) }) } else { Err(NoSolution) } } fn consider_object_bound_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, ) -> QueryResult<'tcx> { if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() && poly_trait_pred.def_id() == goal.predicate.def_id() { // FIXME: Constness and polarity ecx.probe(|ecx| { let assumption_trait_pred = ecx.instantiate_binder_with_infer(poly_trait_pred); let mut nested_goals = ecx.eq( goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref, )?; let tcx = ecx.tcx(); let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { bug!("expected object type in `consider_object_bound_candidate`"); }; nested_goals.extend( structural_traits::predicates_for_object_candidate( ecx, goal.param_env, goal.predicate.trait_ref, bounds, ) .into_iter() .map(|pred| goal.with(tcx, pred)), ); ecx.evaluate_all_and_make_canonical_response(nested_goals) }) } else { Err(NoSolution) } } fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { // This differs from the current stable behavior and // fixes #84857. Due to breakage found via crater, we // currently instead lint patterns which can be used to // exploit this unsoundness on stable, see #93367 for // more details. if let Some(def_id) = ecx.tcx().find_map_relevant_impl( goal.predicate.def_id(), goal.predicate.self_ty(), Some, ) { debug!(?def_id, ?goal, "disqualified auto-trait implementation"); return Err(NoSolution); } ecx.probe_and_evaluate_goal_for_constituent_tys( goal, structural_traits::instantiate_constituent_tys_for_auto_trait, ) } fn consider_trait_alias_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); ecx.probe(|ecx| { let nested_obligations = tcx .predicates_of(goal.predicate.def_id()) .instantiate(tcx, goal.predicate.trait_ref.substs); ecx.evaluate_all_and_make_canonical_response( nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)).collect(), ) }) } fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { ecx.probe_and_evaluate_goal_for_constituent_tys( goal, structural_traits::instantiate_constituent_tys_for_sized_trait, ) } fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { ecx.probe_and_evaluate_goal_for_constituent_tys( goal, structural_traits::instantiate_constituent_tys_for_copy_clone_trait, ) } fn consider_builtin_pointer_like_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.self_ty().has_non_region_infer() { return ecx.make_canonical_response(Certainty::AMBIGUOUS); } let tcx = ecx.tcx(); let self_ty = tcx.erase_regions(goal.predicate.self_ty()); if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty)) && let usize_layout = tcx.layout_of(ty::ParamEnv::empty().and(tcx.types.usize)).unwrap().layout && layout.layout.size() == usize_layout.size() && layout.layout.align().abi == usize_layout.align().abi { // FIXME: We could make this faster by making a no-constraints response ecx.make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } } 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 Some(tupled_inputs_and_output) = structural_traits::extract_tupled_inputs_and_output_from_callable( tcx, goal.predicate.self_ty(), goal_kind, )? else { return ecx.make_canonical_response(Certainty::AMBIGUOUS); }; let output_is_sized_pred = tupled_inputs_and_output .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); let pred = tupled_inputs_and_output .map_bound(|(inputs, _)| { tcx.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) }) .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> { if let ty::Tuple(..) = goal.predicate.self_ty().kind() { ecx.make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } } fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, _goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { ecx.make_canonical_response(Certainty::Yes) } fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let ty::Generator(def_id, _, _) = *goal.predicate.self_ty().kind() else { return Err(NoSolution); }; // Generators are not futures unless they come from `async` desugaring let tcx = ecx.tcx(); if !tcx.generator_is_async(def_id) { return Err(NoSolution); } // Async generator unconditionally implement `Future` // Technically, we need to check that the future output type is Sized, // but that's already proven by the generator being WF. ecx.make_canonical_response(Certainty::Yes) } fn consider_builtin_generator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let self_ty = goal.predicate.self_ty(); let ty::Generator(def_id, substs, _) = *self_ty.kind() else { return Err(NoSolution); }; // `async`-desugared generators do not implement the generator trait let tcx = ecx.tcx(); if tcx.generator_is_async(def_id) { return Err(NoSolution); } let generator = substs.as_generator(); Self::consider_implied_clause( ecx, goal, ty::Binder::dummy( tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]), ) .to_predicate(tcx), // Technically, we need to check that the generator types are Sized, // but that's already proven by the generator being WF. [], ) } fn consider_builtin_unsize_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); let a_ty = goal.predicate.self_ty(); let b_ty = goal.predicate.trait_ref.substs.type_at(1); if b_ty.is_ty_var() { return ecx.make_canonical_response(Certainty::AMBIGUOUS); } ecx.probe(|ecx| { match (a_ty.kind(), b_ty.kind()) { // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { // Dyn upcasting is handled separately, since due to upcasting, // when there are two supertraits that differ by substs, we // may return more than one query response. return Err(NoSolution); } // `T` -> `dyn Trait` unsizing (_, &ty::Dynamic(data, region, ty::Dyn)) => { // Can only unsize to an object-safe type if data .principal_def_id() .map_or(false, |def_id| !tcx.check_is_object_safe(def_id)) { return Err(NoSolution); } let Some(sized_def_id) = tcx.lang_items().sized_trait() else { return Err(NoSolution); }; let nested_goals: Vec<_> = data .iter() // Check that the type implements all of the predicates of the def-id. // (i.e. the principal, all of the associated types match, and any auto traits) .map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))) .chain([ // The type must be Sized to be unsized. goal.with( tcx, ty::Binder::dummy(tcx.mk_trait_ref(sized_def_id, [a_ty])), ), // The type must outlive the lifetime of the `dyn` we're unsizing into. goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))), ]) .collect(); ecx.evaluate_all_and_make_canonical_response(nested_goals) } // `[T; n]` -> `[T]` unsizing (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { // We just require that the element type stays the same let nested_goals = ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?; ecx.evaluate_all_and_make_canonical_response(nested_goals) } // Struct unsizing `Struct` -> `Struct` where `T: Unsize` (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def.is_struct() && a_def.did() == b_def.did() => { let unsizing_params = tcx.unsizing_params_for_adt(a_def.did()); // We must be unsizing some type parameters. This also implies // that the struct has a tail field. if unsizing_params.is_empty() { return Err(NoSolution); } let tail_field = a_def .non_enum_variant() .fields .last() .expect("expected unsized ADT to have a tail field"); let tail_field_ty = tcx.type_of(tail_field.did); let a_tail_ty = tail_field_ty.subst(tcx, a_substs); let b_tail_ty = tail_field_ty.subst(tcx, b_substs); // Substitute just the unsizing params from B into A. The type after // this substitution must be equal to B. This is so we don't unsize // unrelated type parameters. let new_a_substs = tcx.mk_substs_from_iter(a_substs.iter().enumerate().map(|(i, a)| { if unsizing_params.contains(i as u32) { b_substs[i] } else { a } })); let unsized_a_ty = tcx.mk_adt(a_def, new_a_substs); // Finally, we require that `TailA: Unsize` for the tail field // types. let mut nested_goals = ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; nested_goals.push(goal.with( tcx, ty::Binder::dummy( tcx.mk_trait_ref(goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), ), )); ecx.evaluate_all_and_make_canonical_response(nested_goals) } // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize` (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) if a_tys.len() == b_tys.len() && !a_tys.is_empty() => { let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); let b_last_ty = b_tys.last().unwrap(); // Substitute just the tail field of B., and require that they're equal. let unsized_a_ty = tcx.mk_tup_from_iter(a_rest_tys.iter().chain([b_last_ty]).copied()); let mut nested_goals = ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; // Similar to ADTs, require that the rest of the fields are equal. nested_goals.push(goal.with( tcx, ty::Binder::dummy( tcx.mk_trait_ref(goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), ), )); ecx.evaluate_all_and_make_canonical_response(nested_goals) } _ => Err(NoSolution), } }) } fn consider_builtin_dyn_upcast_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> Vec> { let tcx = ecx.tcx(); let a_ty = goal.predicate.self_ty(); let b_ty = goal.predicate.trait_ref.substs.type_at(1); let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { return vec![]; }; let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { return vec![]; }; // All of a's auto traits need to be in b's auto traits. let auto_traits_compatible = b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); if !auto_traits_compatible { return vec![]; } let mut unsize_dyn_to_principal = |principal: Option>| { ecx.probe(|ecx| -> Result<_, NoSolution> { // Require that all of the trait predicates from A match B, except for // the auto traits. We do this by constructing a new A type with B's // auto traits, and equating these types. let new_a_data = principal .into_iter() .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) .chain(a_data.iter().filter(|a| { matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) })) .chain( b_data .auto_traits() .map(ty::ExistentialPredicate::AutoTrait) .map(ty::Binder::dummy), ); let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn); // We also require that A's lifetime outlives B's lifetime. let mut nested_obligations = ecx.eq(goal.param_env, new_a_ty, b_ty)?; nested_obligations.push( goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))), ); ecx.evaluate_all_and_make_canonical_response(nested_obligations) }) }; let mut responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). if a_data.principal_def_id() == b_data.principal_def_id() { if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { responses.push(response); } } else if let Some(a_principal) = a_data.principal() && let Some(b_principal) = b_data.principal() { for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { if super_trait_ref.def_id() != b_principal.def_id() { continue; } let erased_trait_ref = super_trait_ref .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { responses.push(response); } } } responses } fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, _goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { // `DiscriminantKind` is automatically implemented for every type. ecx.make_canonical_response(Certainty::Yes) } } impl<'tcx> EvalCtxt<'_, 'tcx> { /// Convenience function for traits that are structural, i.e. that only /// have nested subgoals that only change the self type. Unlike other /// evaluate-like helpers, this does a probe, so it doesn't need to be /// wrapped in one. fn probe_and_evaluate_goal_for_constituent_tys( &mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>, constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result>, NoSolution>, ) -> QueryResult<'tcx> { self.probe(|this| { this.evaluate_all_and_make_canonical_response( constituent_tys(this, goal.predicate.self_ty())? .into_iter() .map(|ty| { goal.with( this.tcx(), ty::Binder::dummy(goal.predicate.with_self_ty(this.tcx(), ty)), ) }) .collect(), ) }) } pub(super) fn compute_trait_goal( &mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>, ) -> QueryResult<'tcx> { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates_and_discard_reservation_impls(candidates) } }