//! Dealing with trait goals, i.e. `T: Trait<'a, U>`. use super::assembly::{self, structural_traits}; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::inspect::ProbeKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::traits::{BuiltinImplSource, Reveal}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, DUMMY_SP}; impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn self_ty(self) -> Ty<'tcx> { self.self_ty() } fn trait_ref(self, _: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { self.trait_ref } fn polarity(self) -> ty::ImplPolarity { self.polarity } 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::ForLookup }; if !drcx .args_refs_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) { return Err(NoSolution); } let impl_polarity = tcx.impl_polarity(impl_def_id); // An upper bound of the certainty of this goal, used to lower the certainty // of reservation impl to ambiguous during coherence. let maximal_certainty = match impl_polarity { ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => { match impl_polarity == goal.predicate.polarity { true => Certainty::Yes, false => return Err(NoSolution), } } ty::ImplPolarity::Reservation => match ecx.solver_mode() { SolverMode::Normal => return Err(NoSolution), SolverMode::Coherence => Certainty::AMBIGUOUS, }, }; ecx.probe_misc_candidate("impl").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.predicate.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); ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty) }) } fn consider_error_guaranteed_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, _guar: ErrorGuaranteed, ) -> QueryResult<'tcx> { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } 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(trait_clause) = assumption.as_trait_clause() { if trait_clause.def_id() == goal.predicate.def_id() && trait_clause.polarity() == goal.predicate.polarity { // FIXME: Constness ecx.probe_misc_candidate("assumption").enter(|ecx| { let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); ecx.eq( goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref, )?; then(ecx) }) } else { Err(NoSolution) } } else { Err(NoSolution) } } fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) { return result; } // Don't call `type_of` on a local TAIT that's in the defining scope, // since that may require calling `typeck` on the same item we're // currently type checking, which will result in a fatal cycle that // ideally we want to avoid, since we can make progress on this goal // via an alias bound or a locally-inferred hidden type instead. // // Also, don't call `type_of` on a TAIT in `Reveal::All` mode, since // we already normalize the self type in // `assemble_candidates_after_normalizing_self_ty`, and we'd // just be registering an identical candidate here. // // We always return `Err(NoSolution)` here in `SolverMode::Coherence` // since we'll always register an ambiguous candidate in // `assemble_candidates_after_normalizing_self_ty` due to normalizing // the TAIT. if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() { if matches!(goal.param_env.reveal(), Reveal::All) || matches!(ecx.solver_mode(), SolverMode::Coherence) || opaque_ty .def_id .as_local() .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id)) { 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> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } let tcx = ecx.tcx(); ecx.probe_misc_candidate("trait alias").enter(|ecx| { let nested_obligations = tcx .predicates_of(goal.predicate.def_id()) .instantiate(tcx, goal.predicate.trait_ref.args); ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p))); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } 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> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } 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.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } // The regions of a type don't affect the size of the type let tcx = ecx.tcx(); // We should erase regions from both the param-env and type, since both // may have infer regions. Specifically, after canonicalizing and instantiating, // early bound regions turn into region vars in both the new and old solver. let key = tcx.erase_regions(goal.param_env.and(goal.predicate.self_ty())); // But if there are inference variables, we have to wait until it's resolved. if key.has_non_region_infer() { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); } if let Ok(layout) = tcx.layout_of(key) && layout.layout.is_pointer_like(&tcx.data_layout) { // FIXME: We could make this faster by making a no-constraints response ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } } fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let self_ty = goal.predicate.self_ty(); match goal.predicate.polarity { ty::ImplPolarity::Positive => { if self_ty.is_fn_ptr() { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } } ty::ImplPolarity::Negative => { // If a type is rigid and not a fn ptr, then we know for certain // that it does *not* implement `FnPtr`. if !self_ty.is_fn_ptr() && self_ty.is_known_rigid() { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } } ty::ImplPolarity::Reservation => bug!(), } } fn consider_builtin_fn_trait_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> QueryResult<'tcx> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } 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(a) => a, 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, _)| { ty::TraitRef::new(tcx, 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 goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } if let ty::Tuple(..) = goal.predicate.self_ty().kind() { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } } fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } 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> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } let ty::Coroutine(def_id, _, _) = *goal.predicate.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); } // Async coroutine unconditionally implement `Future` // Technically, we need to check that the future output type is Sized, // but that's already proven by the coroutine being WF. ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } fn consider_builtin_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } let ty::Coroutine(def_id, _, _) = *goal.predicate.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); } // Gen coroutines unconditionally implement `Iterator` // Technically, we need to check that the iterator output type is Sized, // but that's already proven by the coroutines being WF. ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } 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(); Self::consider_implied_clause( ecx, goal, ty::TraitRef::new(tcx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()]) .to_predicate(tcx), // Technically, we need to check that the coroutine types are Sized, // but that's already proven by the coroutine being WF. [], ) } fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } // `DiscriminantKind` is automatically implemented for every type. 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> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver // `Destruct` is automatically implemented for every type in // non-const environments. ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } fn consider_builtin_transmute_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return Err(NoSolution); } // `rustc_transmute` does not have support for type or const params if goal.has_non_region_placeholders() { return Err(NoSolution); } // Erase regions because we compute layouts in `rustc_transmute`, // which will ICE for region vars. let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args); let Some(assume) = rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3)) else { return Err(NoSolution); }; let certainty = ecx.is_transmutable( rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, args.type_at(2), assume, )?; ecx.evaluate_added_goals_and_make_canonical_response(certainty) } fn consider_unsize_to_dyn_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| { let a_ty = goal.predicate.self_ty(); // We need to normalize the b_ty since it's destructured as a `dyn Trait`. let Some(b_ty) = ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))? else { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); }; let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { return Err(NoSolution); }; let tcx = ecx.tcx(); // Can only unsize to an object-safe trait. if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) { return Err(NoSolution); } // Check that the type implements all of the predicates of the trait object. // (i.e. the principal, all of the associated types match, and any auto traits) ecx.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty)))); // The type must be `Sized` to be unsized. if let Some(sized_def_id) = tcx.lang_items().sized_trait() { ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty]))); } else { return Err(NoSolution); } // The type must outlive the lifetime of the `dyn` we're unsizing into. ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region))); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } /// ```ignore (builtin impl example) /// trait Trait { /// fn foo(&self); /// } /// // results in the following builtin impl /// impl<'a, T: Trait + 'a> Unsize for T {} /// ``` fn consider_structural_builtin_unsize_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { if goal.predicate.polarity != ty::ImplPolarity::Positive { return vec![]; } let misc_candidate = |ecx: &mut EvalCtxt<'_, 'tcx>, certainty| { ( ecx.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(), BuiltinImplSource::Misc, ) }; let result_to_single = |result, source| match result { Ok(resp) => vec![(resp, source)], Err(NoSolution) => vec![], }; ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| { let a_ty = goal.predicate.self_ty(); // We need to normalize the b_ty since it's matched structurally // in the other functions below. let b_ty = match ecx .try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1)) { Ok(Some(b_ty)) => b_ty, Ok(None) => return vec![misc_candidate(ecx, Certainty::OVERFLOW)], Err(_) => return vec![], }; let goal = goal.with(ecx.tcx(), (a_ty, b_ty)); match (a_ty.kind(), b_ty.kind()) { (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"), (_, ty::Infer(ty::TyVar(..))) => vec![misc_candidate(ecx, Certainty::AMBIGUOUS)], // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`. ( &ty::Dynamic(a_data, a_region, ty::Dyn), &ty::Dynamic(b_data, b_region, ty::Dyn), ) => ecx.consider_builtin_dyn_upcast_candidates( goal, a_data, a_region, b_data, b_region, ), // `T` -> `dyn Trait` unsizing is handled separately in `consider_unsize_to_dyn_candidate` (_, &ty::Dynamic(..)) => vec![], // `[T; N]` -> `[T]` unsizing (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single( ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty), BuiltinImplSource::Misc, ), // `Struct` -> `Struct` where `T: Unsize` (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) if a_def.is_struct() && a_def == b_def => { result_to_single( ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args), BuiltinImplSource::Misc, ) } // `(A, B, T)` -> `(A, B, U)` where `T: Unsize` (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) if a_tys.len() == b_tys.len() && !a_tys.is_empty() => { result_to_single( ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys), BuiltinImplSource::TupleUnsizing, ) } _ => vec![], } }) } } impl<'tcx> EvalCtxt<'_, 'tcx> { /// Trait upcasting allows for coercions between trait objects: /// ```ignore (builtin impl example) /// trait Super {} /// trait Trait: Super {} /// // results in builtin impls upcasting to a super trait /// impl<'a, 'b: 'a> Unsize for dyn Trait + 'b {} /// // and impls removing auto trait bounds. /// impl<'a, 'b: 'a> Unsize for dyn Trait + Send + 'b {} /// ``` fn consider_builtin_dyn_upcast_candidates( &mut self, goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, a_data: &'tcx ty::List>, a_region: ty::Region<'tcx>, b_data: &'tcx ty::List>, b_region: ty::Region<'tcx>, ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { let tcx = self.tcx(); let Goal { predicate: (a_ty, _b_ty), .. } = goal; // 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 responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). if a_data.principal_def_id() == b_data.principal_def_id() { if let Ok(resp) = self.consider_builtin_upcast_to_principal( goal, a_data, a_region, b_data, b_region, a_data.principal(), ) { responses.push((resp, BuiltinImplSource::Misc)); } } else if let Some(a_principal) = a_data.principal() { self.walk_vtable( a_principal.with_self_ty(tcx, a_ty), |ecx, new_a_principal, _, vtable_vptr_slot| { if let Ok(resp) = ecx.probe_misc_candidate("dyn upcast").enter(|ecx| { ecx.consider_builtin_upcast_to_principal( goal, a_data, a_region, b_data, b_region, Some(new_a_principal.map_bound(|trait_ref| { ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) })), ) }) { responses .push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot })); } }, ); } responses } fn consider_builtin_upcast_to_principal( &mut self, goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, a_data: &'tcx ty::List>, a_region: ty::Region<'tcx>, b_data: &'tcx ty::List>, b_region: ty::Region<'tcx>, upcast_principal: Option>, ) -> QueryResult<'tcx> { let param_env = goal.param_env; // More than one projection in a_ty's bounds may match the projection // in b_ty's bound. Use this to first determine *which* apply without // having any inference side-effects. We process obligations because // unification may initially succeed due to deferred projection equality. let projection_may_match = |ecx: &mut Self, source_projection: ty::PolyExistentialProjection<'tcx>, target_projection: ty::PolyExistentialProjection<'tcx>| { source_projection.item_def_id() == target_projection.item_def_id() && ecx .probe(|_| ProbeKind::UpcastProjectionCompatibility) .enter(|ecx| -> Result<(), NoSolution> { ecx.eq(param_env, source_projection, target_projection)?; let _ = ecx.try_evaluate_added_goals()?; Ok(()) }) .is_ok() }; for bound in b_data { match bound.skip_binder() { // Check that a's supertrait (upcast_principal) is compatible // with the target (b_ty). ty::ExistentialPredicate::Trait(target_principal) => { self.eq(param_env, upcast_principal.unwrap(), bound.rebind(target_principal))?; } // Check that b_ty's projection is satisfied by exactly one of // a_ty's projections. First, we look through the list to see if // any match. If not, error. Then, if *more* than one matches, we // return ambiguity. Otherwise, if exactly one matches, equate // it with b_ty's projection. ty::ExistentialPredicate::Projection(target_projection) => { let target_projection = bound.rebind(target_projection); let mut matching_projections = a_data.projection_bounds().filter(|source_projection| { projection_may_match(self, *source_projection, target_projection) }); let Some(source_projection) = matching_projections.next() else { return Err(NoSolution); }; if matching_projections.next().is_some() { return self.evaluate_added_goals_and_make_canonical_response( Certainty::AMBIGUOUS, ); } self.eq(param_env, source_projection, target_projection)?; } // Check that b_ty's auto traits are present in a_ty's bounds. ty::ExistentialPredicate::AutoTrait(def_id) => { if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) { return Err(NoSolution); } } } } // Also require that a_ty's lifetime outlives b_ty's lifetime. self.add_goal(Goal::new( self.tcx(), param_env, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), )); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } /// We have the following builtin impls for arrays: /// ```ignore (builtin impl example) /// impl Unsize<[T]> for [T; N] {} /// ``` /// While the impl itself could theoretically not be builtin, /// the actual unsizing behavior is builtin. Its also easier to /// make all impls of `Unsize` builtin as we're able to use /// `#[rustc_deny_explicit_impl]` in this case. fn consider_builtin_array_unsize( &mut self, goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, a_elem_ty: Ty<'tcx>, b_elem_ty: Ty<'tcx>, ) -> QueryResult<'tcx> { self.eq(goal.param_env, a_elem_ty, b_elem_ty)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } /// We generate a builtin `Unsize` impls for structs with generic parameters only /// mentioned by the last field. /// ```ignore (builtin impl example) /// struct Foo { /// sized_field: Vec, /// unsizable: Box, /// } /// // results in the following builtin impl /// impl Unsize> for Foo /// where /// Box: Unsize>, /// {} /// ``` fn consider_builtin_struct_unsize( &mut self, goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, def: ty::AdtDef<'tcx>, a_args: ty::GenericArgsRef<'tcx>, b_args: ty::GenericArgsRef<'tcx>, ) -> QueryResult<'tcx> { let tcx = self.tcx(); let Goal { predicate: (_a_ty, b_ty), .. } = goal; let unsizing_params = tcx.unsizing_params_for_adt(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 = def.non_enum_variant().tail(); let tail_field_ty = tcx.type_of(tail_field.did); let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); // Substitute just the unsizing params from B into A. The type after // this substitution must be equal to B. This is so we don't unsize // unrelated type parameters. let new_a_args = tcx.mk_args_from_iter( a_args .iter() .enumerate() .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }), ); let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args); // Finally, we require that `TailA: Unsize` for the tail field // types. self.eq(goal.param_env, unsized_a_ty, b_ty)?; self.add_goal(goal.with( tcx, ty::TraitRef::new( tcx, tcx.lang_items().unsize_trait().unwrap(), [a_tail_ty, b_tail_ty], ), )); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } /// We generate the following builtin impl for tuples of all sizes. /// /// This impl is still unstable and we emit a feature error when it /// when it is used by a coercion. /// ```ignore (builtin impl example) /// impl Unsize<(T, V)> for (T, U) /// where /// U: Unsize, /// {} /// ``` fn consider_builtin_tuple_unsize( &mut self, goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, a_tys: &'tcx ty::List>, b_tys: &'tcx ty::List>, ) -> QueryResult<'tcx> { let tcx = self.tcx(); let Goal { predicate: (_a_ty, b_ty), .. } = goal; let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); let &b_last_ty = b_tys.last().unwrap(); // Substitute just the tail field of B., and require that they're equal. let unsized_a_ty = Ty::new_tup_from_iter(tcx, a_rest_tys.iter().copied().chain([b_last_ty])); self.eq(goal.param_env, unsized_a_ty, b_ty)?; // Similar to ADTs, require that we can unsize the tail. self.add_goal(goal.with( tcx, ty::TraitRef::new( tcx, tcx.lang_items().unsize_trait().unwrap(), [a_last_ty, b_last_ty], ), )); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // Return `Some` if there is an impl (built-in or user provided) that may // hold for the self type of the goal, which for coherence and soundness // purposes must disqualify the built-in auto impl assembled by considering // the type's constituent types. fn disqualify_auto_trait_candidate_due_to_possible_impl( &mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>, ) -> Option> { let self_ty = goal.predicate.self_ty(); match *self_ty.kind() { // Stall int and float vars until they are resolved to a concrete // numerical type. That's because the check for impls below treats // int vars as matching any impl. Even if we filtered such impls, // we probably don't want to treat an `impl !AutoTrait for i32` as // disqualifying the built-in auto impl for `i64: AutoTrait` either. ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)) } // These types cannot be structurally decomposed into constituent // types, and therefore have no built-in auto impl. ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..) | ty::Placeholder(..) => Some(Err(NoSolution)), ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"), // Coroutines have one special built-in candidate, `Unpin`, which // takes precedence over the structural auto trait candidate being // assembled. ty::Coroutine(_, _, movability) if Some(goal.predicate.def_id()) == self.tcx().lang_items().unpin_trait() => { match movability { Movability::Static => Some(Err(NoSolution)), Movability::Movable => { Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } } } // For rigid types, any possible implementation that could apply to // the type (even if after unification and processing nested goals // it does not hold) will disqualify the built-in auto impl. // // 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. ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Array(_, _) | ty::Slice(_) | ty::RawPtr(_) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _) | ty::Coroutine(_, _, _) | ty::CoroutineWitness(..) | ty::Never | ty::Tuple(_) | ty::Adt(_, _) // FIXME: Handling opaques here is kinda sus. Especially because we // simplify them to SimplifiedType::Placeholder. | ty::Alias(ty::Opaque, _) => { let mut disqualifying_impl = None; self.tcx().for_each_relevant_impl_treating_projections( goal.predicate.def_id(), goal.predicate.self_ty(), TreatProjections::NextSolverLookup, |impl_def_id| { disqualifying_impl = Some(impl_def_id); }, ); if let Some(def_id) = disqualifying_impl { debug!(?def_id, ?goal, "disqualified auto-trait implementation"); // No need to actually consider the candidate here, // since we do that in `consider_impl_candidate`. return Some(Err(NoSolution)); } else { None } } ty::Error(_) => None, } } /// 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_misc_candidate("constituent tys").enter(|ecx| { ecx.add_goals( constituent_tys(ecx, goal.predicate.self_ty())? .into_iter() .map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty))) .collect::>(), ); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } #[instrument(level = "debug", skip(self))] 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(candidates) } }