From 4f9fe856a25ab29345b90e7725509e9ee38a37be Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:41 +0200 Subject: Adding upstream version 1.69.0+dfsg1. Signed-off-by: Daniel Baumann --- .../rustc_trait_selection/src/solve/trait_goals.rs | 468 ++++++++++++++++----- 1 file changed, 364 insertions(+), 104 deletions(-) (limited to 'compiler/rustc_trait_selection/src/solve/trait_goals.rs') diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 9985d7181..5c499c36e 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,15 +2,15 @@ use std::iter; -use super::assembly::{self, Candidate, CandidateSource}; -use super::infcx_ext::InferCtxtExt; -use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; +use super::assembly; +use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; -use rustc_infer::infer::InferCtxt; +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, TypeVisitable}; +use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; use rustc_span::DUMMY_SP; pub mod structural_traits; @@ -43,12 +43,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return Err(NoSolution); } - ecx.infcx.probe(|_| { - let impl_substs = ecx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + 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.infcx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; + 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) @@ -60,21 +60,65 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } - fn consider_assumption( + 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() { + 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.infcx.probe(|_| { + ecx.probe(|ecx| { let assumption_trait_pred = - ecx.infcx.instantiate_bound_vars_with_infer(poly_trait_pred); - let nested_goals = ecx.infcx.eq( + 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 { @@ -86,6 +130,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { 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, @@ -98,7 +156,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); - ecx.infcx.probe(|_| { + ecx.probe(|ecx| { let nested_obligations = tcx .predicates_of(goal.predicate.def_id()) .instantiate(tcx, goal.predicate.trait_ref.substs); @@ -128,12 +186,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } - fn consider_builtin_pointer_sized_candidate( + 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::Maybe(MaybeCause::Ambiguity)); + return ecx.make_canonical_response(Certainty::AMBIGUOUS); } let tcx = ecx.tcx(); @@ -156,23 +214,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> QueryResult<'tcx> { - if let Some(tupled_inputs_and_output) = + let tcx = ecx.tcx(); + let Some(tupled_inputs_and_output) = structural_traits::extract_tupled_inputs_and_output_from_callable( - ecx.tcx(), + tcx, goal.predicate.self_ty(), goal_kind, - )? - { - let pred = tupled_inputs_and_output - .map_bound(|(inputs, _)| { - ecx.tcx() - .mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) - }) - .to_predicate(ecx.tcx()); - Self::consider_assumption(ecx, goal, pred) - } else { - ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)) - } + )? 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( @@ -185,6 +246,272 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { 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> { @@ -195,16 +522,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn probe_and_evaluate_goal_for_constituent_tys( &mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>, - constituent_tys: impl Fn(&InferCtxt<'tcx>, Ty<'tcx>) -> Result>, NoSolution>, + constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result>, NoSolution>, ) -> QueryResult<'tcx> { - self.infcx.probe(|_| { - self.evaluate_all_and_make_canonical_response( - constituent_tys(self.infcx, goal.predicate.self_ty())? + self.probe(|this| { + this.evaluate_all_and_make_canonical_response( + constituent_tys(this, goal.predicate.self_ty())? .into_iter() .map(|ty| { goal.with( - self.tcx(), - ty::Binder::dummy(goal.predicate.with_self_ty(self.tcx(), ty)), + this.tcx(), + ty::Binder::dummy(goal.predicate.with_self_ty(this.tcx(), ty)), ) }) .collect(), @@ -217,73 +544,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, TraitPredicate<'tcx>>, ) -> QueryResult<'tcx> { let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_trait_candidates_discard_reservation_impls(candidates) - } - - #[instrument(level = "debug", skip(self), ret)] - pub(super) fn merge_trait_candidates_discard_reservation_impls( - &mut self, - mut candidates: Vec>, - ) -> QueryResult<'tcx> { - match candidates.len() { - 0 => return Err(NoSolution), - 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result), - _ => {} - } - - if candidates.len() > 1 { - let mut i = 0; - 'outer: while i < candidates.len() { - for j in (0..candidates.len()).filter(|&j| i != j) { - if self.trait_candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - ) { - debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); - candidates.swap_remove(i); - continue 'outer; - } - } - - debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - i += 1; - if i > 1 { - debug!("multiple matches, ambig"); - // FIXME: return overflow if all candidates overflow, otherwise return ambiguity. - unimplemented!(); - } - } - } - - Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result) - } - - fn trait_candidate_should_be_dropped_in_favor_of( - &self, - candidate: &Candidate<'tcx>, - other: &Candidate<'tcx>, - ) -> bool { - // FIXME: implement this - match (candidate.source, other.source) { - (CandidateSource::Impl(_), _) - | (CandidateSource::ParamEnv(_), _) - | (CandidateSource::AliasBound(_), _) - | (CandidateSource::BuiltinImpl, _) => unimplemented!(), - } - } - - fn discard_reservation_impl(&self, candidate: Candidate<'tcx>) -> Candidate<'tcx> { - if let CandidateSource::Impl(def_id) = candidate.source { - if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) { - debug!("Selected reservation impl"); - // FIXME: reduce candidate to ambiguous - // FIXME: replace `var_values` with identity, yeet external constraints. - unimplemented!() - } - } - - candidate + self.merge_candidates_and_discard_reservation_impls(candidates) } } -- cgit v1.2.3