diff options
Diffstat (limited to 'compiler/rustc_trait_selection')
40 files changed, 2719 insertions, 1806 deletions
diff --git a/compiler/rustc_trait_selection/locales/en-US.ftl b/compiler/rustc_trait_selection/messages.ftl index 14eb4a550..14eb4a550 100644 --- a/compiler/rustc_trait_selection/locales/en-US.ftl +++ b/compiler/rustc_trait_selection/messages.ftl diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 9b47c7299..911cc0b88 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -8,26 +8,16 @@ use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryRes use rustc_middle::traits::query::Fallible; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::ty::{GenericArg, ToPredicate}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::DUMMY_SP; use std::fmt::Debug; pub use rustc_infer::infer::*; pub trait InferCtxtExt<'tcx> { - fn type_is_copy_modulo_regions( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - span: Span, - ) -> bool; + fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool; - fn type_is_sized_modulo_regions( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - span: Span, - ) -> bool; + fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool; /// Check whether a `ty` implements given trait(trait_def_id). /// The inputs are: @@ -46,13 +36,9 @@ pub trait InferCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> traits::EvaluationResult; } + impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { - fn type_is_copy_modulo_regions( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - span: Span, - ) -> bool { + fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let ty = self.resolve_vars_if_possible(ty); if !(param_env, ty).needs_infer() { @@ -65,17 +51,12 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { // rightly refuses to work with inference variables, but // moves_by_default has a cache, which we want to use in other // cases. - traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span) + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) } - fn type_is_sized_modulo_regions( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - span: Span, - ) -> bool { + fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); - traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item, span) + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item) } #[instrument(level = "debug", skip(self, params), ret)] diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 548b42cef..f866cb016 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -44,4 +44,4 @@ pub mod infer; pub mod solve; pub mod traits; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index dec9f8016..10d817f75 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -1,16 +1,21 @@ //! Code shared by trait and projection goals for candidate assembly. -#[cfg(doc)] -use super::trait_goals::structural_traits::*; -use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; -use itertools::Itertools; +use super::search_graph::OverflowHandler; +use super::{EvalCtxt, SolverMode}; +use crate::solve::CanonicalResponseExt; +use crate::traits::coherence; +use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::util::elaborate_predicates; +use rustc_infer::traits::util::elaborate; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; use std::fmt::Debug; +pub(super) mod structural_traits; + /// A candidate is a possible way to prove a goal. /// /// It consists of both the `source`, which describes how that goal would be proven, @@ -85,6 +90,8 @@ pub(super) enum CandidateSource { pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq { fn self_ty(self) -> Ty<'tcx>; + fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>; + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; @@ -148,6 +155,12 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + // A type is a `FnPtr` if it is of `FnPtr` type. + fn consider_builtin_fn_ptr_trait_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + // A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>` // family of traits where `A` is given by the signature of the type. fn consider_builtin_fn_trait_candidates( @@ -207,6 +220,16 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + + fn consider_builtin_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + fn consider_builtin_transmute_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -222,7 +245,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if goal.predicate.self_ty().is_ty_var() { return vec![Candidate { source: CandidateSource::BuiltinImpl, - result: self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(), + result: self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + .unwrap(), }]; } @@ -240,14 +265,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.assemble_object_bound_candidates(goal, &mut candidates); + self.assemble_coherence_unknowable_candidates(goal, &mut candidates); + candidates } /// If the self type of a goal is a projection, computing the relevant candidates is difficult. /// /// To deal with this, we first try to normalize the self type and add the candidates for the normalized - /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in - /// this case as projections as self types add ` + /// self type to the list of candidates in case that succeeds. We also have to consider candidates with the + /// projection as a self type as well + #[instrument(level = "debug", skip_all)] fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, @@ -258,48 +286,52 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else { return }; - self.probe(|this| { - let normalized_ty = this.next_ty_infer(); - let normalizes_to_goal = goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), - ); - let normalization_certainty = match this.evaluate_goal(normalizes_to_goal) { - Ok((_, certainty)) => certainty, - Err(NoSolution) => return, - }; - let normalized_ty = this.resolve_vars_if_possible(normalized_ty); - - // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. - // This doesn't work as long as we use `CandidateSource` in winnowing. - let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); - let normalized_candidates = this.assemble_and_evaluate_candidates(goal); - for mut normalized_candidate in normalized_candidates { - normalized_candidate.result = - normalized_candidate.result.unchecked_map(|mut response| { - // FIXME: This currently hides overflow in the normalization step of the self type - // which is probably wrong. Maybe `unify_and` should actually keep overflow as - // we treat it as non-fatal anyways. - response.certainty = response.certainty.unify_and(normalization_certainty); - response - }); - candidates.push(normalized_candidate); - } - }) + + let normalized_self_candidates: Result<_, NoSolution> = self.probe(|ecx| { + ecx.with_incremented_depth( + |ecx| { + let result = ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::Maybe(MaybeCause::Overflow), + )?; + Ok(vec![Candidate { source: CandidateSource::BuiltinImpl, result }]) + }, + |ecx| { + let normalized_ty = ecx.next_ty_infer(); + let normalizes_to_goal = goal.with( + tcx, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + ecx.add_goal(normalizes_to_goal); + let _ = ecx.try_evaluate_added_goals()?; + let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty); + // NOTE: Alternatively we could call `evaluate_goal` here and only + // have a `Normalized` candidate. This doesn't work as long as we + // use `CandidateSource` in winnowing. + let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); + Ok(ecx.assemble_and_evaluate_candidates(goal)) + }, + ) + }); + + if let Ok(normalized_self_candidates) = normalized_self_candidates { + candidates.extend(normalized_self_candidates); + } } + #[instrument(level = "debug", skip_all)] fn assemble_impl_candidates<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, candidates: &mut Vec<Candidate<'tcx>>, ) { let tcx = self.tcx(); - tcx.for_each_relevant_impl( + tcx.for_each_relevant_impl_treating_projections( goal.predicate.trait_def_id(tcx), goal.predicate.self_ty(), + TreatProjections::NextSolverLookup, |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) { Ok(result) => candidates .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), @@ -308,6 +340,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ); } + #[instrument(level = "debug", skip_all)] fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, @@ -315,6 +348,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) { let lang_items = self.tcx().lang_items(); let trait_def_id = goal.predicate.trait_def_id(self.tcx()); + + // N.B. When assembling built-in candidates for lang items that are also + // `auto` traits, then the auto trait candidate that is assembled in + // `consider_auto_trait_candidate` MUST be disqualified to remain sound. + // + // Instead of adding the logic here, it's a better idea to add it in + // `EvalCtxt::disqualify_auto_trait_candidate_due_to_possible_impl` in + // `solve::trait_goals` instead. let result = if self.tcx().trait_is_auto(trait_def_id) { G::consider_auto_trait_candidate(self, goal) } else if self.tcx().trait_is_alias(trait_def_id) { @@ -327,6 +368,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_copy_clone_candidate(self, goal) } else if lang_items.pointer_like() == Some(trait_def_id) { G::consider_builtin_pointer_like_candidate(self, goal) + } else if lang_items.fn_ptr_trait() == Some(trait_def_id) { + G::consider_builtin_fn_ptr_trait_candidate(self, goal) } else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) { G::consider_builtin_fn_trait_candidates(self, goal, kind) } else if lang_items.tuple_trait() == Some(trait_def_id) { @@ -341,6 +384,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_unsize_candidate(self, goal) } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { G::consider_builtin_discriminant_kind_candidate(self, goal) + } else if lang_items.destruct_trait() == Some(trait_def_id) { + G::consider_builtin_destruct_candidate(self, goal) + } else if lang_items.transmute_trait() == Some(trait_def_id) { + G::consider_builtin_transmute_candidate(self, goal) } else { Err(NoSolution) }; @@ -361,6 +408,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + #[instrument(level = "debug", skip_all)] fn assemble_param_env_candidates<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, @@ -376,6 +424,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + #[instrument(level = "debug", skip_all)] fn assemble_alias_bound_candidates<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, @@ -423,6 +472,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + #[instrument(level = "debug", skip_all)] fn assemble_object_bound_candidates<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, @@ -461,10 +511,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }; let tcx = self.tcx(); - for assumption in - elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty))) + let own_bounds: FxIndexSet<_> = + bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect(); + for assumption in elaborate(tcx, own_bounds.iter().copied()) + // we only care about bounds that match the `Self` type + .filter_only_self() { - match G::consider_object_bound_candidate(self, goal, assumption.predicate) { + // FIXME: Predicates are fully elaborated in the object type's existential bounds + // list. We want to only consider these pre-elaborated projections, and not other + // projection predicates that we reach by elaborating the principal trait ref, + // since that'll cause ambiguity. + // + // We can remove this when we have implemented intersections in responses. + if assumption.to_opt_poly_projection_pred().is_some() + && !own_bounds.contains(&assumption) + { + continue; + } + + match G::consider_object_bound_candidate(self, goal, assumption) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) } @@ -473,78 +538,68 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - #[instrument(level = "debug", skip(self), ret)] - pub(super) fn merge_candidates_and_discard_reservation_impls( + #[instrument(level = "debug", skip_all)] + fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>( &mut self, - mut candidates: Vec<Candidate<'tcx>>, - ) -> 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; - } + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + match self.solver_mode() { + SolverMode::Normal => return, + SolverMode::Coherence => { + let trait_ref = goal.predicate.trait_ref(self.tcx()); + match coherence::trait_ref_is_knowable(self.tcx(), trait_ref) { + Ok(()) => {} + Err(_) => match self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + { + Ok(result) => candidates + .push(Candidate { source: CandidateSource::BuiltinImpl, result }), + // FIXME: This will be reachable at some point if we're in + // `assemble_candidates_after_normalizing_self_ty` and we get a + // universe error. We'll deal with it at this point. + Err(NoSolution) => bug!("coherence candidate resulted in NoSolution"), + }, } - - debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); - i += 1; - } - - // If there are *STILL* multiple candidates that have *different* response - // results, give up and report ambiguity. - if candidates.len() > 1 && !candidates.iter().map(|cand| cand.result).all_equal() { - let certainty = if candidates.iter().all(|x| { - matches!(x.result.value.certainty, Certainty::Maybe(MaybeCause::Overflow)) - }) { - Certainty::Maybe(MaybeCause::Overflow) - } else { - Certainty::AMBIGUOUS - }; - return self.make_canonical_response(certainty); } } - - // FIXME: What if there are >1 candidates left with the same response, and one is a reservation impl? - 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, _) => false, + /// If there are multiple ways to prove a trait or projection goal, we have + /// to somehow try to merge the candidates into one. If that fails, we return + /// ambiguity. + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn merge_candidates( + &mut self, + mut candidates: Vec<Candidate<'tcx>>, + ) -> QueryResult<'tcx> { + // First try merging all candidates. This is complete and fully sound. + let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>(); + if let Some(result) = self.try_merge_responses(&responses) { + return Ok(result); } - } - fn discard_reservation_impl(&self, mut 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"); - // We assemble all candidates inside of a probe so by - // making a new canonical response here our result will - // have no constraints. - candidate.result = self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(); + // We then check whether we should prioritize `ParamEnv` candidates. + // + // Doing so is incomplete and would therefore be unsound during coherence. + match self.solver_mode() { + SolverMode::Coherence => (), + // Prioritize `ParamEnv` candidates only if they do not guide inference. + // + // This is still incomplete as we may add incorrect region bounds. + SolverMode::Normal => { + let param_env_responses = candidates + .iter() + .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) + .map(|c| c.result) + .collect::<Vec<_>>(); + if let Some(result) = self.try_merge_responses(¶m_env_responses) { + if result.has_only_region_constraints() { + return Ok(result); + } + } } } - - candidate + self.flounder(&responses) } } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index d7d93377c..1a566e87d 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -1,7 +1,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::{def_id::DefId, Movability, Mutability}; use rustc_infer::traits::query::NoSolution; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, +}; use crate::solve::EvalCtxt; @@ -9,7 +11,7 @@ use crate::solve::EvalCtxt; // // For types with an "existential" binder, i.e. generator witnesses, we also // instantiate the binder with placeholders eagerly. -pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>( +pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ecx: &EvalCtxt<'_, 'tcx>, ty: Ty<'tcx>, ) -> Result<Vec<Ty<'tcx>>, NoSolution> { @@ -22,21 +24,19 @@ pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>( | ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) - | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Never | ty::Char => Ok(vec![]), - // Treat this like `struct str([u8]);` + // Treat `str` like it's defined as `struct str([u8]);` ty::Str => Ok(vec![tcx.mk_slice(tcx.types.u8)]), ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) | ty::Alias(ty::Projection, ..) - | ty::Placeholder(..) => Err(NoSolution), - - ty::Bound(..) - | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(_) => { bug!("unexpected type `{ty}`") } @@ -60,7 +60,16 @@ pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::GeneratorWitness(types) => Ok(ecx.instantiate_binder_with_placeholders(types).to_vec()), - ty::GeneratorWitnessMIR(..) => todo!(), + ty::GeneratorWitnessMIR(def_id, substs) => Ok(ecx + .tcx() + .generator_hidden_types(def_id) + .map(|bty| { + ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars( + tcx, + bty.subst(tcx, substs), + )) + }) + .collect()), // For `PhantomData<T>`, we pass `T`. ty::Adt(def, substs) if def.is_phantom_data() => Ok(vec![substs.type_at(0)]), @@ -76,7 +85,28 @@ pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>( } } -pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>( +pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> ty::Binder<'tcx, Ty<'tcx>> { + debug_assert!(!ty.has_late_bound_regions()); + let mut counter = 0; + let ty = tcx.fold_regions(ty, |mut r, current_depth| { + if let ty::ReErased = r.kind() { + let br = + ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon(None) }; + counter += 1; + r = tcx.mk_re_late_bound(current_depth, br); + } + r + }); + let bound_vars = tcx.mk_bound_variable_kinds_from_iter( + (0..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon(None))), + ); + ty::Binder::bind_with_vars(ty, bound_vars) +} + +pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( ecx: &EvalCtxt<'_, 'tcx>, ty: Ty<'tcx>, ) -> Result<Vec<Ty<'tcx>>, NoSolution> { @@ -126,7 +156,7 @@ pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>( } } -pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( +pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( ecx: &EvalCtxt<'_, 'tcx>, ty: Ty<'tcx>, ) -> Result<Vec<Ty<'tcx>>, NoSolution> { @@ -178,29 +208,65 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( ty::GeneratorWitness(types) => Ok(ecx.instantiate_binder_with_placeholders(types).to_vec()), - ty::GeneratorWitnessMIR(..) => todo!(), + ty::GeneratorWitnessMIR(def_id, substs) => Ok(ecx + .tcx() + .generator_hidden_types(def_id) + .map(|bty| { + ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars( + ecx.tcx(), + bty.subst(ecx.tcx(), substs), + )) + }) + .collect()), } } // Returns a binder of the tupled inputs types and output type from a builtin callable type. -pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>( +pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>, goal_kind: ty::ClosureKind, ) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> { match *self_ty.kind() { - ty::FnDef(def_id, substs) => Ok(Some( - tcx.fn_sig(def_id) - .subst(tcx, substs) - .map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())), - )), - ty::FnPtr(sig) => Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())))), + // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. + ty::FnDef(def_id, substs) => { + let sig = tcx.fn_sig(def_id); + if sig.skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + Ok(Some( + sig.subst(tcx, substs) + .map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())), + )) + } else { + Err(NoSolution) + } + } + // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. + ty::FnPtr(sig) => { + if sig.is_fn_trait_compatible() { + Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())))) + } else { + Err(NoSolution) + } + } ty::Closure(_, substs) => { let closure_substs = substs.as_closure(); match closure_substs.kind_ty().to_opt_closure_kind() { - Some(closure_kind) if closure_kind.extends(goal_kind) => {} - None => return Ok(None), - _ => return Err(NoSolution), + // If the closure's kind doesn't extend the goal kind, + // then the closure doesn't implement the trait. + Some(closure_kind) => { + if !closure_kind.extends(goal_kind) { + return Err(NoSolution); + } + } + // Closure kind is not yet determined, so we return ambiguity unless + // the expected kind is `FnOnce` as that is always implemented. + None => { + if goal_kind != ty::ClosureKind::FnOnce { + return Ok(None); + } + } } Ok(Some(closure_substs.sig().map_bound(|sig| (sig.inputs()[0], sig.output())))) } @@ -269,7 +335,13 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>( /// additional step of eagerly folding the associated types in the where /// clauses of the impl. In this example, that means replacing /// `<Self as Foo>::Bar` with `Ty` in the first impl. -pub(crate) fn predicates_for_object_candidate<'tcx>( +/// +// FIXME: This is only necessary as `<Self as Trait>::Assoc: ItemBound` +// bounds in impls are trivially proven using the item bound candidates. +// This is unsound in general and once that is fixed, we don't need to +// normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9 +// for more details. +pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( ecx: &EvalCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, trait_ref: ty::TraitRef<'tcx>, @@ -333,7 +405,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> { // FIXME: Technically this folder could be fallible? let nested = self .ecx - .eq(self.param_env, alias_ty, proj.projection_ty) + .eq_and_get_goals(self.param_env, alias_ty, proj.projection_ty) .expect("expected to be able to unify goal projection with dyn's projection"); // FIXME: Technically we could register these too.. assert!(nested.is_empty(), "did not expect unification to have any nested goals"); diff --git a/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs index c048d4a2a..976849696 100644 --- a/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -/// Whether we're canonicalizing a query input or the query reponse. +/// Whether we're canonicalizing a query input or the query response. /// /// When canonicalizing an input we're in the context of the caller /// while canonicalizing the response happens in the context of the @@ -21,7 +21,7 @@ use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; #[derive(Debug, Clone, Copy)] pub enum CanonicalizeMode { Input, - /// FIXME: We currently return region constraints refering to + /// FIXME: We currently return region constraints referring to /// placeholders and inference variables from a binder instantiated /// inside of the query. /// @@ -125,8 +125,9 @@ impl<'a, 'tcx> Canonicalizer<'a, 'tcx> { // - var_infos: [E0, U1, E1, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 6 // - var_infos: [E0, U1, E1, U1, E1, E2, U2], curr_compressed_uv: 2, next_orig_uv: - // - // This algorithm runs in `O(n²)` where `n` is the number of different universe - // indices in the input. This should be fine as `n` is expected to be small. + // This algorithm runs in `O(nm)` where `n` is the number of different universe + // indices in the input and `m` is the number of canonical variables. + // This should be fine as both `n` and `m` are expected to be small. let mut curr_compressed_uv = ty::UniverseIndex::ROOT; let mut existential_in_new_uv = false; let mut next_orig_uv = Some(ty::UniverseIndex::ROOT); @@ -245,42 +246,49 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { ty::ReError(_) => return r, }; - let existing_bound_var = match self.canonicalize_mode { - CanonicalizeMode::Input => None, - CanonicalizeMode::Response { .. } => { - self.variables.iter().position(|&v| v == r.into()).map(ty::BoundVar::from) - } - }; - let var = existing_bound_var.unwrap_or_else(|| { - let var = ty::BoundVar::from(self.variables.len()); - self.variables.push(r.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }); - let br = ty::BoundRegion { var, kind: BrAnon(var.as_u32(), None) }; + let var = ty::BoundVar::from( + self.variables.iter().position(|&v| v == r.into()).unwrap_or_else(|| { + let var = self.variables.len(); + self.variables.push(r.into()); + self.primitive_var_infos.push(CanonicalVarInfo { kind }); + var + }), + ); + let br = ty::BoundRegion { var, kind: BrAnon(None) }; self.interner().mk_re_late_bound(self.binder_index, br) } - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> { let kind = match *t.kind() { - ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) { - Ok(t) => return self.fold_ty(t), - Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), - }, - ty::Infer(ty::IntVar(_)) => { - let nt = self.infcx.shallow_resolve(t); + ty::Infer(ty::TyVar(mut vid)) => { + // We need to canonicalize the *root* of our ty var. + // This is so that our canonical response correctly reflects + // any equated inference vars correctly! + let root_vid = self.infcx.root_var(vid); + if root_vid != vid { + t = self.infcx.tcx.mk_ty_var(root_vid); + vid = root_vid; + } + + match self.infcx.probe_ty_var(vid) { + Ok(t) => return self.fold_ty(t), + Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), + } + } + ty::Infer(ty::IntVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_int_var(vid); if nt != t { return self.fold_ty(nt); } else { CanonicalVarKind::Ty(CanonicalTyVarKind::Int) } } - ty::Infer(ty::FloatVar(_)) => { - let nt = self.infcx.shallow_resolve(t); + ty::Infer(ty::FloatVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_float_var(vid); if nt != t { return self.fold_ty(nt); } else { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int) + CanonicalVarKind::Ty(CanonicalTyVarKind::Float) } } ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { @@ -289,14 +297,20 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { ty::Placeholder(placeholder) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderTy(ty::Placeholder { universe: placeholder.universe, - name: BoundTyKind::Anon(self.variables.len() as u32), + bound: ty::BoundTy { + var: ty::BoundVar::from_usize(self.variables.len()), + kind: ty::BoundTyKind::Anon, + }, }), CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder), }, ty::Param(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderTy(ty::Placeholder { universe: ty::UniverseIndex::ROOT, - name: ty::BoundTyKind::Anon(self.variables.len() as u32), + bound: ty::BoundTy { + var: ty::BoundVar::from_usize(self.variables.len()), + kind: ty::BoundTyKind::Anon, + }, }), CanonicalizeMode::Response { .. } => bug!("param ty in response: {t:?}"), }, @@ -334,17 +348,27 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { var }), ); - let bt = ty::BoundTy { var, kind: BoundTyKind::Anon(var.index() as u32) }; + let bt = ty::BoundTy { var, kind: BoundTyKind::Anon }; self.interner().mk_bound(self.binder_index, bt) } - fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> { let kind = match c.kind() { - ty::ConstKind::Infer(ty::InferConst::Var(vid)) => match self.infcx.probe_const_var(vid) - { - Ok(c) => return self.fold_const(c), - Err(universe) => CanonicalVarKind::Const(universe, c.ty()), - }, + ty::ConstKind::Infer(ty::InferConst::Var(mut vid)) => { + // We need to canonicalize the *root* of our const var. + // This is so that our canonical response correctly reflects + // any equated inference vars correctly! + let root_vid = self.infcx.root_const_var(vid); + if root_vid != vid { + c = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), c.ty()); + vid = root_vid; + } + + match self.infcx.probe_const_var(vid) { + Ok(c) => return self.fold_const(c), + Err(universe) => CanonicalVarKind::Const(universe, c.ty()), + } + } ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => { bug!("fresh var during canonicalization: {c:?}") } @@ -352,7 +376,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( ty::Placeholder { universe: placeholder.universe, - name: ty::BoundVar::from(self.variables.len()), + bound: ty::BoundVar::from(self.variables.len()), }, c.ty(), ), @@ -364,7 +388,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( ty::Placeholder { universe: ty::UniverseIndex::ROOT, - name: ty::BoundVar::from(self.variables.len()), + bound: ty::BoundVar::from(self.variables.len()), }, c.ty(), ), diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 95612674e..c29b5b04e 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -2,10 +2,13 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; +use rustc_infer::infer::{ + DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt, +}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -13,12 +16,32 @@ use rustc_middle::ty::{ use rustc_span::DUMMY_SP; use std::ops::ControlFlow; -use super::search_graph::SearchGraph; -use super::Goal; +use crate::traits::specialization_graph; + +use super::search_graph::{self, OverflowHandler}; +use super::SolverMode; +use super::{search_graph::SearchGraph, Goal}; + +mod canonical; pub struct EvalCtxt<'a, 'tcx> { - // FIXME: should be private. - pub(super) infcx: &'a InferCtxt<'tcx>, + /// The inference context that backs (mostly) inference and placeholder terms + /// instantiated while solving goals. + /// + /// NOTE: The `InferCtxt` that backs the `EvalCtxt` is intentionally private, + /// because the `InferCtxt` is much more general than `EvalCtxt`. Methods such + /// as `take_registered_region_obligations` can mess up query responses, + /// using `At::normalize` is totally wrong, calling `evaluate_root_goal` can + /// cause coinductive unsoundness, etc. + /// + /// Methods that are generally of use for trait solving are *intentionally* + /// re-declared through the `EvalCtxt` below, often with cleaner signatures + /// since we don't care about things like `ObligationCause`s and `Span`s here. + /// If some `InferCtxt` method is missing, please first think defensively about + /// the method's compatibility with this solver, or if an existing one does + /// the job already. + infcx: &'a InferCtxt<'tcx>, + pub(super) var_values: CanonicalVarValues<'tcx>, /// The highest universe index nameable by the caller. /// @@ -33,14 +56,356 @@ pub struct EvalCtxt<'a, 'tcx> { pub(super) search_graph: &'a mut SearchGraph<'tcx>, - /// This field is used by a debug assertion in [`EvalCtxt::evaluate_goal`], - /// see the comment in that method for more details. - pub in_projection_eq_hack: bool, + pub(super) nested_goals: NestedGoals<'tcx>, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub(super) enum IsNormalizesToHack { + Yes, + No, +} + +#[derive(Debug, Clone)] +pub(super) struct NestedGoals<'tcx> { + /// This normalizes-to goal that is treated specially during the evaluation + /// loop. In each iteration we take the RHS of the projection, replace it with + /// a fresh inference variable, and only after evaluating that goal do we + /// equate the fresh inference variable with the actual RHS of the predicate. + /// + /// This is both to improve caching, and to avoid using the RHS of the + /// projection predicate to influence the normalizes-to candidate we select. + /// + /// This is not a 'real' nested goal. We must not forget to replace the RHS + /// with a fresh inference variable when we evaluate this goal. That can result + /// in a trait solver cycle. This would currently result in overflow but can be + /// can be unsound with more powerful coinduction in the future. + pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::ProjectionPredicate<'tcx>>>, + /// The rest of the goals which have not yet processed or remain ambiguous. + pub(super) goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, +} + +impl NestedGoals<'_> { + pub(super) fn new() -> Self { + Self { normalizes_to_hack_goal: None, goals: Vec::new() } + } + + pub(super) fn is_empty(&self) -> bool { + self.normalizes_to_hack_goal.is_none() && self.goals.is_empty() + } +} + +pub trait InferCtxtEvalExt<'tcx> { + /// Evaluates a goal from **outside** of the trait solver. + /// + /// Using this while inside of the solver is wrong as it uses a new + /// search graph which would break cycle detection. + fn evaluate_root_goal( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>; +} + +impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { + #[instrument(level = "debug", skip(self), ret)] + fn evaluate_root_goal( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { + let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; + let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode); + + let mut ecx = EvalCtxt { + search_graph: &mut search_graph, + infcx: self, + // Only relevant when canonicalizing the response. + max_input_universe: ty::UniverseIndex::ROOT, + var_values: CanonicalVarValues::dummy(), + nested_goals: NestedGoals::new(), + }; + let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal); + + assert!( + ecx.nested_goals.is_empty(), + "root `EvalCtxt` should not have any goals added to it" + ); + + assert!(search_graph.is_empty()); + result + } +} + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + pub(super) fn solver_mode(&self) -> SolverMode { + self.search_graph.solver_mode() + } + + /// The entry point of the solver. + /// + /// This function deals with (coinductive) cycles, overflow, and caching + /// and then calls [`EvalCtxt::compute_goal`] which contains the actual + /// logic of the solver. + /// + /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal] + /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're + /// outside of it. + #[instrument(level = "debug", skip(tcx, search_graph), ret)] + fn evaluate_canonical_goal( + tcx: TyCtxt<'tcx>, + search_graph: &'a mut search_graph::SearchGraph<'tcx>, + canonical_goal: CanonicalGoal<'tcx>, + ) -> QueryResult<'tcx> { + // Deal with overflow, caching, and coinduction. + // + // The actual solver logic happens in `ecx.compute_goal`. + search_graph.with_new_goal(tcx, canonical_goal, |search_graph| { + let intercrate = match search_graph.solver_mode() { + SolverMode::Normal => false, + SolverMode::Coherence => true, + }; + let (ref infcx, goal, var_values) = tcx + .infer_ctxt() + .intercrate(intercrate) + .build_with_canonical(DUMMY_SP, &canonical_goal); + let mut ecx = EvalCtxt { + infcx, + var_values, + max_input_universe: canonical_goal.max_universe, + search_graph, + nested_goals: NestedGoals::new(), + }; + ecx.compute_goal(goal) + }) + } + + /// Recursively evaluates `goal`, returning whether any inference vars have + /// been constrained and the certainty of the result. + fn evaluate_goal( + &mut self, + is_normalizes_to_hack: IsNormalizesToHack, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { + let (orig_values, canonical_goal) = self.canonicalize_goal(goal); + let canonical_response = + EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; + + let has_changed = !canonical_response.value.var_values.is_identity(); + let (certainty, nested_goals) = self.instantiate_and_apply_query_response( + goal.param_env, + orig_values, + canonical_response, + )?; + + if !has_changed && !nested_goals.is_empty() { + bug!("an unchanged goal shouldn't have any side-effects on instantiation"); + } + + // Check that rerunning this query with its inference constraints applied + // doesn't result in new inference constraints and has the same result. + // + // If we have projection goals like `<T as Trait>::Assoc == u32` we recursively + // call `exists<U> <T as Trait>::Assoc == U` to enable better caching. This goal + // could constrain `U` to `u32` which would cause this check to result in a + // solver cycle. + if cfg!(debug_assertions) + && has_changed + && is_normalizes_to_hack == IsNormalizesToHack::No + && !self.search_graph.in_cycle() + { + debug!("rerunning goal to check result is stable"); + let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); + let canonical_response = + EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; + if !canonical_response.value.var_values.is_identity() { + bug!( + "unstable result: re-canonicalized goal={canonical_goal:#?} \ + response={canonical_response:#?}" + ); + } + if certainty != canonical_response.value.certainty { + bug!( + "unstable certainty: {certainty:#?} re-canonicalized goal={canonical_goal:#?} \ + response={canonical_response:#?}" + ); + } + } + + Ok((has_changed, certainty, nested_goals)) + } + + fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> { + let Goal { param_env, predicate } = goal; + let kind = predicate.kind(); + if let Some(kind) = kind.no_bound_vars() { + match kind { + ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => { + self.compute_trait_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => { + self.compute_projection_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => { + self.compute_type_outlives_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => { + self.compute_region_outlives_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) }) + } + ty::PredicateKind::Subtype(predicate) => { + self.compute_subtype_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Coerce(predicate) => { + self.compute_coerce_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::ClosureKind(def_id, substs, kind) => self + .compute_closure_kind_goal(Goal { + param_env, + predicate: (def_id, substs, kind), + }), + ty::PredicateKind::ObjectSafe(trait_def_id) => { + self.compute_object_safe_goal(trait_def_id) + } + ty::PredicateKind::WellFormed(arg) => { + self.compute_well_formed_goal(Goal { param_env, predicate: arg }) + } + ty::PredicateKind::Ambiguous => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + // FIXME: implement these predicates :) + ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + ty::PredicateKind::TypeWellFormedFromEnv(..) => { + bug!("TypeWellFormedFromEnv is only used for Chalk") + } + ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self + .compute_alias_relate_goal(Goal { + param_env, + predicate: (lhs, rhs, direction), + }), + } + } else { + let kind = self.infcx.instantiate_binder_with_placeholders(kind); + let goal = goal.with(self.tcx(), ty::Binder::dummy(kind)); + self.add_goal(goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + + // Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning + // the certainty of all the goals. + #[instrument(level = "debug", skip(self))] + pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> { + let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); + let mut new_goals = NestedGoals::new(); + + let response = self.repeat_while_none( + |_| Ok(Certainty::Maybe(MaybeCause::Overflow)), + |this| { + let mut has_changed = Err(Certainty::Yes); + + if let Some(goal) = goals.normalizes_to_hack_goal.take() { + // Replace the goal with an unconstrained infer var, so the + // RHS does not affect projection candidate assembly. + let unconstrained_rhs = this.next_term_infer_of_kind(goal.predicate.term); + let unconstrained_goal = goal.with( + this.tcx(), + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: goal.predicate.projection_ty, + term: unconstrained_rhs, + }), + ); + + let (_, certainty, instantiate_goals) = + match this.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal) { + Ok(r) => r, + Err(NoSolution) => return Some(Err(NoSolution)), + }; + new_goals.goals.extend(instantiate_goals); + + // Finally, equate the goal's RHS with the unconstrained var. + // We put the nested goals from this into goals instead of + // next_goals to avoid needing to process the loop one extra + // time if this goal returns something -- I don't think this + // matters in practice, though. + match this.eq_and_get_goals( + goal.param_env, + goal.predicate.term, + unconstrained_rhs, + ) { + Ok(eq_goals) => { + goals.goals.extend(eq_goals); + } + Err(NoSolution) => return Some(Err(NoSolution)), + }; + + // We only look at the `projection_ty` part here rather than + // looking at the "has changed" return from evaluate_goal, + // because we expect the `unconstrained_rhs` part of the predicate + // to have changed -- that means we actually normalized successfully! + if goal.predicate.projection_ty + != this.resolve_vars_if_possible(goal.predicate.projection_ty) + { + has_changed = Ok(()) + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + // We need to resolve vars here so that we correctly + // deal with `has_changed` in the next iteration. + new_goals.normalizes_to_hack_goal = + Some(this.resolve_vars_if_possible(goal)); + has_changed = has_changed.map_err(|c| c.unify_with(certainty)); + } + } + } + + for goal in goals.goals.drain(..) { + let (changed, certainty, instantiate_goals) = + match this.evaluate_goal(IsNormalizesToHack::No, goal) { + Ok(result) => result, + Err(NoSolution) => return Some(Err(NoSolution)), + }; + new_goals.goals.extend(instantiate_goals); + + if changed { + has_changed = Ok(()); + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + new_goals.goals.push(goal); + has_changed = has_changed.map_err(|c| c.unify_with(certainty)); + } + } + } + + core::mem::swap(&mut new_goals, &mut goals); + match has_changed { + Ok(()) => None, + Err(certainty) => Some(Ok(certainty)), + } + }, + ); + + self.nested_goals = goals; + response + } } impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn probe<T>(&mut self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T { - self.infcx.probe(|_| f(self)) + let mut ecx = EvalCtxt { + infcx: self.infcx, + var_values: self.var_values, + max_input_universe: self.max_input_universe, + search_graph: self.search_graph, + nested_goals: self.nested_goals.clone(), + }; + self.infcx.probe(|_| f(&mut ecx)) } pub(super) fn tcx(&self) -> TyCtxt<'tcx> { @@ -61,6 +426,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) } + /// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`. + /// If `kind` is an integer inference variable this will still return a ty infer var. + pub(super) fn next_term_infer_of_kind(&self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> { + match kind.unpack() { + ty::TermKind::Ty(_) => self.next_ty_infer().into(), + ty::TermKind::Const(ct) => self.next_const_infer(ct.ty()).into(), + } + } + /// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`. /// /// This is the case if the `term` is an inference variable in the innermost universe @@ -74,7 +448,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if let &ty::Infer(ty::TyVar(vid)) = ty.kind() { match self.infcx.probe_ty_var(vid) { Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"), - Err(universe) => universe == self.universe(), + Err(universe) => universe == self.infcx.universe(), } } else { false @@ -84,7 +458,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() { match self.infcx.probe_const_var(vid) { Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"), - Err(universe) => universe == self.universe(), + Err(universe) => universe == self.infcx.universe(), } } else { false @@ -93,37 +467,42 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }; // Guard against `<T as Trait<?0>>::Assoc = ?0>`. - struct ContainsTerm<'tcx> { + struct ContainsTerm<'a, 'tcx> { term: ty::Term<'tcx>, + infcx: &'a InferCtxt<'tcx>, } - impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTerm<'tcx> { + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTerm<'_, 'tcx> { type BreakTy = (); fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if t.needs_infer() { - if ty::Term::from(t) == self.term { - ControlFlow::Break(()) - } else { - t.super_visit_with(self) - } + if let Some(vid) = t.ty_vid() + && let ty::TermKind::Ty(term) = self.term.unpack() + && let Some(term_vid) = term.ty_vid() + && self.infcx.root_var(vid) == self.infcx.root_var(term_vid) + { + ControlFlow::Break(()) + } else if t.has_non_region_infer() { + t.super_visit_with(self) } else { ControlFlow::Continue(()) } } fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - if c.needs_infer() { - if ty::Term::from(c) == self.term { - ControlFlow::Break(()) - } else { - c.super_visit_with(self) - } + if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = c.kind() + && let ty::TermKind::Const(term) = self.term.unpack() + && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind() + && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid) + { + ControlFlow::Break(()) + } else if c.has_non_region_infer() { + c.super_visit_with(self) } else { ControlFlow::Continue(()) } } } - let mut visitor = ContainsTerm { term: goal.predicate.term }; + let mut visitor = ContainsTerm { infcx: self.infcx, term: goal.predicate.term }; term_is_infer && goal.predicate.projection_ty.visit_with(&mut visitor).is_continue() @@ -132,6 +511,49 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self, param_env), ret)] pub(super) fn eq<T: ToTrace<'tcx>>( + &mut self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + rhs: T, + ) -> Result<(), NoSolution> { + self.infcx + .at(&ObligationCause::dummy(), param_env) + .eq(DefineOpaqueTypes::No, lhs, rhs) + .map(|InferOk { value: (), obligations }| { + self.add_goals(obligations.into_iter().map(|o| o.into())); + }) + .map_err(|e| { + debug!(?e, "failed to equate"); + NoSolution + }) + } + + #[instrument(level = "debug", skip(self, param_env), ret)] + pub(super) fn sub<T: ToTrace<'tcx>>( + &mut self, + param_env: ty::ParamEnv<'tcx>, + sub: T, + sup: T, + ) -> Result<(), NoSolution> { + self.infcx + .at(&ObligationCause::dummy(), param_env) + .sub(DefineOpaqueTypes::No, sub, sup) + .map(|InferOk { value: (), obligations }| { + self.add_goals(obligations.into_iter().map(|o| o.into())); + }) + .map_err(|e| { + debug!(?e, "failed to subtype"); + NoSolution + }) + } + + /// Equates two values returning the nested goals without adding them + /// to the nested goals of the `EvalCtxt`. + /// + /// If possible, try using `eq` instead which automatically handles nested + /// goals correctly. + #[instrument(level = "trace", skip(self, param_env), ret)] + pub(super) fn eq_and_get_goals<T: ToTrace<'tcx>>( &self, param_env: ty::ParamEnv<'tcx>, lhs: T, @@ -139,7 +561,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { self.infcx .at(&ObligationCause::dummy(), param_env) - .eq(lhs, rhs) + .eq(DefineOpaqueTypes::No, lhs, rhs) .map(|InferOk { value: (), obligations }| { obligations.into_iter().map(|o| o.into()).collect() }) @@ -178,7 +600,64 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.infcx.fresh_substs_for_item(DUMMY_SP, def_id) } - pub(super) fn universe(&self) -> ty::UniverseIndex { - self.infcx.universe() + pub(super) fn translate_substs( + &self, + param_env: ty::ParamEnv<'tcx>, + source_impl: DefId, + source_substs: ty::SubstsRef<'tcx>, + target_node: specialization_graph::Node, + ) -> ty::SubstsRef<'tcx> { + crate::traits::translate_substs( + self.infcx, + param_env, + source_impl, + source_substs, + target_node, + ) + } + + pub(super) fn register_ty_outlives(&self, ty: Ty<'tcx>, lt: ty::Region<'tcx>) { + self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy()); + } + + pub(super) fn register_region_outlives(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) { + // `b : a` ==> `a <= b` + // (inlined from `InferCtxt::region_outlives_predicate`) + self.infcx.sub_regions( + rustc_infer::infer::SubregionOrigin::RelateRegionParamBound(DUMMY_SP), + b, + a, + ); + } + + /// Computes the list of goals required for `arg` to be well-formed + pub(super) fn well_formed_goals( + &self, + param_env: ty::ParamEnv<'tcx>, + arg: ty::GenericArg<'tcx>, + ) -> Option<impl Iterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>> { + crate::traits::wf::unnormalized_obligations(self.infcx, param_env, arg) + .map(|obligations| obligations.into_iter().map(|obligation| obligation.into())) + } + + pub(super) fn is_transmutable( + &self, + src_and_dst: rustc_transmute::Types<'tcx>, + scope: Ty<'tcx>, + assume: rustc_transmute::Assume, + ) -> Result<Certainty, NoSolution> { + // FIXME(transmutability): This really should be returning nested goals for `Answer::If*` + match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( + ObligationCause::dummy(), + ty::Binder::dummy(src_and_dst), + scope, + assume, + ) { + rustc_transmute::Answer::Yes => Ok(Certainty::Yes), + rustc_transmute::Answer::No(_) + | rustc_transmute::Answer::IfTransmutable { .. } + | rustc_transmute::Answer::IfAll(_) + | rustc_transmute::Answer::IfAny(_) => Err(NoSolution), + } } } diff --git a/compiler/rustc_trait_selection/src/solve/canonical/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 8c3be8da1..ada868705 100644 --- a/compiler/rustc_trait_selection/src/solve/canonical/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -8,22 +8,19 @@ /// section of the [rustc-dev-guide][c]. /// /// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html -use self::canonicalize::{CanonicalizeMode, Canonicalizer}; use super::{CanonicalGoal, Certainty, EvalCtxt, Goal}; -use super::{CanonicalResponse, ExternalConstraints, QueryResult, Response}; +use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; +use crate::solve::{CanonicalResponse, QueryResult, Response}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; -use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::ExternalConstraintsData; -use rustc_infer::traits::ObligationCause; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData}; use rustc_middle::ty::{self, GenericArgKind}; use rustc_span::DUMMY_SP; use std::iter; use std::ops::Deref; -mod canonicalize; - impl<'tcx> EvalCtxt<'_, 'tcx> { /// Canonicalizes the goal remembering the original values /// for each bound variable. @@ -45,10 +42,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// /// - `var_values`: a map from bound variables in the canonical goal to /// the values inferred while solving the instantiated goal. - /// - `external_constraints`: additional constraints which aren't expressable + /// - `external_constraints`: additional constraints which aren't expressible /// using simple unification of inference variables. #[instrument(level = "debug", skip(self))] - pub(super) fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> { + pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( + &mut self, + certainty: Certainty, + ) -> QueryResult<'tcx> { + let goals_certainty = self.try_evaluate_added_goals()?; + let certainty = certainty.unify_with(goals_certainty); + let external_constraints = self.compute_external_query_constraints()?; let response = Response { var_values: self.var_values, external_constraints, certainty }; @@ -93,24 +96,24 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { param_env: ty::ParamEnv<'tcx>, original_values: Vec<ty::GenericArg<'tcx>>, response: CanonicalResponse<'tcx>, - ) -> Result<Certainty, NoSolution> { + ) -> Result<(Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { let substitution = self.compute_query_response_substitution(&original_values, &response); let Response { var_values, external_constraints, certainty } = response.substitute(self.tcx(), &substitution); - self.unify_query_var_values(param_env, &original_values, var_values)?; + let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?; // FIXME: implement external constraints. let ExternalConstraintsData { region_constraints, opaque_types: _ } = external_constraints.deref(); self.register_region_constraints(region_constraints); - Ok(certainty) + Ok((certainty, nested_goals)) } /// This returns the substitutions to instantiate the bound variables of - /// the canonical reponse. This depends on the `original_values` for the + /// the canonical response. This depends on the `original_values` for the /// bound variables. fn compute_query_response_substitution( &self, @@ -185,7 +188,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } else { // For placeholders which were already part of the input, we simply map this // universal bound variable back the placeholder of the input. - original_values[info.expect_anon_placeholder() as usize] + original_values[info.expect_placeholder_index()] } }, )); @@ -199,35 +202,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { param_env: ty::ParamEnv<'tcx>, original_values: &[ty::GenericArg<'tcx>], var_values: CanonicalVarValues<'tcx>, - ) -> Result<(), NoSolution> { + ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { assert_eq!(original_values.len(), var_values.len()); + + let mut nested_goals = vec![]; for (&orig, response) in iter::zip(original_values, var_values.var_values) { - // This can fail due to the occurs check, see - // `tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs` for an example - // where that can happen. - // - // FIXME: To deal with #105787 I also expect us to emit nested obligations here at - // some point. We can figure out how to deal with this once we actually have - // an ICE. - let nested_goals = self.eq(param_env, orig, response)?; - assert!(nested_goals.is_empty(), "{nested_goals:?}"); + nested_goals.extend(self.eq_and_get_goals(param_env, orig, response)?); } - Ok(()) + Ok(nested_goals) } fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstraints<'tcx>) { for &(ty::OutlivesPredicate(lhs, rhs), _) in ®ion_constraints.outlives { match lhs.unpack() { - GenericArgKind::Lifetime(lhs) => self.infcx.region_outlives_predicate( - &ObligationCause::dummy(), - ty::Binder::dummy(ty::OutlivesPredicate(lhs, rhs)), - ), - GenericArgKind::Type(lhs) => self.infcx.register_region_obligation_with_cause( - lhs, - rhs, - &ObligationCause::dummy(), - ), + GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs), + GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs), GenericArgKind::Const(_) => bug!("const outlives: {lhs:?}: {rhs:?}"), } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index a55b984fd..32bd10f0b 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -1,6 +1,8 @@ use std::mem; use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::solve::MaybeCause; +use rustc_infer::traits::Obligation; use rustc_infer::traits::{ query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, PredicateObligation, SelectionError, TraitEngine, @@ -40,13 +42,31 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { self.obligations.push(obligation); } - fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> { + fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { self.obligations .drain(..) - .map(|obligation| FulfillmentError { - obligation: obligation.clone(), - code: FulfillmentErrorCode::CodeAmbiguity, - root_obligation: obligation, + .map(|obligation| { + let code = + infcx.probe(|_| match infcx.evaluate_root_goal(obligation.clone().into()) { + Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => { + FulfillmentErrorCode::CodeAmbiguity { overflow: false } + } + Ok((_, Certainty::Maybe(MaybeCause::Overflow), _)) => { + FulfillmentErrorCode::CodeAmbiguity { overflow: true } + } + Ok((_, Certainty::Yes, _)) => { + bug!("did not expect successful goal when collecting ambiguity errors") + } + Err(_) => { + bug!("did not expect selection error when collecting ambiguity errors") + } + }); + + FulfillmentError { + obligation: obligation.clone(), + code, + root_obligation: obligation, + } }) .collect() } @@ -61,7 +81,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut has_changed = false; for obligation in mem::take(&mut self.obligations) { let goal = obligation.clone().into(); - let (changed, certainty) = match infcx.evaluate_root_goal(goal) { + let (changed, certainty, nested_goals) = match infcx.evaluate_root_goal(goal) { Ok(result) => result, Err(NoSolution) => { errors.push(FulfillmentError { @@ -73,7 +93,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { MismatchedProjectionTypes { err: TypeError::Mismatch }, ) } - ty::PredicateKind::AliasEq(_, _) => { + ty::PredicateKind::AliasRelate(_, _, _) => { FulfillmentErrorCode::CodeProjectionError( MismatchedProjectionTypes { err: TypeError::Mismatch }, ) @@ -125,7 +145,16 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { continue; } }; - + // Push any nested goals that we get from unifying our canonical response + // with our obligation onto the fulfillment context. + self.obligations.extend(nested_goals.into_iter().map(|goal| { + Obligation::new( + infcx.tcx, + obligation.cause.clone(), + goal.param_env, + goal.predicate, + ) + })); has_changed |= changed; match certainty { Certainty::Yes => {} @@ -149,6 +178,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { &mut self, _: &InferCtxt<'tcx>, ) -> Vec<PredicateObligation<'tcx>> { - unimplemented!() + std::mem::take(&mut self.obligations) } } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 57b6a4527..19bcbd461 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -9,81 +9,45 @@ //! FIXME(@lcnr): Write that section. If you read this before then ask me //! about it on zulip. -// FIXME: Instead of using `infcx.canonicalize_query` we have to add a new routine which -// preserves universes and creates a unique var (in the highest universe) for each -// appearance of a region. - -// FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented. - -use std::mem; - use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::Obligation; -use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData}; +use rustc_middle::traits::solve::{ + CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult, Response, +}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ - CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate, + CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; -use rustc_span::DUMMY_SP; - -use crate::solve::search_graph::OverflowHandler; -use crate::traits::ObligationCause; mod assembly; -mod canonical; +mod canonicalize; mod eval_ctxt; mod fulfill; mod project_goals; mod search_graph; mod trait_goals; -pub use eval_ctxt::EvalCtxt; +pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt}; pub use fulfill::FulfillmentCtxt; -/// A goal is a statement, i.e. `predicate`, we want to prove -/// given some assumptions, i.e. `param_env`. -/// -/// Most of the time the `param_env` contains the `where`-bounds of the function -/// we're currently typechecking while the `predicate` is some trait bound. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] -pub struct Goal<'tcx, P> { - param_env: ty::ParamEnv<'tcx>, - predicate: P, -} - -impl<'tcx, P> Goal<'tcx, P> { - pub fn new( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - predicate: impl ToPredicate<'tcx, P>, - ) -> Goal<'tcx, P> { - Goal { param_env, predicate: predicate.to_predicate(tcx) } - } - - /// Updates the goal to one with a different `predicate` but the same `param_env`. - fn with<Q>(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> { - Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) } - } -} - -impl<'tcx, P> From<Obligation<'tcx, P>> for Goal<'tcx, P> { - fn from(obligation: Obligation<'tcx, P>) -> Goal<'tcx, P> { - Goal { param_env: obligation.param_env, predicate: obligation.predicate } - } -} -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] -pub struct Response<'tcx> { - pub var_values: CanonicalVarValues<'tcx>, - /// Additional constraints returned by this query. - pub external_constraints: ExternalConstraints<'tcx>, - pub certainty: Certainty, +#[derive(Debug, Clone, Copy)] +enum SolverMode { + /// Ordinary trait solving, using everywhere except for coherence. + Normal, + /// Trait solving during coherence. There are a few notable differences + /// between coherence and ordinary trait solving. + /// + /// Most importantly, trait solving during coherence must not be incomplete, + /// i.e. return `Err(NoSolution)` for goals for which a solution exists. + /// This means that we must not make any guesses or arbitrary choices. + Coherence, } trait CanonicalResponseExt { fn has_no_inference_or_external_constraints(&self) -> bool; + + fn has_only_region_constraints(&self) -> bool; } impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> { @@ -92,242 +56,35 @@ impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> { && self.value.var_values.is_identity() && self.value.external_constraints.opaque_types.is_empty() } -} -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] -pub enum Certainty { - Yes, - Maybe(MaybeCause), -} - -impl Certainty { - pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); - - /// When proving multiple goals using **AND**, e.g. nested obligations for an impl, - /// use this function to unify the certainty of these goals - pub fn unify_and(self, other: Certainty) -> Certainty { - match (self, other) { - (Certainty::Yes, Certainty::Yes) => Certainty::Yes, - (Certainty::Yes, Certainty::Maybe(_)) => other, - (Certainty::Maybe(_), Certainty::Yes) => self, - (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => { - Certainty::Maybe(MaybeCause::Overflow) - } - // If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal - // may still result in failure. - (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_)) - | (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => { - Certainty::Maybe(MaybeCause::Ambiguity) - } - } - } -} - -/// Why we failed to evaluate a goal. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] -pub enum MaybeCause { - /// We failed due to ambiguity. This ambiguity can either - /// be a true ambiguity, i.e. there are multiple different answers, - /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals. - Ambiguity, - /// We gave up due to an overflow, most often by hitting the recursion limit. - Overflow, -} - -type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>; -type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>; -/// The result of evaluating a canonical query. -/// -/// FIXME: We use a different type than the existing canonical queries. This is because -/// we need to add a `Certainty` for `overflow` and may want to restructure this code without -/// having to worry about changes to currently used code. Once we've made progress on this -/// solver, merge the two responses again. -pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>; - -pub trait InferCtxtEvalExt<'tcx> { - /// Evaluates a goal from **outside** of the trait solver. - /// - /// Using this while inside of the solver is wrong as it uses a new - /// search graph which would break cycle detection. - fn evaluate_root_goal( - &self, - goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> Result<(bool, Certainty), NoSolution>; -} - -impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { - fn evaluate_root_goal( - &self, - goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> Result<(bool, Certainty), NoSolution> { - let mut search_graph = search_graph::SearchGraph::new(self.tcx); - - let result = EvalCtxt { - search_graph: &mut search_graph, - infcx: self, - // Only relevant when canonicalizing the response. - max_input_universe: ty::UniverseIndex::ROOT, - var_values: CanonicalVarValues::dummy(), - in_projection_eq_hack: false, - } - .evaluate_goal(goal); - - assert!(search_graph.is_empty()); - result + fn has_only_region_constraints(&self) -> bool { + self.value.var_values.is_identity_modulo_regions() + && self.value.external_constraints.opaque_types.is_empty() } } impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { - /// The entry point of the solver. - /// - /// This function deals with (coinductive) cycles, overflow, and caching - /// and then calls [`EvalCtxt::compute_goal`] which contains the actual - /// logic of the solver. - /// - /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal] - /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're - /// outside of it. - #[instrument(level = "debug", skip(tcx, search_graph), ret)] - fn evaluate_canonical_goal( - tcx: TyCtxt<'tcx>, - search_graph: &'a mut search_graph::SearchGraph<'tcx>, - canonical_goal: CanonicalGoal<'tcx>, - ) -> QueryResult<'tcx> { - // Deal with overflow, caching, and coinduction. - // - // The actual solver logic happens in `ecx.compute_goal`. - search_graph.with_new_goal(tcx, canonical_goal, |search_graph| { - let (ref infcx, goal, var_values) = - tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal); - let mut ecx = EvalCtxt { - infcx, - var_values, - max_input_universe: canonical_goal.max_universe, - search_graph, - in_projection_eq_hack: false, - }; - ecx.compute_goal(goal) - }) - } - - /// Recursively evaluates `goal`, returning whether any inference vars have - /// been constrained and the certainty of the result. - fn evaluate_goal( - &mut self, - goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> Result<(bool, Certainty), NoSolution> { - let (orig_values, canonical_goal) = self.canonicalize_goal(goal); - let canonical_response = - EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; - - let has_changed = !canonical_response.value.var_values.is_identity(); - let certainty = self.instantiate_and_apply_query_response( - goal.param_env, - orig_values, - canonical_response, - )?; - - // Check that rerunning this query with its inference constraints applied - // doesn't result in new inference constraints and has the same result. - // - // If we have projection goals like `<T as Trait>::Assoc == u32` we recursively - // call `exists<U> <T as Trait>::Assoc == U` to enable better caching. This goal - // could constrain `U` to `u32` which would cause this check to result in a - // solver cycle. - if cfg!(debug_assertions) - && has_changed - && !self.in_projection_eq_hack - && !self.search_graph.in_cycle() - { - let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); - let canonical_response = - EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; - if !canonical_response.value.var_values.is_identity() { - bug!("unstable result: {goal:?} {canonical_goal:?} {canonical_response:?}"); - } - assert_eq!(certainty, canonical_response.value.certainty); - } - - Ok((has_changed, certainty)) - } - - fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> { - let Goal { param_env, predicate } = goal; - let kind = predicate.kind(); - if let Some(kind) = kind.no_bound_vars() { - match kind { - ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => { - self.compute_trait_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => { - self.compute_projection_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => { - self.compute_type_outlives_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => { - self.compute_region_outlives_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { - self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) }) - } - ty::PredicateKind::Subtype(predicate) => { - self.compute_subtype_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Coerce(predicate) => { - self.compute_coerce_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::ClosureKind(def_id, substs, kind) => self - .compute_closure_kind_goal(Goal { - param_env, - predicate: (def_id, substs, kind), - }), - ty::PredicateKind::ObjectSafe(trait_def_id) => { - self.compute_object_safe_goal(trait_def_id) - } - ty::PredicateKind::WellFormed(arg) => { - self.compute_well_formed_goal(Goal { param_env, predicate: arg }) - } - ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS), - // FIXME: implement these predicates :) - ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => { - self.make_canonical_response(Certainty::Yes) - } - ty::PredicateKind::TypeWellFormedFromEnv(..) => { - bug!("TypeWellFormedFromEnv is only used for Chalk") - } - ty::PredicateKind::AliasEq(lhs, rhs) => { - self.compute_alias_eq_goal(Goal { param_env, predicate: (lhs, rhs) }) - } - } - } else { - let kind = self.infcx.instantiate_binder_with_placeholders(kind); - let goal = goal.with(self.tcx(), ty::Binder::dummy(kind)); - let (_, certainty) = self.evaluate_goal(goal)?; - self.make_canonical_response(certainty) - } - } - + #[instrument(level = "debug", skip(self))] fn compute_type_outlives_goal( &mut self, goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>, ) -> QueryResult<'tcx> { let ty::OutlivesPredicate(ty, lt) = goal.predicate; - self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy()); - self.make_canonical_response(Certainty::Yes) + self.register_ty_outlives(ty, lt); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + #[instrument(level = "debug", skip(self))] fn compute_region_outlives_goal( &mut self, goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>, ) -> QueryResult<'tcx> { - self.infcx.region_outlives_predicate( - &ObligationCause::dummy(), - ty::Binder::dummy(goal.predicate), - ); - self.make_canonical_response(Certainty::Yes) + let ty::OutlivesPredicate(a, b) = goal.predicate; + self.register_region_outlives(a, b); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + #[instrument(level = "debug", skip(self))] fn compute_coerce_goal( &mut self, goal: Goal<'tcx, CoercePredicate<'tcx>>, @@ -342,25 +99,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { }) } + #[instrument(level = "debug", skip(self))] fn compute_subtype_goal( &mut self, goal: Goal<'tcx, SubtypePredicate<'tcx>>, ) -> QueryResult<'tcx> { if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() { - // FIXME: Do we want to register a subtype relation between these vars? - // That won't actually reflect in the query response, so it seems moot. - self.make_canonical_response(Certainty::AMBIGUOUS) + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } else { - let InferOk { value: (), obligations } = self - .infcx - .at(&ObligationCause::dummy(), goal.param_env) - .sub(goal.predicate.a, goal.predicate.b)?; - self.evaluate_all_and_make_canonical_response( - obligations.into_iter().map(|pred| pred.into()).collect(), - ) + self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } + #[instrument(level = "debug", skip(self))] fn compute_closure_kind_goal( &mut self, goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>, @@ -369,92 +121,154 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind(); let Some(found_kind) = found_kind else { - return self.make_canonical_response(Certainty::AMBIGUOUS); + return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); }; if found_kind.extends(expected_kind) { - self.make_canonical_response(Certainty::Yes) + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } } + #[instrument(level = "debug", skip(self))] fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> { if self.tcx().check_is_object_safe(trait_def_id) { - self.make_canonical_response(Certainty::Yes) + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } } + #[instrument(level = "debug", skip(self))] fn compute_well_formed_goal( &mut self, goal: Goal<'tcx, ty::GenericArg<'tcx>>, ) -> QueryResult<'tcx> { - match crate::traits::wf::unnormalized_obligations( - self.infcx, - goal.param_env, - goal.predicate, - ) { - Some(obligations) => self.evaluate_all_and_make_canonical_response( - obligations.into_iter().map(|o| o.into()).collect(), - ), - None => self.make_canonical_response(Certainty::AMBIGUOUS), + match self.well_formed_goals(goal.param_env, goal.predicate) { + Some(goals) => { + self.add_goals(goals); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS), } } #[instrument(level = "debug", skip(self), ret)] - fn compute_alias_eq_goal( + fn compute_alias_relate_goal( &mut self, - goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>)>, + goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>, ) -> QueryResult<'tcx> { let tcx = self.tcx(); + // We may need to invert the alias relation direction if dealing an alias on the RHS. + #[derive(Debug)] + enum Invert { + No, + Yes, + } + let evaluate_normalizes_to = + |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other, direction, invert| { + let span = tracing::span!( + tracing::Level::DEBUG, + "compute_alias_relate_goal(evaluate_normalizes_to)", + ?alias, + ?other, + ?direction, + ?invert + ); + let _enter = span.enter(); + let result = ecx.probe(|ecx| { + let other = match direction { + // This is purely an optimization. + ty::AliasRelationDirection::Equate => other, + + ty::AliasRelationDirection::Subtype => { + let fresh = ecx.next_term_infer_of_kind(other); + let (sub, sup) = match invert { + Invert::No => (fresh, other), + Invert::Yes => (other, fresh), + }; + ecx.sub(goal.param_env, sub, sup)?; + fresh + } + }; + ecx.add_goal(goal.with( + tcx, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: alias, + term: other, + }), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }); + debug!(?result); + result + }; - let evaluate_normalizes_to = |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other| { - debug!("evaluate_normalizes_to(alias={:?}, other={:?})", alias, other); - let r = ecx.probe(|ecx| { - let (_, certainty) = ecx.evaluate_goal(goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: alias, - term: other, - }), - ))?; - ecx.make_canonical_response(certainty) - }); - debug!("evaluate_normalizes_to(..) -> {:?}", r); - r - }; + let (lhs, rhs, direction) = goal.predicate; - if goal.predicate.0.is_infer() || goal.predicate.1.is_infer() { + if lhs.is_infer() || rhs.is_infer() { bug!( - "`AliasEq` goal with an infer var on lhs or rhs which should have been instantiated" + "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated" ); } - match ( - goal.predicate.0.to_alias_term_no_opaque(tcx), - goal.predicate.1.to_alias_term_no_opaque(tcx), - ) { - (None, None) => bug!("`AliasEq` goal without an alias on either lhs or rhs"), - (Some(alias), None) => evaluate_normalizes_to(self, alias, goal.predicate.1), - (None, Some(alias)) => evaluate_normalizes_to(self, alias, goal.predicate.0), - (Some(alias_lhs), Some(alias_rhs)) => { - debug!("compute_alias_eq_goal: both sides are aliases"); + match (lhs.to_projection_term(tcx), rhs.to_projection_term(tcx)) { + (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), - let mut candidates = Vec::with_capacity(3); + // RHS is not a projection, only way this is true is if LHS normalizes-to RHS + (Some(alias_lhs), None) => { + evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No) + } + + // LHS is not a projection, only way this is true is if RHS normalizes-to LHS + (None, Some(alias_rhs)) => { + evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes) + } - // Evaluate all 3 potential candidates for the alias' being equal - candidates.push(evaluate_normalizes_to(self, alias_lhs, goal.predicate.1)); - candidates.push(evaluate_normalizes_to(self, alias_rhs, goal.predicate.0)); - candidates.push(self.probe(|this| { - debug!("compute_alias_eq_goal: alias defids are equal, equating substs"); - let nested_goals = this.eq(goal.param_env, alias_lhs, alias_rhs)?; - this.evaluate_all_and_make_canonical_response(nested_goals) - })); + (Some(alias_lhs), Some(alias_rhs)) => { + debug!("both sides are aliases"); + + let mut candidates = Vec::new(); + // LHS normalizes-to RHS + candidates.extend( + evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No).ok(), + ); + // RHS normalizes-to RHS + candidates.extend( + evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes).ok(), + ); + // Relate via substs + candidates.extend( + self.probe(|ecx| { + let span = tracing::span!( + tracing::Level::DEBUG, + "compute_alias_relate_goal(relate_via_substs)", + ?alias_lhs, + ?alias_rhs, + ?direction + ); + let _enter = span.enter(); + + match direction { + ty::AliasRelationDirection::Equate => { + ecx.eq(goal.param_env, alias_lhs, alias_rhs)?; + } + ty::AliasRelationDirection::Subtype => { + ecx.sub(goal.param_env, alias_lhs, alias_rhs)?; + } + } + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + .ok(), + ); debug!(?candidates); - self.try_merge_responses(candidates.into_iter()) + if let Some(merged) = self.try_merge_responses(&candidates) { + Ok(merged) + } else { + self.flounder(&candidates) + } } } } @@ -465,99 +279,78 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>, ) -> QueryResult<'tcx> { let (ct, ty) = goal.predicate; - let nested_goals = self.eq(goal.param_env, ct.ty(), ty)?; - self.evaluate_all_and_make_canonical_response(nested_goals) + self.eq(goal.param_env, ct.ty(), ty)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } impl<'tcx> EvalCtxt<'_, 'tcx> { - // Recursively evaluates a list of goals to completion, returning the certainty - // of all of the goals. - fn evaluate_all( - &mut self, - mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, - ) -> Result<Certainty, NoSolution> { - let mut new_goals = Vec::new(); - self.repeat_while_none( - |_| Ok(Certainty::Maybe(MaybeCause::Overflow)), - |this| { - let mut has_changed = Err(Certainty::Yes); - for goal in goals.drain(..) { - let (changed, certainty) = match this.evaluate_goal(goal) { - Ok(result) => result, - Err(NoSolution) => return Some(Err(NoSolution)), - }; - - if changed { - has_changed = Ok(()); - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - new_goals.push(goal); - has_changed = has_changed.map_err(|c| c.unify_and(certainty)); - } - } - } + #[instrument(level = "debug", skip(self))] + fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>) { + assert!( + self.nested_goals.normalizes_to_hack_goal.is_none(), + "attempted to set the projection eq hack goal when one already exists" + ); + self.nested_goals.normalizes_to_hack_goal = Some(goal); + } - match has_changed { - Ok(()) => { - mem::swap(&mut new_goals, &mut goals); - None - } - Err(certainty) => Some(Ok(certainty)), - } - }, - ) + #[instrument(level = "debug", skip(self))] + fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) { + self.nested_goals.goals.push(goal); } - // Recursively evaluates a list of goals to completion, making a query response. - // - // This is just a convenient way of calling [`EvalCtxt::evaluate_all`], - // then [`EvalCtxt::make_canonical_response`]. - fn evaluate_all_and_make_canonical_response( - &mut self, - goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, - ) -> QueryResult<'tcx> { - self.evaluate_all(goals).and_then(|certainty| self.make_canonical_response(certainty)) + #[instrument(level = "debug", skip(self, goals))] + fn add_goals(&mut self, goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>) { + let current_len = self.nested_goals.goals.len(); + self.nested_goals.goals.extend(goals); + debug!("added_goals={:?}", &self.nested_goals.goals[current_len..]); } + /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`. + /// + /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`. + #[instrument(level = "debug", skip(self), ret)] fn try_merge_responses( &mut self, - responses: impl Iterator<Item = QueryResult<'tcx>>, - ) -> QueryResult<'tcx> { - let candidates = responses.into_iter().flatten().collect::<Box<[_]>>(); - - if candidates.is_empty() { - return Err(NoSolution); + responses: &[CanonicalResponse<'tcx>], + ) -> Option<CanonicalResponse<'tcx>> { + if responses.is_empty() { + return None; } - // FIXME(-Ztreat-solver=next): We should instead try to find a `Certainty::Yes` response with + // FIXME(-Ztrait-solver=next): We should instead try to find a `Certainty::Yes` response with // a subset of the constraints that all the other responses have. - let one = candidates[0]; - if candidates[1..].iter().all(|resp| resp == &one) { - return Ok(one); + let one = responses[0]; + if responses[1..].iter().all(|&resp| resp == one) { + return Some(one); } - if let Some(response) = candidates.iter().find(|response| { - response.value.certainty == Certainty::Yes - && response.has_no_inference_or_external_constraints() - }) { - return Ok(*response); - } + responses + .iter() + .find(|response| { + response.value.certainty == Certainty::Yes + && response.has_no_inference_or_external_constraints() + }) + .copied() + } - let certainty = candidates.iter().fold(Certainty::AMBIGUOUS, |certainty, response| { - certainty.unify_and(response.value.certainty) + /// If we fail to merge responses we flounder and return overflow or ambiguity. + #[instrument(level = "debug", skip(self), ret)] + fn flounder(&mut self, responses: &[CanonicalResponse<'tcx>]) -> QueryResult<'tcx> { + if responses.is_empty() { + return Err(NoSolution); + } + let certainty = responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| { + certainty.unify_with(response.value.certainty) }); - // FIXME(-Ztrait-solver=next): We should take the intersection of the constraints on all the - // responses and use that for the constraints of this ambiguous response. - let response = self.make_canonical_response(certainty); - if let Ok(response) = &response { + + let response = self.evaluate_added_goals_and_make_canonical_response(certainty); + if let Ok(response) = response { assert!(response.has_no_inference_or_external_constraints()); + Ok(response) + } else { + bug!("failed to make floundered response: {responses:?}"); } - - response } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 33c66d072..14cb43b89 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,24 +1,23 @@ -use crate::traits::{specialization_graph, translate_substs}; +use crate::traits::specialization_graph; -use super::assembly; -use super::trait_goals::structural_traits; -use super::{Certainty, EvalCtxt, Goal, QueryResult}; +use super::assembly::{self, structural_traits}; +use super::EvalCtxt; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; -use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; 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, DUMMY_SP}; -use std::iter; impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] pub(super) fn compute_projection_goal( &mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, @@ -32,57 +31,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // projection cache in the solver. if self.term_is_fully_unconstrained(goal) { let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates_and_discard_reservation_impls(candidates) + self.merge_candidates(candidates) } else { - let predicate = goal.predicate; - let unconstrained_rhs = match predicate.term.unpack() { - ty::TermKind::Ty(_) => self.next_ty_infer().into(), - ty::TermKind::Const(ct) => self.next_const_infer(ct.ty()).into(), - }; - let unconstrained_predicate = ty::Clause::Projection(ProjectionPredicate { - projection_ty: goal.predicate.projection_ty, - term: unconstrained_rhs, - }); - let (_has_changed, normalize_certainty) = self.in_projection_eq_hack(|this| { - this.evaluate_goal(goal.with(this.tcx(), unconstrained_predicate)) - })?; - - let nested_eq_goals = self.eq(goal.param_env, unconstrained_rhs, predicate.term)?; - let eval_certainty = self.evaluate_all(nested_eq_goals)?; - self.make_canonical_response(normalize_certainty.unify_and(eval_certainty)) + self.set_normalizes_to_hack_goal(goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } - - /// This sets a flag used by a debug assert in [`EvalCtxt::evaluate_goal`], - /// see the comment in that method for more details. - fn in_projection_eq_hack<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { - self.in_projection_eq_hack = true; - let result = f(self); - self.in_projection_eq_hack = false; - result - } - - /// After normalizing the projection to `normalized_alias` with the given - /// `normalization_certainty`, constrain the inference variable `term` to it - /// and return a query response. - fn eq_term_and_make_canonical_response( - &mut self, - goal: Goal<'tcx, ProjectionPredicate<'tcx>>, - normalization_certainty: Certainty, - normalized_alias: impl Into<ty::Term<'tcx>>, - ) -> QueryResult<'tcx> { - // The term of our goal should be fully unconstrained, so this should never fail. - // - // It can however be ambiguous when the `normalized_alias` contains a projection. - let nested_goals = self - .eq(goal.param_env, goal.predicate.term, normalized_alias.into()) - .expect("failed to unify with unconstrained term"); - - let unify_certainty = - self.evaluate_all(nested_goals).expect("failed to unify with unconstrained term"); - - self.make_canonical_response(normalization_certainty.unify_and(unify_certainty)) - } } impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { @@ -90,6 +44,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { self.self_ty() } + fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { + self.projection_ty.trait_ref(tcx) + } + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { self.with_self_ty(tcx, self_ty) } @@ -110,19 +68,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx.probe(|ecx| { let assumption_projection_pred = ecx.instantiate_binder_with_infer(poly_projection_pred); - let mut nested_goals = ecx.eq( + ecx.eq( goal.param_env, goal.predicate.projection_ty, assumption_projection_pred.projection_ty, )?; - nested_goals.extend(requirements); - let subst_certainty = ecx.evaluate_all(nested_goals)?; - - ecx.eq_term_and_make_canonical_response( - goal, - subst_certainty, - assumption_projection_pred.term, - ) + ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)?; + ecx.add_goals(requirements); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } else { Err(NoSolution) @@ -138,21 +91,22 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { && poly_projection_pred.projection_def_id() == goal.predicate.def_id() { ecx.probe(|ecx| { + let tcx = ecx.tcx(); + let assumption_projection_pred = ecx.instantiate_binder_with_infer(poly_projection_pred); - let mut nested_goals = ecx.eq( + ecx.eq( goal.param_env, goal.predicate.projection_ty, assumption_projection_pred.projection_ty, )?; - 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( + ecx.add_goals( structural_traits::predicates_for_object_candidate( - ecx, + &ecx, goal.param_env, goal.predicate.projection_ty.trait_ref(tcx), bounds, @@ -160,14 +114,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { .into_iter() .map(|pred| goal.with(tcx, pred)), ); - - let subst_certainty = ecx.evaluate_all(nested_goals)?; - - ecx.eq_term_and_make_canonical_response( - goal, - subst_certainty, - assumption_projection_pred.term, - ) + ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } else { Err(NoSolution) @@ -183,10 +131,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'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::AsPlaceholder }; - if iter::zip(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs) - .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) - { + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; + if !drcx.substs_refs_may_unify(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs) { return Err(NoSolution); } @@ -194,28 +140,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { 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_trait_ref, impl_trait_ref)?; + ecx.eq(goal.param_env, goal_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); - let match_impl_certainty = ecx.evaluate_all(nested_goals)?; + ecx.add_goals(where_clause_bounds); // 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.infcx, + ecx, goal.param_env, goal_trait_ref, goal.predicate.def_id(), impl_def_id )? else { - return ecx.make_canonical_response(match_impl_certainty.unify_and(Certainty::AMBIGUOUS)); + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); }; if !assoc_def.item.defaultness(tcx).has_value() { @@ -240,8 +185,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal_trait_ref.def_id, impl_substs, ); - let substs = translate_substs( - ecx.infcx, + let substs = ecx.translate_substs( goal.param_env, impl_def_id, impl_substs_with_gat, @@ -262,7 +206,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ty.map_bound(|ty| ty.into()) }; - ecx.eq_term_and_make_canonical_response(goal, match_impl_certainty, term.subst(tcx, substs)) + ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -301,20 +246,31 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'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 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 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)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); @@ -378,27 +334,21 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { LangItem::Sized, [ty::GenericArg::from(goal.predicate.self_ty())], )); - - let (_, is_sized_certainty) = - ecx.evaluate_goal(goal.with(tcx, sized_predicate))?; - return ecx.eq_term_and_make_canonical_response( - goal, - is_sized_certainty, - tcx.types.unit, - ); + ecx.add_goal(goal.with(tcx, sized_predicate)); + tcx.types.unit } ty::Adt(def, substs) if def.is_struct() => { - match def.non_enum_variant().fields.last() { + match def.non_enum_variant().fields.raw.last() { None => tcx.types.unit, Some(field_def) => { let self_ty = field_def.ty(tcx, substs); - let new_goal = goal.with( + ecx.add_goal(goal.with( tcx, ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), - ); - let (_, certainty) = ecx.evaluate_goal(new_goal)?; - return ecx.make_canonical_response(certainty); + )); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } } } @@ -407,12 +357,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ty::Tuple(elements) => match elements.last() { None => tcx.types.unit, Some(&self_ty) => { - let new_goal = goal.with( + ecx.add_goal(goal.with( tcx, ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), - ); - let (_, certainty) = ecx.evaluate_goal(new_goal)?; - return ecx.make_canonical_response(certainty); + )); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } }, @@ -425,7 +375,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ), }; - ecx.eq_term_and_make_canonical_response(goal, Certainty::Yes, metadata_ty) + ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -512,7 +463,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { fn consider_builtin_dyn_upcast_candidates( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Vec<super::CanonicalResponse<'tcx>> { + ) -> Vec<CanonicalResponse<'tcx>> { bug!("`Unsize` does not have an associated type: {:?}", goal); } @@ -520,8 +471,65 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - let discriminant = goal.predicate.self_ty().discriminant_ty(ecx.tcx()); - ecx.probe(|ecx| ecx.eq_term_and_make_canonical_response(goal, Certainty::Yes, discriminant)) + 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::Generator(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | 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(|ecx| { + ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())?; + 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) } } @@ -529,15 +537,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { /// /// FIXME: We should merge these 3 implementations as it's likely that they otherwise /// diverge. -#[instrument(level = "debug", skip(infcx, param_env), ret)] +#[instrument(level = "debug", skip(ecx, param_env), ret)] fn fetch_eligible_assoc_item_def<'tcx>( - infcx: &InferCtxt<'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(infcx.tcx, impl_def_id, trait_assoc_def_id) + 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() { @@ -549,7 +557,7 @@ fn fetch_eligible_assoc_item_def<'tcx>( // 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 = infcx.resolve_vars_if_possible(goal_trait_ref); + 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"); diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs index 86b13c05f..d1b4fa554 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs @@ -8,12 +8,10 @@ //! //! FIXME(@lcnr): Write that section, feel free to ping me if you need help here //! before then or if I still haven't done that before January 2023. -use super::overflow::OverflowData; use super::StackDepth; -use crate::solve::{CanonicalGoal, QueryResult}; use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::IndexVec; -use rustc_middle::ty::TyCtxt; +use rustc_middle::traits::solve::{CanonicalGoal, QueryResult}; rustc_index::newtype_index! { pub struct EntryIndex {} @@ -98,26 +96,3 @@ impl<'tcx> ProvisionalCache<'tcx> { self.entries[entry_index].response } } - -pub(super) fn try_move_finished_goal_to_global_cache<'tcx>( - tcx: TyCtxt<'tcx>, - overflow_data: &mut OverflowData, - stack: &IndexVec<super::StackDepth, super::StackElem<'tcx>>, - goal: CanonicalGoal<'tcx>, - response: QueryResult<'tcx>, -) { - // We move goals to the global cache if we either did not hit an overflow or if it's - // the root goal as that will now always hit the same overflow limit. - // - // NOTE: We cannot move any non-root goals to the global cache even if their final result - // isn't impacted by the overflow as that goal still has unstable query dependencies - // because it didn't go its full depth. - // - // FIXME(@lcnr): We could still cache subtrees which are not impacted by overflow though. - // Tracking that info correctly isn't trivial, so I haven't implemented it for now. - let should_cache_globally = !overflow_data.did_overflow() || stack.is_empty(); - if should_cache_globally { - // FIXME: move the provisional entry to the global cache. - let _ = (tcx, goal, response); - } -} diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index c7eb8de65..050269fa9 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -1,15 +1,19 @@ mod cache; mod overflow; +pub(super) use overflow::OverflowHandler; + use self::cache::ProvisionalEntry; -use super::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; -pub(super) use crate::solve::search_graph::overflow::OverflowHandler; use cache::ProvisionalCache; use overflow::OverflowData; use rustc_index::vec::IndexVec; +use rustc_middle::dep_graph::DepKind; +use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; use rustc_middle::ty::TyCtxt; use std::{collections::hash_map::Entry, mem}; +use super::SolverMode; + rustc_index::newtype_index! { pub struct StackDepth {} } @@ -20,6 +24,7 @@ struct StackElem<'tcx> { } pub(super) struct SearchGraph<'tcx> { + mode: SolverMode, /// The stack of goals currently being computed. /// /// An element is *deeper* in the stack if its index is *lower*. @@ -29,24 +34,43 @@ pub(super) struct SearchGraph<'tcx> { } impl<'tcx> SearchGraph<'tcx> { - pub(super) fn new(tcx: TyCtxt<'tcx>) -> SearchGraph<'tcx> { + pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> { Self { + mode, stack: Default::default(), overflow_data: OverflowData::new(tcx), provisional_cache: ProvisionalCache::empty(), } } + pub(super) fn solver_mode(&self) -> SolverMode { + self.mode + } + + /// We do not use the global cache during coherence. + /// + /// The trait solver behavior is different for coherence + /// so we would have to add the solver mode to the cache key. + /// This is probably not worth it as trait solving during + /// coherence tends to already be incredibly fast. + /// + /// We could add another global cache for coherence instead, + /// but that's effort so let's only do it if necessary. + pub(super) fn should_use_global_cache(&self) -> bool { + match self.mode { + SolverMode::Normal => true, + SolverMode::Coherence => false, + } + } + pub(super) fn is_empty(&self) -> bool { - self.stack.is_empty() - && self.provisional_cache.is_empty() - && !self.overflow_data.did_overflow() + self.stack.is_empty() && self.provisional_cache.is_empty() } /// Whether we're currently in a cycle. This should only be used /// for debug assertions. pub(super) fn in_cycle(&self) -> bool { - if let Some(stack_depth) = self.stack.last() { + if let Some(stack_depth) = self.stack.last_index() { // Either the current goal on the stack is the root of a cycle... if self.stack[stack_depth].has_been_used { return true; @@ -70,8 +94,6 @@ impl<'tcx> SearchGraph<'tcx> { tcx: TyCtxt<'tcx>, goal: CanonicalGoal<'tcx>, ) -> Result<(), QueryResult<'tcx>> { - // FIXME: start by checking the global cache - // Look at the provisional cache to check for cycles. let cache = &mut self.provisional_cache; match cache.lookup_table.entry(goal) { @@ -131,7 +153,7 @@ impl<'tcx> SearchGraph<'tcx> { /// coinductive cycles. /// /// When we encounter a coinductive cycle, we have to prove the final result of that cycle - /// while we are still computing that result. Because of this we continously recompute the + /// while we are still computing that result. Because of this we continuously recompute the /// cycle until the result of the previous iteration is equal to the final result, at which /// point we are done. /// @@ -139,10 +161,9 @@ impl<'tcx> SearchGraph<'tcx> { /// updated the provisional cache and we have to recompute the current goal. /// /// FIXME: Refer to the rustc-dev-guide entry once it exists. - #[instrument(level = "debug", skip(self, tcx, actual_goal), ret)] + #[instrument(level = "debug", skip(self, actual_goal), ret)] fn try_finalize_goal( &mut self, - tcx: TyCtxt<'tcx>, actual_goal: CanonicalGoal<'tcx>, response: QueryResult<'tcx>, ) -> bool { @@ -176,72 +197,90 @@ impl<'tcx> SearchGraph<'tcx> { self.stack.push(StackElem { goal, has_been_used: false }); false } else { - self.try_move_finished_goal_to_global_cache(tcx, stack_elem); true } } - fn try_move_finished_goal_to_global_cache( + pub(super) fn with_new_goal( &mut self, tcx: TyCtxt<'tcx>, - stack_elem: StackElem<'tcx>, - ) { - let StackElem { goal, .. } = stack_elem; + canonical_goal: CanonicalGoal<'tcx>, + mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx> { + if self.should_use_global_cache() { + if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_goal, tcx) { + debug!(?canonical_goal, ?result, "cache hit"); + return result; + } + } + + match self.try_push_stack(tcx, canonical_goal) { + Ok(()) => {} + // Our goal is already on the stack, eager return. + Err(response) => return response, + } + + // This is for global caching, so we properly track query dependencies. + // Everything that affects the `Result` should be performed within this + // `with_anon_task` closure. + let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || { + self.repeat_while_none( + |this| { + let result = this.deal_with_overflow(tcx, canonical_goal); + let _ = this.stack.pop().unwrap(); + result + }, + |this| { + let result = loop_body(this); + this.try_finalize_goal(canonical_goal, result).then(|| result) + }, + ) + }); + let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap(); + let provisional_entry_index = *cache.lookup_table.get(&canonical_goal).unwrap(); let provisional_entry = &mut cache.entries[provisional_entry_index]; let depth = provisional_entry.depth; // If not, we're done with this goal. // // Check whether that this goal doesn't depend on a goal deeper on the stack - // and if so, move it and all nested goals to the global cache. + // and if so, move it to the global cache. // // Note that if any nested goal were to depend on something deeper on the stack, // this would have also updated the depth of the current goal. if depth == self.stack.next_index() { - for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) { + // If the current goal is the head of a cycle, we drop all other + // cycle participants without moving them to the global cache. + let other_cycle_participants = provisional_entry_index.index() + 1; + for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) { let actual_index = cache.lookup_table.remove(&entry.goal); debug_assert_eq!(Some(i), actual_index); debug_assert!(entry.depth == depth); - cache::try_move_finished_goal_to_global_cache( - tcx, - &mut self.overflow_data, - &self.stack, - entry.goal, - entry.response, - ); } - } - } - pub(super) fn with_new_goal( - &mut self, - tcx: TyCtxt<'tcx>, - canonical_goal: CanonicalGoal<'tcx>, - mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>, - ) -> QueryResult<'tcx> { - match self.try_push_stack(tcx, canonical_goal) { - Ok(()) => {} - // Our goal is already on the stack, eager return. - Err(response) => return response, + let current_goal = cache.entries.pop().unwrap(); + let actual_index = cache.lookup_table.remove(¤t_goal.goal); + debug_assert_eq!(Some(provisional_entry_index), actual_index); + debug_assert!(current_goal.depth == depth); + + // We move the root goal to the global cache if we either did not hit an overflow or if it's + // the root goal as that will now always hit the same overflow limit. + // + // NOTE: We cannot move any non-root goals to the global cache. When replaying the root goal's + // dependencies, our non-root goal may no longer appear as child of the root goal. + // + // See https://github.com/rust-lang/rust/pull/108071 for some additional context. + let can_cache = !self.overflow_data.did_overflow() || self.stack.is_empty(); + if self.should_use_global_cache() && can_cache { + tcx.new_solver_evaluation_cache.insert( + current_goal.goal, + dep_node, + current_goal.response, + ); + } } - self.repeat_while_none( - |this| { - let result = this.deal_with_overflow(tcx, canonical_goal); - let stack_elem = this.stack.pop().unwrap(); - this.try_move_finished_goal_to_global_cache(tcx, stack_elem); - result - }, - |this| { - let result = loop_body(this); - if this.try_finalize_goal(tcx, canonical_goal, result) { - Some(result) - } else { - None - } - }, - ) + result } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs index 56409b060..e0a2e0c5c 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs @@ -1,10 +1,11 @@ use rustc_infer::infer::canonical::Canonical; use rustc_infer::traits::query::NoSolution; +use rustc_middle::traits::solve::{Certainty, MaybeCause, QueryResult}; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; use super::SearchGraph; -use crate::solve::{response_no_constraints, Certainty, EvalCtxt, MaybeCause, QueryResult}; +use crate::solve::{response_no_constraints, EvalCtxt}; /// When detecting a solver overflow, we return ambiguity. Overflow can be /// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**. @@ -44,7 +45,7 @@ impl OverflowData { /// Updating the current limit when hitting overflow. fn deal_with_overflow(&mut self) { // When first hitting overflow we reduce the overflow limit - // for all future goals to prevent hangs if there's an exponental + // for all future goals to prevent hangs if there's an exponential // blowup. self.current_limit.0 = self.default_limit.0 / 8; } @@ -72,6 +73,27 @@ pub(in crate::solve) trait OverflowHandler<'tcx> { self.search_graph().overflow_data.deal_with_overflow(); on_overflow(self) } + + // Increment the `additional_depth` by one and evaluate `body`, or `on_overflow` + // if the depth is overflown. + fn with_incremented_depth<T>( + &mut self, + on_overflow: impl FnOnce(&mut Self) -> T, + body: impl FnOnce(&mut Self) -> T, + ) -> T { + let depth = self.search_graph().stack.len(); + self.search_graph().overflow_data.additional_depth += 1; + + let result = if self.search_graph().overflow_data.has_overflow(depth) { + self.search_graph().overflow_data.deal_with_overflow(); + on_overflow(self) + } else { + body(self) + }; + + self.search_graph().overflow_data.additional_depth -= 1; + result + } } impl<'tcx> OverflowHandler<'tcx> for EvalCtxt<'_, 'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 5c499c36e..abd11a15a 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,25 +1,26 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use std::iter; - -use super::assembly; -use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; +use super::assembly::{self, structural_traits}; +use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; -use rustc_hir::LangItem; +use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +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::DUMMY_SP; -pub mod structural_traits; - 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 with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { self.with_self_ty(tcx, self_ty) } @@ -36,27 +37,44 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'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)) - { + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; + if !drcx.substs_refs_may_unify( + goal.predicate.trait_ref.substs, + impl_trait_ref.skip_binder().substs, + ) { 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(|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)?; + 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) + ecx.add_goals(where_clause_bounds); + + ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty) }) } @@ -73,13 +91,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.probe(|ecx| { let assumption_trait_pred = ecx.instantiate_binder_with_infer(poly_trait_pred); - let mut nested_goals = ecx.eq( + 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) + ecx.add_goals(requirements); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } else { Err(NoSolution) @@ -98,7 +116,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.probe(|ecx| { let assumption_trait_pred = ecx.instantiate_binder_with_infer(poly_trait_pred); - let mut nested_goals = ecx.eq( + ecx.eq( goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref, @@ -108,9 +126,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { bug!("expected object type in `consider_object_bound_candidate`"); }; - nested_goals.extend( + ecx.add_goals( structural_traits::predicates_for_object_candidate( - ecx, + &ecx, goal.param_env, goal.predicate.trait_ref, bounds, @@ -118,8 +136,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { .into_iter() .map(|pred| goal.with(tcx, pred)), ); - - ecx.evaluate_all_and_make_canonical_response(nested_goals) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } else { Err(NoSolution) @@ -130,18 +147,8 @@ 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); + if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) { + return result; } ecx.probe_and_evaluate_goal_for_constituent_tys( @@ -160,9 +167,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { 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(), - ) + ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p))); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -191,19 +197,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if goal.predicate.self_ty().has_non_region_infer() { - return ecx.make_canonical_response(Certainty::AMBIGUOUS); + return ecx.evaluate_added_goals_and_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 + && layout.layout.is_pointer_like(&tcx.data_layout) { // FIXME: We could make this faster by making a no-constraints response - ecx.make_canonical_response(Certainty::Yes) + 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> { + if let ty::FnPtr(..) = goal.predicate.self_ty().kind() { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } @@ -215,14 +230,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { 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( + let tupled_inputs_and_output = + match structural_traits::extract_tupled_inputs_and_output_from_callable( tcx, goal.predicate.self_ty(), goal_kind, - )? else { - return ecx.make_canonical_response(Certainty::AMBIGUOUS); - }; + )? { + 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)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output])); @@ -241,7 +260,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { if let ty::Tuple(..) = goal.predicate.self_ty().kind() { - ecx.make_canonical_response(Certainty::Yes) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { Err(NoSolution) } @@ -251,7 +270,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, _goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - ecx.make_canonical_response(Certainty::Yes) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } fn consider_builtin_future_candidate( @@ -271,7 +290,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // 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) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } fn consider_builtin_generator_candidate( @@ -311,7 +330,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'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); + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); } ecx.probe(|ecx| { match (a_ty.kind(), b_ty.kind()) { @@ -320,7 +339,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // 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); + Err(NoSolution) } // `T` -> `dyn Trait` unsizing (_, &ty::Dynamic(data, region, ty::Dyn)) => { @@ -335,29 +354,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { 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) + // 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) + ecx.add_goals( + data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), + ); + // The type must be Sized to be unsized. + ecx.add_goal( + 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. + ecx.add_goal( + goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))), + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // `[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) + ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>` (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) @@ -373,6 +389,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let tail_field = a_def .non_enum_variant() .fields + .raw .last() .expect("expected unsized ADT to have a tail field"); let tail_field_ty = tcx.type_of(tail_field.did); @@ -391,15 +408,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Finally, we require that `TailA: Unsize<TailB>` for the tail field // types. - let mut nested_goals = ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; - nested_goals.push(goal.with( + ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; + ecx.add_goal(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) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>` (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) @@ -411,17 +427,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // 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)?; + 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( + ecx.add_goal(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) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } _ => Err(NoSolution), } @@ -471,12 +486,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { 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( + ecx.eq(goal.param_env, new_a_ty, b_ty)?; + ecx.add_goal( goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))), ); - - ecx.evaluate_all_and_make_canonical_response(nested_obligations) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) }; @@ -510,11 +524,145 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { _goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { // `DiscriminantKind` is automatically implemented for every type. - ecx.make_canonical_response(Certainty::Yes) + 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.param_env.is_const() { + // `Destruct` is automatically implemented for every type in + // non-const environments. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver + Err(NoSolution) + } + } + + fn consider_builtin_transmute_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + // `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 substs = ecx.tcx().erase_regions(goal.predicate.trait_ref.substs); + + let Some(assume) = rustc_transmute::Assume::from_const( + ecx.tcx(), + goal.param_env, + substs.const_at(3), + ) else { + return Err(NoSolution); + }; + + let certainty = ecx.is_transmutable( + rustc_transmute::Types { dst: substs.type_at(0), src: substs.type_at(1) }, + substs.type_at(2), + assume, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(certainty) } } impl<'tcx> EvalCtxt<'_, 'tcx> { + // 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<QueryResult<'tcx>> { + 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 constitutent + // types, and therefore have no built-in auto impl. + ty::Dynamic(..) + | ty::Param(..) + | ty::Foreign(..) + | ty::Alias(ty::Projection, ..) + | ty::Placeholder(..) => Some(Err(NoSolution)), + + ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"), + + // Generators have one special built-in candidate, `Unpin`, which + // takes precedence over the structural auto trait candidate being + // assembled. + ty::Generator(_, _, 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::Generator(_, _, _) + | ty::GeneratorWitness(_) + | ty::GeneratorWitnessMIR(_, _) + | ty::Never + | ty::Tuple(_) + | ty::Adt(_, _) + // FIXME: Handling opaques here is kinda sus. Especially because we + // simplify them to PlaceholderSimplifiedType. + | ty::Alias(ty::Opaque, _) => { + if let Some(def_id) = self.tcx().find_map_relevant_impl( + goal.predicate.def_id(), + goal.predicate.self_ty(), + TreatProjections::NextSolverLookup, + Some, + ) { + 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 @@ -524,26 +672,28 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, TraitPredicate<'tcx>>, constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>, ) -> QueryResult<'tcx> { - self.probe(|this| { - this.evaluate_all_and_make_canonical_response( - constituent_tys(this, goal.predicate.self_ty())? + self.probe(|ecx| { + ecx.add_goals( + constituent_tys(ecx, goal.predicate.self_ty())? .into_iter() .map(|ty| { goal.with( - this.tcx(), - ty::Binder::dummy(goal.predicate.with_self_ty(this.tcx(), ty)), + ecx.tcx(), + ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)), ) }) - .collect(), - ) + .collect::<Vec<_>>(), + ); + 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_and_discard_reservation_impls(candidates) + self.merge_candidates(candidates) } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 1fb8659bb..182d995c4 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -7,8 +7,8 @@ use crate::errors::UnableToConstructConstantValue; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::InferCtxt; use crate::traits::project::ProjectAndUnifyResult; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ImplPolarity, Region, RegionVid}; @@ -179,13 +179,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { // At this point, we already have all of the bounds we need. FulfillmentContext is used // to store all of the necessary region/lifetime bounds in the InferContext, as well as // an additional sanity check. - let errors = - super::fully_solve_bound(&infcx, ObligationCause::dummy(), full_env, ty, trait_did); + let ocx = ObligationCtxt::new(&infcx); + ocx.register_bound(ObligationCause::dummy(), full_env, ty, trait_did); + let errors = ocx.select_all_or_error(); if !errors.is_empty() { panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors); } - infcx.process_registered_region_obligations(&Default::default(), full_env); + let outlives_env = OutlivesEnvironment::new(full_env); + infcx.process_registered_region_obligations(&outlives_env); let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().region_constraint_data().clone(); @@ -232,7 +234,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// constructed once for a given type. As part of the construction process, the `ParamEnv` will /// have any supertrait bounds normalized -- e.g., if we have a type `struct Foo<T: Copy>`, the /// `ParamEnv` will contain `T: Copy` and `T: Clone`, since `Copy: Clone`. When we construct our - /// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate_predicates`, or + /// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate`, or /// else `SelectionContext` will choke on the missing predicates. However, this should never /// show up in the final synthesized generics: we don't want our generated docs page to contain /// something like `T: Copy + Clone`, as that's redundant. Therefore, we keep track of a @@ -344,11 +346,8 @@ impl<'tcx> AutoTraitFinder<'tcx> { _ => panic!("Unexpected error for '{:?}': {:?}", ty, result), }; - let normalized_preds = elaborate_predicates( - tcx, - computed_preds.clone().chain(user_computed_preds.iter().cloned()), - ) - .map(|o| o.predicate); + let normalized_preds = + elaborate(tcx, computed_preds.clone().chain(user_computed_preds.iter().cloned())); new_env = ty::ParamEnv::new( tcx.mk_predicates_from_iter(normalized_preds), param_env.reveal(), @@ -814,7 +813,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { - match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2) + match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(DefineOpaqueTypes::No,c1, c2) { Ok(_) => (), Err(_) => return false, @@ -830,7 +829,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // the `ParamEnv`. ty::PredicateKind::WellFormed(..) | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) - | ty::PredicateKind::AliasEq(..) + | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) @@ -852,23 +851,3 @@ impl<'tcx> AutoTraitFinder<'tcx> { infcx.freshen(p) } } - -/// Replaces all ReVars in a type with ty::Region's, using the provided map -pub struct RegionReplacer<'a, 'tcx> { - vid_to_region: &'a FxHashMap<ty::RegionVid, ty::Region<'tcx>>, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for RegionReplacer<'a, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - (match *r { - ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned(), - _ => None, - }) - .unwrap_or_else(|| r.super_fold_with(self)) - } -} diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index b42a49eb4..28967e1cc 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -40,13 +40,16 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { self.obligations.insert(obligation); } - fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> { + fn collect_remaining_errors( + &mut self, + _infcx: &InferCtxt<'tcx>, + ) -> Vec<FulfillmentError<'tcx>> { // any remaining obligations are errors self.obligations .iter() .map(|obligation| FulfillmentError { obligation: obligation.clone(), - code: FulfillmentErrorCode::CodeAmbiguity, + code: FulfillmentErrorCode::CodeAmbiguity { overflow: false }, // FIXME - does Chalk have a notation of 'root obligation'? // This is just for diagnostics, so it's okay if this is wrong root_obligation: obligation.clone(), diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 6b688c322..20c2605f2 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -17,7 +17,7 @@ use crate::traits::{ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::{DefineOpaqueTypes, DefiningAnchor, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; @@ -75,12 +75,13 @@ pub fn overlapping_impls( // Before doing expensive operations like entering an inference context, do // a quick check via fast_reject to tell if the impl headers could possibly // unify. - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer }; + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }; let impl1_ref = tcx.impl_trait_ref(impl1_def_id); let impl2_ref = tcx.impl_trait_ref(impl2_def_id); let may_overlap = match (impl1_ref, impl2_ref) { - (Some(a), Some(b)) => iter::zip(a.skip_binder().substs, b.skip_binder().substs) - .all(|(arg1, arg2)| drcx.generic_args_may_unify(arg1, arg2)), + (Some(a), Some(b)) => { + drcx.substs_refs_may_unify(a.skip_binder().substs, b.skip_binder().substs) + } (None, None) => { let self_ty1 = tcx.type_of(impl1_def_id).skip_binder(); let self_ty2 = tcx.type_of(impl2_def_id).skip_binder(); @@ -95,8 +96,11 @@ pub fn overlapping_impls( return None; } - let infcx = - tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).intercrate().build(); + let infcx = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bubble) + .intercrate(true) + .build(); let selcx = &mut SelectionContext::new(&infcx); let overlaps = overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some(); @@ -107,8 +111,11 @@ pub fn overlapping_impls( // In the case where we detect an error, run the check again, but // this time tracking intercrate ambiguity causes for better // diagnostics. (These take time and can lead to false errors.) - let infcx = - tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).intercrate().build(); + let infcx = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bubble) + .intercrate(true) + .build(); let selcx = &mut SelectionContext::new(&infcx); selcx.enable_tracking_intercrate_ambiguity_causes(); Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap()) @@ -181,7 +188,7 @@ fn overlap_within_probe<'cx, 'tcx>( let impl1_header = with_fresh_ty_vars(selcx, param_env, impl1_def_id); let impl2_header = with_fresh_ty_vars(selcx, param_env, impl2_def_id); - let obligations = equate_impl_headers(selcx, &impl1_header, &impl2_header)?; + let obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?; debug!("overlap: unification check succeeded"); if overlap_mode.use_implicit_negative() { @@ -207,20 +214,25 @@ fn overlap_within_probe<'cx, 'tcx>( Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) } -fn equate_impl_headers<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - impl1_header: &ty::ImplHeader<'tcx>, - impl2_header: &ty::ImplHeader<'tcx>, +#[instrument(level = "debug", skip(infcx), ret)] +fn equate_impl_headers<'tcx>( + infcx: &InferCtxt<'tcx>, + impl1: &ty::ImplHeader<'tcx>, + impl2: &ty::ImplHeader<'tcx>, ) -> Option<PredicateObligations<'tcx>> { - // Do `a` and `b` unify? If not, no overlap. - debug!("equate_impl_headers(impl1_header={:?}, impl2_header={:?}", impl1_header, impl2_header); - selcx - .infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .define_opaque_types(true) - .eq_impl_headers(impl1_header, impl2_header) - .map(|infer_ok| infer_ok.obligations) - .ok() + let result = match (impl1.trait_ref, impl2.trait_ref) { + (Some(impl1_ref), Some(impl2_ref)) => infcx + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .eq(DefineOpaqueTypes::Yes, impl1_ref, impl2_ref), + (None, None) => infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( + DefineOpaqueTypes::Yes, + impl1.self_ty, + impl2.self_ty, + ), + _ => bug!("mk_eq_impl_headers given mismatched impl kinds"), + }; + + result.map(|infer_ok| infer_ok.obligations).ok() } /// Given impl1 and impl2 check if both impls can be satisfied by a common type (including @@ -294,7 +306,7 @@ fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> b &infcx, ObligationCause::dummy(), impl_env, - tcx.impl_subject(impl1_def_id), + tcx.impl_subject(impl1_def_id).subst_identity(), ) { Ok(s) => s, Err(err) => { @@ -325,7 +337,7 @@ fn equate<'tcx>( ) -> bool { // do the impls unify? If not, not disjoint. let Ok(InferOk { obligations: more_obligations, .. }) = - infcx.at(&ObligationCause::dummy(), impl_env).eq(subject1, subject2) + infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No,subject1, subject2) else { debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2); return true; @@ -356,8 +368,8 @@ fn negative_impl_exists<'tcx>( } // Try to prove a negative obligation exists for super predicates - for o in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) { - if resolve_negative_obligation(infcx.fork(), &o, body_def_id) { + for pred in util::elaborate(infcx.tcx, iter::once(o.predicate)) { + if resolve_negative_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) { return true; } } @@ -378,7 +390,10 @@ fn resolve_negative_obligation<'tcx>( }; let param_env = o.param_env; - if !super::fully_solve_obligation(&infcx, o).is_empty() { + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligation(o); + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { return false; } @@ -388,20 +403,29 @@ fn resolve_negative_obligation<'tcx>( let wf_tys = ocx.assumed_wf_types(param_env, DUMMY_SP, body_def_id); let outlives_env = OutlivesEnvironment::with_bounds( param_env, - Some(&infcx), infcx.implied_bounds_tys(param_env, body_def_id, wf_tys), ); - - infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env); - infcx.resolve_regions(&outlives_env).is_empty() } +/// Returns whether all impls which would apply to the `trait_ref` +/// e.g. `Ty: Trait<Arg>` are already known in the local crate. +/// +/// This both checks whether any downstream or sibling crates could +/// implement it and whether an upstream crate can add this impl +/// without breaking backwards compatibility. #[instrument(level = "debug", skip(tcx), ret)] pub fn trait_ref_is_knowable<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, ) -> Result<(), Conflict> { + if Some(trait_ref.def_id) == tcx.lang_items().fn_ptr_trait() { + // The only types implementing `FnPtr` are function pointers, + // so if there's no impl of `FnPtr` in the current crate, + // then such an impl will never be added in the future. + return Ok(()); + } + if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() { // A downstream or cousin crate is allowed to implement some // substitution of this trait-ref. diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index b20636174..2beebe94b 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -6,12 +6,14 @@ use super::{ChalkFulfillmentContext, FulfillmentContext}; use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt; use crate::traits::NormalizeExt; use rustc_data_structures::fx::FxIndexSet; +use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; -use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_infer::traits::query::Fallible; use rustc_infer::traits::{ FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _, @@ -128,8 +130,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { { self.infcx .at(cause, param_env) - .define_opaque_types(true) - .eq_exp(a_is_expected, a, b) + .eq_exp(DefineOpaqueTypes::Yes, a_is_expected, a, b) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } @@ -142,8 +143,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { ) -> Result<(), TypeError<'tcx>> { self.infcx .at(cause, param_env) - .define_opaque_types(true) - .eq(expected, actual) + .eq(DefineOpaqueTypes::Yes, expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } @@ -157,8 +157,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { ) -> Result<(), TypeError<'tcx>> { self.infcx .at(cause, param_env) - .define_opaque_types(true) - .sup(expected, actual) + .sub(DefineOpaqueTypes::Yes, expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } @@ -172,19 +171,37 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { ) -> Result<(), TypeError<'tcx>> { self.infcx .at(cause, param_env) - .define_opaque_types(true) - .sup(expected, actual) + .sup(DefineOpaqueTypes::Yes, expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } + #[must_use] pub fn select_where_possible(&self) -> Vec<FulfillmentError<'tcx>> { self.engine.borrow_mut().select_where_possible(self.infcx) } + #[must_use] pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> { self.engine.borrow_mut().select_all_or_error(self.infcx) } + /// Resolves regions and reports errors. + /// + /// Takes ownership of the context as doing trait solving afterwards + /// will result in region constraints getting ignored. + pub fn resolve_regions_and_report_errors( + self, + generic_param_scope: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, + ) -> Result<(), ErrorGuaranteed> { + let errors = self.infcx.resolve_regions(&outlives_env); + if errors.is_empty() { + Ok(()) + } else { + Err(self.infcx.err_ctxt().report_region_errors(generic_param_scope, &errors)) + } + } + pub fn assumed_wf_types( &self, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs index 84045c4d0..0475f24d8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs @@ -1,6 +1,6 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime}; -use rustc_infer::traits::util::elaborate_predicates_with_span; +use rustc_infer::traits::util::elaborate; use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation}; use rustc_middle::ty; use rustc_span::{Span, DUMMY_SP}; @@ -82,15 +82,15 @@ pub fn recompute_applicable_impls<'tcx>( let predicates = tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx); - for obligation in elaborate_predicates_with_span(tcx, predicates.into_iter()) { - let kind = obligation.predicate.kind(); + for (pred, span) in elaborate(tcx, predicates.into_iter()) { + let kind = pred.kind(); if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder() && param_env_candidate_may_apply(kind.rebind(trait_pred)) { if kind.rebind(trait_pred.trait_ref) == ty::TraitRef::identity(tcx, trait_pred.def_id()) { ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id()))) } else { - ambiguities.push(Ambiguity::ParamEnv(obligation.cause.span)) + ambiguities.push(Ambiguity::ParamEnv(span)) } } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs index 1174efdbf..7e1dba4ed 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs @@ -21,10 +21,6 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> { self.infcx.tcx } - fn intercrate(&self) -> bool { - false - } - fn param_env(&self) -> ty::ParamEnv<'tcx> { self.param_env } @@ -33,10 +29,6 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> { true } - fn mark_ambiguous(&mut self) { - bug!() - } - fn relate_with_variance<T: Relate<'tcx>>( &mut self, _: ty::Variance, @@ -92,6 +84,11 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> { } impl<'tcx> ObligationEmittingRelation<'tcx> for CollectAllMismatches<'_, 'tcx> { + fn alias_relate_direction(&self) -> ty::AliasRelationDirection { + // FIXME(deferred_projection_equality): We really should get rid of this relation. + ty::AliasRelationDirection::Equate + } + fn register_obligations(&mut self, _obligations: PredicateObligations<'tcx>) { // FIXME(deferred_projection_equality) } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index a844a1494..1b741b730 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -24,16 +24,15 @@ use rustc_errors::{ }; use rustc_hir as hir; use rustc_hir::def::Namespace; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::GenericParam; -use rustc_hir::Item; -use rustc_hir::Node; +use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print}; use rustc_middle::ty::{ @@ -126,11 +125,9 @@ pub trait TypeErrCtxtExt<'tcx> { + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug; - fn report_fulfillment_errors( - &self, - errors: &[FulfillmentError<'tcx>], - body_id: Option<hir::BodyId>, - ) -> ErrorGuaranteed; + fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed; + + fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed; fn report_overflow_obligation<T>( &self, @@ -388,11 +385,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { } impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { - fn report_fulfillment_errors( - &self, - errors: &[FulfillmentError<'tcx>], - body_id: Option<hir::BodyId>, - ) -> ErrorGuaranteed { + fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed { #[derive(Debug)] struct ErrorDescriptor<'tcx> { predicate: ty::Predicate<'tcx>, @@ -469,7 +462,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for from_expansion in [false, true] { for (error, suppressed) in iter::zip(errors, &is_suppressed) { if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { - self.report_fulfillment_error(error, body_id); + self.report_fulfillment_error(error); } } } @@ -611,6 +604,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } + fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed { + let obligation = self.resolve_vars_if_possible(obligation); + let mut err = self.build_overflow_error(&obligation.predicate, obligation.cause.span, true); + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + err.emit() + } + fn report_selection_error( &self, mut obligation: PredicateObligation<'tcx>, @@ -672,6 +673,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } let trait_ref = trait_predicate.to_poly_trait_ref(); + let (post_message, pre_message, type_def) = self .get_parent_trait_ref(obligation.cause.code()) .map(|(t, s)| { @@ -711,33 +713,45 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { (message, note, append_const_msg) }; - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0277, - "{}", - message - .and_then(|cannot_do_this| { - match (predicate_is_const, append_const_msg) { - // do nothing if predicate is not const - (false, _) => Some(cannot_do_this), - // suggested using default post message - (true, Some(None)) => { - Some(format!("{cannot_do_this} in const contexts")) - } - // overridden post message - (true, Some(Some(post_message))) => { - Some(format!("{cannot_do_this}{post_message}")) - } - // fallback to generic message - (true, None) => None, + let err_msg = message + .and_then(|cannot_do_this| { + match (predicate_is_const, append_const_msg) { + // do nothing if predicate is not const + (false, _) => Some(cannot_do_this), + // suggested using default post message + (true, Some(None)) => { + Some(format!("{cannot_do_this} in const contexts")) } - }) - .unwrap_or_else(|| format!( + // overridden post message + (true, Some(Some(post_message))) => { + Some(format!("{cannot_do_this}{post_message}")) + } + // fallback to generic message + (true, None) => None, + } + }) + .unwrap_or_else(|| { + format!( "the trait bound `{}` is not satisfied{}", trait_predicate, post_message, - )) - ); + ) + }); + + let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id()) + == self.tcx.lang_items().transmute_trait() + { + // Recompute the safe transmute reason and use that for the error reporting + self.get_safe_transmute_error_and_reason( + trait_predicate, + obligation.clone(), + trait_ref, + span, + ) + } else { + (err_msg, None) + }; + + let mut err = struct_span_err!(self.tcx.sess, span, E0277, "{}", err_msg); if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { err.span_label( @@ -827,6 +841,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // at the type param with a label to suggest constraining it. err.help(&explanation); } + } else if let Some(custom_explanation) = safe_transmute_explanation { + err.span_label(span, custom_explanation); } else { err.span_label(span, explanation); } @@ -955,8 +971,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } - let body_hir_id = - self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id); + let body_def_id = obligation.cause.body_id; // Try to report a help message if is_fn_trait && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( @@ -1035,9 +1050,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Can't show anything else useful, try to find similar impls. let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( - impl_candidates, + &impl_candidates, trait_ref, - body_hir_id, + body_def_id, &mut err, true, ) { @@ -1071,14 +1086,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let impl_candidates = self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( - impl_candidates, + &impl_candidates, trait_ref, - body_hir_id, + body_def_id, &mut err, true, ); } } + + self.maybe_suggest_convert_to_slice( + &mut err, + trait_ref, + impl_candidates.as_slice(), + span, + ); } // Changing mutability doesn't make a difference to whether we have @@ -1279,16 +1301,26 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "TypeWellFormedFromEnv predicate should only exist in the environment" ), - ty::PredicateKind::AliasEq(..) => span_bug!( + ty::PredicateKind::AliasRelate(..) => span_bug!( span, - "AliasEq predicate should never be the predicate cause of a SelectionError" + "AliasRelate predicate should never be the predicate cause of a SelectionError" ), ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { - self.tcx.sess.struct_span_err( + let mut diag = self.tcx.sess.struct_span_err( span, &format!("the constant `{}` is not of type `{}`", ct, ty), - ) + ); + self.note_type_err( + &mut diag, + &obligation.cause, + None, + None, + TypeError::Sorts(ty::error::ExpectedFound::new(true, ty, ct.ty())), + false, + false, + ); + diag } } } @@ -1494,11 +1526,7 @@ trait InferCtxtPrivExt<'tcx> { // `error` occurring implies that `cond` occurs. fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; - fn report_fulfillment_error( - &self, - error: &FulfillmentError<'tcx>, - body_id: Option<hir::BodyId>, - ); + fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>); fn report_projection_error( &self, @@ -1529,9 +1557,9 @@ trait InferCtxtPrivExt<'tcx> { fn report_similar_impl_candidates( &self, - impl_candidates: Vec<ImplCandidate<'tcx>>, + impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, - body_id: hir::HirId, + body_def_id: LocalDefId, err: &mut Diagnostic, other: bool, ) -> bool; @@ -1561,11 +1589,7 @@ trait InferCtxtPrivExt<'tcx> { trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, ) -> PredicateObligation<'tcx>; - fn maybe_report_ambiguity( - &self, - obligation: &PredicateObligation<'tcx>, - body_id: Option<hir::BodyId>, - ); + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>); fn predicate_can_apply( &self, @@ -1602,6 +1626,14 @@ trait InferCtxtPrivExt<'tcx> { obligated_types: &mut Vec<Ty<'tcx>>, cause_code: &ObligationCauseCode<'tcx>, ) -> bool; + + fn get_safe_transmute_error_and_reason( + &self, + trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + obligation: Obligation<'tcx, ty::Predicate<'tcx>>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + span: Span, + ) -> (String, Option<String>); } impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { @@ -1625,8 +1657,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } }; - for obligation in super::elaborate_predicates(self.tcx, std::iter::once(cond)) { - let bound_predicate = obligation.predicate.kind(); + for pred in super::elaborate(self.tcx, std::iter::once(cond)) { + let bound_predicate = pred.kind(); if let ty::PredicateKind::Clause(ty::Clause::Trait(implication)) = bound_predicate.skip_binder() { @@ -1647,11 +1679,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn report_fulfillment_error( - &self, - error: &FulfillmentError<'tcx>, - body_id: Option<hir::BodyId>, - ) { + fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) { match error.code { FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { self.report_selection_error( @@ -1663,8 +1691,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { FulfillmentErrorCode::CodeProjectionError(ref e) => { self.report_projection_error(&error.obligation, e); } - FulfillmentErrorCode::CodeAmbiguity => { - self.maybe_report_ambiguity(&error.obligation, body_id); + FulfillmentErrorCode::CodeAmbiguity { overflow: false } => { + self.maybe_report_ambiguity(&error.obligation); + } + FulfillmentErrorCode::CodeAmbiguity { overflow: true } => { + self.report_overflow_no_abort(error.obligation.clone()); } FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { self.report_mismatched_types( @@ -1768,7 +1799,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // constrain inference variables a bit more to nested obligations from normalize so // we can have more helpful errors. - ocx.select_where_possible(); + // + // we intentionally drop errors from normalization here, + // since the normalization is just done to improve the error message. + let _ = ocx.select_where_possible(); if let Err(new_err) = ocx.eq_exp( &obligation.cause, @@ -1815,12 +1849,17 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }) .and_then(|(trait_assoc_item, id)| { let trait_assoc_ident = trait_assoc_item.ident(self.tcx); - self.tcx.find_map_relevant_impl(id, proj.projection_ty.self_ty(), |did| { - self.tcx - .associated_items(did) - .in_definition_order() - .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident) - }) + self.tcx.find_map_relevant_impl( + id, + proj.projection_ty.self_ty(), + TreatProjections::ForLookup, + |did| { + self.tcx + .associated_items(did) + .in_definition_order() + .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident) + }, + ) }) .and_then(|item| match self.tcx.hir().get_if_local(item.def_id) { Some( @@ -2027,9 +2066,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn report_similar_impl_candidates( &self, - impl_candidates: Vec<ImplCandidate<'tcx>>, + impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, - body_id: hir::HirId, + body_def_id: LocalDefId, err: &mut Diagnostic, other: bool, ) -> bool { @@ -2120,9 +2159,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // FIXME(compiler-errors): This could be generalized, both to // be more granular, and probably look past other `#[fundamental]` // types, too. - self.tcx - .visibility(def.did()) - .is_accessible_from(body_id.owner.def_id, self.tcx) + self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) } else { true } @@ -2138,7 +2175,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Prefer more similar candidates first, then sort lexicographically // by their normalized string representation. let mut normalized_impl_candidates_and_similarities = impl_candidates - .into_iter() + .iter() + .copied() .map(|ImplCandidate { trait_ref, similarity }| { // FIXME(compiler-errors): This should be using `NormalizeExt::normalize` let normalized = self @@ -2193,7 +2231,12 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_ref: &ty::PolyTraitRef<'tcx>, ) -> bool { let get_trait_impl = |trait_def_id| { - self.tcx.find_map_relevant_impl(trait_def_id, trait_ref.skip_binder().self_ty(), Some) + self.tcx.find_map_relevant_impl( + trait_def_id, + trait_ref.skip_binder().self_ty(), + TreatProjections::ForLookup, + Some, + ) }; let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); let traits_with_same_path: std::collections::BTreeSet<_> = self @@ -2231,11 +2274,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn maybe_report_ambiguity( - &self, - obligation: &PredicateObligation<'tcx>, - body_id: Option<hir::BodyId>, - ) { + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) { // Unable to successfully determine, probably means // insufficient type information, but could mean // ambiguous impls. The latter *ought* to be a @@ -2277,7 +2316,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { if let None = self.tainted_by_errors() { self.emit_inference_failure_err( - body_id, + obligation.cause.body_id, span, trait_ref.self_ty().skip_binder().into(), ErrorCode::E0282, @@ -2304,7 +2343,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let subst = data.trait_ref.substs.iter().find(|s| s.has_non_region_infer()); let mut err = if let Some(subst) = subst { - self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0283, true) + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + subst, + ErrorCode::E0283, + true, + ) } else { struct_span_err!( self.tcx.sess, @@ -2348,12 +2393,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { predicate.to_opt_poly_trait_pred().unwrap(), ); if impl_candidates.len() < 10 { - let hir = - self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id); self.report_similar_impl_candidates( - impl_candidates, + impl_candidates.as_slice(), trait_ref, - body_id.map(|id| id.hir_id).unwrap_or(hir), + obligation.cause.body_id, &mut err, false, ); @@ -2375,8 +2418,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); } - if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) = - (body_id, subst.map(|subst| subst.unpack())) + if let Some(ty::subst::GenericArgKind::Type(_)) = subst.map(|subst| subst.unpack()) + && let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) { let mut expr_finder = FindExprBySpan::new(span); expr_finder.visit_expr(&self.tcx.hir().body(body_id).value); @@ -2473,7 +2516,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } - self.emit_inference_failure_err(body_id, span, arg, ErrorCode::E0282, false) + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + ErrorCode::E0282, + false, + ) } ty::PredicateKind::Subtype(data) => { @@ -2487,7 +2536,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let SubtypePredicate { a_is_expected: _, a, b } = data; // both must be type variables, or the other would've been instantiated assert!(a.is_ty_var() && b.is_ty_var()); - self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282, true) + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + a.into(), + ErrorCode::E0282, + true, + ) } ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { if predicate.references_error() || self.tainted_by_errors().is_some() { @@ -2501,7 +2556,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .find(|g| g.has_non_region_infer()); if let Some(subst) = subst { let mut err = self.emit_inference_failure_err( - body_id, + obligation.cause.body_id, span, subst, ErrorCode::E0284, @@ -2530,7 +2585,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let subst = data.walk().find(|g| g.is_non_region_infer()); if let Some(subst) = subst { let err = self.emit_inference_failure_err( - body_id, + obligation.cause.body_id, span, subst, ErrorCode::E0284, @@ -2863,6 +2918,63 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } false } + + fn get_safe_transmute_error_and_reason( + &self, + trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + obligation: Obligation<'tcx, ty::Predicate<'tcx>>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + span: Span, + ) -> (String, Option<String>) { + let src_and_dst = trait_predicate.map_bound(|p| rustc_transmute::Types { + dst: p.trait_ref.substs.type_at(0), + src: p.trait_ref.substs.type_at(1), + }); + let scope = trait_ref.skip_binder().substs.type_at(2); + let Some(assume) = + rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, trait_ref.skip_binder().substs.const_at(3)) else { + span_bug!(span, "Unable to construct rustc_transmute::Assume where it was previously possible"); + }; + match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( + obligation.cause, + src_and_dst, + scope, + assume, + ) { + rustc_transmute::Answer::No(reason) => { + let dst = trait_ref.skip_binder().substs.type_at(0); + let src = trait_ref.skip_binder().substs.type_at(1); + let custom_err_msg = format!("`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`").to_string(); + let reason_msg = match reason { + rustc_transmute::Reason::SrcIsUnspecified => { + format!("`{src}` does not have a well-specified layout").to_string() + } + rustc_transmute::Reason::DstIsUnspecified => { + format!("`{dst}` does not have a well-specified layout").to_string() + } + rustc_transmute::Reason::DstIsBitIncompatible => { + format!("At least one value of `{src}` isn't a bit-valid value of `{dst}`") + .to_string() + } + rustc_transmute::Reason::DstIsPrivate => format!( + "`{dst}` is or contains a type or field that is not visible in that scope" + ) + .to_string(), + // FIXME(bryangarza): Include the number of bytes of src and dst + rustc_transmute::Reason::DstIsTooBig => { + format!("The size of `{src}` is smaller than the size of `{dst}`") + } + }; + (custom_err_msg, Some(reason_msg)) + } + // Should never get a Yes at this point! We already ran it before, and did not get a Yes. + rustc_transmute::Answer::Yes => span_bug!( + span, + "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", + ), + _ => span_bug!(span, "Unsupported rustc_transmute::Reason variant"), + } + } } /// Crude way of getting back an `Expr` from a `Span`. diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index b3bf9ad59..a9c4e1268 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -149,9 +149,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().substs)); let trait_ref = trait_ref.skip_binder(); - let body_hir = self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id); - let mut flags = - vec![(sym::ItemContext, self.describe_enclosure(body_hir).map(|s| s.to_owned()))]; + let mut flags = vec![]; + // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs, + // but I guess we could synthesize one here. We don't see any errors that rely on + // that yet, though. + let enclosure = + if let Some(body_hir) = self.tcx.opt_local_def_id_to_hir_id(obligation.cause.body_id) { + self.describe_enclosure(body_hir).map(|s| s.to_owned()) + } else { + None + }; + flags.push((sym::ItemContext, enclosure)); match obligation.cause.code() { ObligationCauseCode::BuiltinDerivedObligation(..) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 66d74fd05..fb75ec767 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,7 +1,7 @@ // ignore-tidy-filelength use super::{ - DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode, + DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, }; @@ -30,7 +30,7 @@ use rustc_middle::hir::map; use rustc_middle::ty::error::TypeError::{self, Sorts}; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::{ - self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, + self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, InternalSubsts, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, TypeckResults, @@ -212,7 +212,7 @@ pub trait TypeErrCtxtExt<'tcx> { fn extract_callable_info( &self, - hir_id: HirId, + body_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, found: Ty<'tcx>, ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)>; @@ -382,6 +382,14 @@ pub trait TypeErrCtxtExt<'tcx> { body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>; + + fn maybe_suggest_convert_to_slice( + &self, + err: &mut Diagnostic, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + candidate_impls: &[ImplCandidate<'tcx>], + span: Span, + ); } fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) { @@ -412,6 +420,7 @@ fn suggest_restriction<'tcx>( ) { if hir_generics.where_clause_span.from_expansion() || hir_generics.where_clause_span.desugaring_kind().is_some() + || projection.map_or(false, |projection| tcx.opt_rpitit_info(projection.def_id).is_some()) { return; } @@ -900,9 +909,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_pred.self_ty(), ); - let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id); let Some((def_id_or_name, output, inputs)) = self.extract_callable_info( - body_hir_id, + obligation.cause.body_id, obligation.param_env, self_ty, ) else { return false; }; @@ -1104,10 +1112,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { /// Extracts information about a callable type for diagnostics. This is a /// heuristic -- it doesn't necessarily mean that a type is always callable, /// because the callable type must also be well-formed to be called. - // FIXME(vincenzopalazzo): move the HirId to a LocalDefId fn extract_callable_info( &self, - hir_id: HirId, + body_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, found: Ty<'tcx>, ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { @@ -1159,7 +1166,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }) } ty::Param(param) => { - let generics = self.tcx.generics_of(hir_id.owner.to_def_id()); + let generics = self.tcx.generics_of(body_id); let name = if generics.count() > param.index as usize && let def = generics.param_at(param.index as usize, self.tcx) && matches!(def.kind, ty::GenericParamDefKind::Type { .. }) @@ -1349,6 +1356,31 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Applicability::MaybeIncorrect, ); } else { + let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; + let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" }); + let sugg_msg = &format!( + "consider{} borrowing here", + if is_mut { " mutably" } else { "" } + ); + + // Issue #109436, we need to add parentheses properly for method calls + // for example, `foo.into()` should be `(&foo).into()` + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet( + self.tcx.sess.source_map().span_look_ahead(span, Some("."), Some(50)), + ) { + if snippet == "." { + err.multipart_suggestion_verbose( + sugg_msg, + vec![ + (span.shrink_to_lo(), format!("({}", sugg_prefix)), + (span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + return true; + } + } + // Issue #104961, we need to add parentheses properly for compond expressions // for example, `x.starts_with("hi".to_string() + "you")` // should be `x.starts_with(&("hi".to_string() + "you"))` @@ -1365,14 +1397,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { _ => false, }; - let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; let span = if needs_parens { span } else { span.shrink_to_lo() }; - let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" }); - let sugg_msg = &format!( - "consider{} borrowing here", - if is_mut { " mutably" } else { "" } - ); - let suggestions = if !needs_parens { vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))] } else { @@ -2220,7 +2245,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // - `BuiltinDerivedObligation` with a generator witness (A) // - `BuiltinDerivedObligation` with a generator (A) // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) - // - `BindingObligation` with `impl_send (Send requirement) + // - `BindingObligation` with `impl_send` (Send requirement) // // The first obligation in the chain is the most useful and has the generator that captured // the type. The last generator (`outer_generator` below) has information about where the @@ -2936,9 +2961,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ObligationCauseCode::SizedYieldType => { err.note("the yield type of a generator must have a statically known size"); } - ObligationCauseCode::SizedBoxType => { - err.note("the type of a box expression must have a statically known size"); - } ObligationCauseCode::AssignmentLhsSized => { err.note("the left-hand-side of an assignment must have a statically known size"); } @@ -3025,8 +3047,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } }; - let identity_future = tcx.require_lang_item(LangItem::IdentityFuture, None); - // Don't print the tuple of capture types 'print: { if !is_upvar_tys_infer_tuple { @@ -3039,12 +3059,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { None => err.note(&msg), }, ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { - // Avoid printing the future from `core::future::identity_future`, it's not helpful - if tcx.parent(*def_id) == identity_future { - break 'print; - } - - // If the previous type is `identity_future`, this is the future generated by the body of an async function. + // If the previous type is async fn, this is the future generated by the body of an async function. // Avoid printing it twice (it was already printed in the `ty::Generator` arm below). let is_future = tcx.ty_is_opaque_future(ty); debug!( @@ -3826,6 +3841,72 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } assocs_in_this_method } + + /// If the type that failed selection is an array or a reference to an array, + /// but the trait is implemented for slices, suggest that the user converts + /// the array into a slice. + fn maybe_suggest_convert_to_slice( + &self, + err: &mut Diagnostic, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + candidate_impls: &[ImplCandidate<'tcx>], + span: Span, + ) { + // Three cases where we can make a suggestion: + // 1. `[T; _]` (array of T) + // 2. `&[T; _]` (reference to array of T) + // 3. `&mut [T; _]` (mutable reference to array of T) + let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() { + ty::Array(element_ty, _) => (element_ty, None), + + ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() { + ty::Array(element_ty, _) => (element_ty, Some(mutability)), + _ => return, + }, + + _ => return, + }; + + // Go through all the candidate impls to see if any of them is for + // slices of `element_ty` with `mutability`. + let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() { + ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => { + if matches!(*t.kind(), ty::Slice(e) if e == element_ty) + && m == mutability.unwrap_or(m) + { + // Use the candidate's mutability going forward. + mutability = Some(m); + true + } else { + false + } + } + _ => false, + }; + + // Grab the first candidate that matches, if any, and make a suggestion. + if let Some(slice_ty) = candidate_impls + .iter() + .map(|trait_ref| trait_ref.trait_ref.self_ty()) + .find(|t| is_slice(*t)) + { + let msg = &format!("convert the array to a `{}` slice instead", slice_ty); + + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let mut suggestions = vec![]; + if snippet.starts_with('&') { + } else if let Some(hir::Mutability::Mut) = mutability { + suggestions.push((span.shrink_to_lo(), "&mut ".into())); + } else { + suggestions.push((span.shrink_to_lo(), "&".into())); + } + suggestions.push((span.shrink_to_hi(), "[..]".into())); + err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect); + } else { + err.span_help(span, msg); + } + } + } } /// Add a hint to add a missing borrow or remove an unnecessary one. @@ -3854,7 +3935,7 @@ fn hint_missing_borrow<'tcx>( // This could be a variant constructor, for example. let Some(fn_decl) = found_node.fn_decl() else { return; }; - let args = fn_decl.inputs.iter().map(|ty| ty); + let args = fn_decl.inputs.iter(); fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { let mut refs = vec![]; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 944436ab8..26cadab3e 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -2,6 +2,7 @@ use crate::infer::{InferCtxt, TyOrConstInferVar}; use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::ProjectionCacheKey; use rustc_infer::traits::{SelectionError, TraitEngine, TraitObligation}; use rustc_middle::mir::interpret::ErrorHandled; @@ -132,8 +133,15 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); } - fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> { - self.predicates.to_errors(CodeAmbiguity).into_iter().map(to_fulfillment_error).collect() + fn collect_remaining_errors( + &mut self, + _infcx: &InferCtxt<'tcx>, + ) -> Vec<FulfillmentError<'tcx>> { + self.predicates + .to_errors(CodeAmbiguity { overflow: false }) + .into_iter() + .map(to_fulfillment_error) + .collect() } fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { @@ -210,6 +218,29 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { type Error = FulfillmentErrorCode<'tcx>; type OUT = Outcome<Self::Obligation, Self::Error>; + /// Compared to `needs_process_obligation` this and its callees + /// contain some optimizations that come at the price of false negatives. + /// + /// They + /// - reduce branching by covering only the most common case + /// - take a read-only view of the unification tables which allows skipping undo_log + /// construction. + /// - bail out on value-cache misses in ena to avoid pointer chasing + /// - hoist RefCell locking out of the loop + #[inline] + fn skippable_obligations<'b>( + &'b self, + it: impl Iterator<Item = &'b Self::Obligation>, + ) -> usize { + let is_unchanged = self.selcx.infcx.is_ty_infer_var_definitely_unchanged(); + + it.take_while(|o| match o.stalled_on.as_slice() { + [o] => is_unchanged(*o), + _ => false, + }) + .count() + } + /// Identifies whether a predicate obligation needs processing. /// /// This is always inlined because it has a single callsite and it is @@ -337,8 +368,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } - ty::PredicateKind::AliasEq(..) => { - bug!("AliasEq is only used for new solver") + ty::PredicateKind::AliasRelate(..) => { + bug!("AliasRelate is only used for new solver") } }, Some(pred) => match pred { @@ -515,7 +546,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) .trace(c1, c2) - .eq(a.substs, b.substs) + .eq(DefineOpaqueTypes::No, a.substs, b.substs) { return ProcessResult::Changed(mk_pending( new_obligations.into_obligations(), @@ -524,8 +555,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } (_, Unevaluated(_)) | (Unevaluated(_), _) => (), (_, _) => { - if let Ok(new_obligations) = - infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2) + if let Ok(new_obligations) = infcx + .at(&obligation.cause, obligation.param_env) + .eq(DefineOpaqueTypes::No, c1, c2) { return ProcessResult::Changed(mk_pending( new_obligations.into_obligations(), @@ -565,12 +597,11 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { - match self - .selcx - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(c1, c2) - { + match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::No, + c1, + c2, + ) { Ok(inf_ok) => { ProcessResult::Changed(mk_pending(inf_ok.into_obligations())) } @@ -606,16 +637,15 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } - ty::PredicateKind::AliasEq(..) => { - bug!("AliasEq is only used for new solver") + ty::PredicateKind::AliasRelate(..) => { + bug!("AliasRelate is only used for new solver") } ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { - match self - .selcx - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(ct.ty(), ty) - { + match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::No, + ct.ty(), + ty, + ) { Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())), Err(_) => ProcessResult::Error(FulfillmentErrorCode::CodeSelectionError( SelectionError::Unimplemented, diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index b94346b09..af567c074 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -87,7 +87,12 @@ pub fn type_allowed_to_implement_copy<'tcx>( }; let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty); let normalization_errors = ocx.select_where_possible(); - if !normalization_errors.is_empty() { + + // NOTE: The post-normalization type may also reference errors, + // such as when we project to a missing type or we have a mismatch + // between expected and found const-generic types. Don't report an + // additional copy error here, since it's not typically useful. + if !normalization_errors.is_empty() || ty.references_error() { tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking Copy implementation")); continue; } @@ -106,17 +111,12 @@ pub fn type_allowed_to_implement_copy<'tcx>( // Check regions assuming the self type of the impl is WF let outlives_env = OutlivesEnvironment::with_bounds( param_env, - Some(&infcx), infcx.implied_bounds_tys( param_env, parent_cause.body_id, FxIndexSet::from_iter([self_type]), ), ); - infcx.process_registered_region_obligations( - outlives_env.region_bound_pairs(), - param_env, - ); let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { infringing.push((field, ty, InfringingFieldsReason::Regions(errors))); diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 4e30108be..8a203dec8 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -4,7 +4,7 @@ pub mod auto_trait; mod chalk_fulfill; -mod coherence; +pub(crate) mod coherence; pub mod const_evaluatable; mod engine; pub mod error_reporting; @@ -28,9 +28,9 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_errors::ErrorGuaranteed; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeSuperVisitable}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeSuperVisitable}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; -use rustc_span::def_id::{DefId, CRATE_DEF_ID}; +use rustc_span::def_id::DefId; use rustc_span::Span; use std::fmt::Debug; @@ -58,17 +58,12 @@ pub use self::specialize::{specialization_graph, translate_substs, OverlapError} pub use self::structural_match::{ search_for_adt_const_param_violation, search_for_structural_match_violation, }; -pub use self::util::{ - elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span, - elaborate_trait_ref, elaborate_trait_refs, -}; +pub use self::util::elaborate; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; -pub use self::util::{ - get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices, -}; +pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; pub use self::util::{ supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_type, - SupertraitDefIds, Supertraits, + SupertraitDefIds, }; pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext; @@ -131,29 +126,23 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, def_id: DefId, - span: Span, ) -> bool { let trait_ref = ty::Binder::dummy(infcx.tcx.mk_trait_ref(def_id, [ty])); - pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref.without_const(), span) + pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref.without_const()) } -#[instrument(level = "debug", skip(infcx, param_env, span, pred), ret)] +/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? +/// +/// Ping me on zulip if you want to use this method and need help with finding +/// an appropriate replacement. +#[instrument(level = "debug", skip(infcx, param_env, pred), ret)] fn pred_known_to_hold_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, pred: impl ToPredicate<'tcx> + TypeVisitable<TyCtxt<'tcx>>, - span: Span, ) -> bool { let has_non_region_infer = pred.has_non_region_infer(); - let obligation = Obligation { - param_env, - // We can use a dummy node-id here because we won't pay any mind - // to region obligations that arise (there shouldn't really be any - // anyhow). - cause: ObligationCause::misc(span, CRATE_DEF_ID), - recursion_depth: 0, - predicate: pred.to_predicate(infcx.tcx), - }; + let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred); let result = infcx.evaluate_obligation_no_overflow(&obligation); debug!(?result); @@ -166,14 +155,13 @@ fn pred_known_to_hold_modulo_regions<'tcx>( // this function's result remains infallible, we must confirm // that guess. While imperfect, I believe this is sound. - // FIXME(@lcnr): this function doesn't seem right. - // // The handling of regions in this area of the code is terrible, // see issue #29149. We should be able to improve on this with // NLL. - let errors = fully_solve_obligation(infcx, obligation); - - match &errors[..] { + let ocx = ObligationCtxt::new(infcx); + ocx.register_obligation(obligation); + let errors = ocx.select_all_or_error(); + match errors.as_slice() { [] => true, errors => { debug!(?errors); @@ -210,7 +198,7 @@ fn do_normalize_predicates<'tcx>( let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) { Ok(predicates) => predicates, Err(errors) => { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); return Err(reported); } }; @@ -276,9 +264,7 @@ pub fn normalize_param_env_or_error<'tcx>( // and errors will get reported then; so outside of type inference we // can be sure that no errors should occur. let mut predicates: Vec<_> = - util::elaborate_predicates(tcx, unnormalized_env.caller_bounds().into_iter()) - .map(|obligation| obligation.predicate) - .collect(); + util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect(); debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); @@ -389,43 +375,6 @@ where Ok(resolved_value) } -/// Process an obligation (and any nested obligations that come from it) to -/// completion, returning any errors -pub fn fully_solve_obligation<'tcx>( - infcx: &InferCtxt<'tcx>, - obligation: PredicateObligation<'tcx>, -) -> Vec<FulfillmentError<'tcx>> { - fully_solve_obligations(infcx, [obligation]) -} - -/// Process a set of obligations (and any nested obligations that come from them) -/// to completion -pub fn fully_solve_obligations<'tcx>( - infcx: &InferCtxt<'tcx>, - obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>, -) -> Vec<FulfillmentError<'tcx>> { - let ocx = ObligationCtxt::new(infcx); - ocx.register_obligations(obligations); - ocx.select_all_or_error() -} - -/// Process a bound (and any nested obligations that come from it) to completion. -/// This is a convenience function for traits that have no generic arguments, such -/// as auto traits, and builtin traits like Copy or Sized. -pub fn fully_solve_bound<'tcx>( - infcx: &InferCtxt<'tcx>, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - bound: DefId, -) -> Vec<FulfillmentError<'tcx>> { - let tcx = infcx.tcx; - let trait_ref = tcx.mk_trait_ref(bound, [ty]); - let obligation = Obligation::new(tcx, cause, param_env, ty::Binder::dummy(trait_ref)); - - fully_solve_obligation(infcx, obligation) -} - /// Normalizes the predicates and checks whether they hold in an empty environment. If this /// returns true, then either normalize encountered an error or one of the predicates did not /// hold. Used when creating vtables to check for unsatisfiable methods. diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 4eacb5211..b8ad1925e 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -8,20 +8,19 @@ //! - not reference the erased type `Self` except for in this receiver; //! - not have generic type parameters. -use super::{elaborate_predicates, elaborate_trait_ref}; +use super::elaborate; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; -use hir::def::DefKind; use rustc_errors::{DelayDm, FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; -use rustc_middle::ty::ToPredicate; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; +use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -140,6 +139,10 @@ fn object_safety_violations_for_trait( if !spans.is_empty() { violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); } + let spans = super_predicates_have_non_lifetime_binders(tcx, trait_def_id); + if !spans.is_empty() { + violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans)); + } violations.extend( tcx.associated_items(trait_def_id) @@ -157,6 +160,7 @@ fn object_safety_violations_for_trait( .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .filter(|item| !tcx.generics_of(item.def_id).params.is_empty()) + .filter(|item| tcx.opt_rpitit_info(item.def_id).is_none()) .map(|item| { let ident = item.ident(tcx); ObjectSafetyViolation::GAT(ident.name, ident.span) @@ -331,7 +335,7 @@ fn predicate_references_self<'tcx>( has_self_ty(&ty.into()).then_some(sp) } - ty::PredicateKind::AliasEq(..) => bug!("`AliasEq` not allowed as assumption"), + ty::PredicateKind::AliasRelate(..) => bug!("`AliasRelate` not allowed as assumption"), ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) @@ -348,6 +352,21 @@ fn predicate_references_self<'tcx>( } } +fn super_predicates_have_non_lifetime_binders( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> SmallVec<[Span; 1]> { + // If non_lifetime_binders is disabled, then exit early + if !tcx.features().non_lifetime_binders { + return SmallVec::new(); + } + tcx.super_predicates_of(trait_def_id) + .predicates + .iter() + .filter_map(|(pred, span)| pred.has_non_region_late_bound().then_some(*span)) + .collect() +} + fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { generics_require_sized_self(tcx, trait_def_id) } @@ -360,26 +379,24 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { // Search for a predicate like `Self : Sized` amongst the trait bounds. let predicates = tcx.predicates_of(def_id); let predicates = predicates.instantiate_identity(tcx).predicates; - elaborate_predicates(tcx, predicates.into_iter()).any(|obligation| { - match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(ref trait_pred)) => { - trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0) - } - ty::PredicateKind::Clause(ty::Clause::Projection(..)) - | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::AliasEq(..) - | ty::PredicateKind::Ambiguous - | ty::PredicateKind::TypeWellFormedFromEnv(..) => false, + elaborate(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(ref trait_pred)) => { + trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0) } + ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::AliasRelate(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::TypeWellFormedFromEnv(..) => false, }) } @@ -649,10 +666,11 @@ fn object_ty_for_trait<'tcx>( }); debug!(?trait_predicate); - let mut elaborated_predicates: Vec<_> = elaborate_trait_ref(tcx, trait_ref) - .filter_map(|obligation| { - debug!(?obligation); - let pred = obligation.predicate.to_opt_poly_projection_pred()?; + let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx); + let mut elaborated_predicates: Vec<_> = elaborate(tcx, [pred]) + .filter_map(|pred| { + debug!(?pred); + let pred = pred.to_opt_poly_projection_pred()?; Some(pred.map_bound(|p| { ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( tcx, p, @@ -854,7 +872,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( } } ty::Alias(ty::Projection, ref data) - if self.tcx.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder => + if self.tcx.is_impl_trait_in_trait(data.def_id) => { // We'll deny these later in their own pass ControlFlow::Continue(()) @@ -921,7 +939,7 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>( ty.skip_binder().walk().find_map(|arg| { if let ty::GenericArgKind::Type(ty) = arg.unpack() && let ty::Alias(ty::Projection, proj) = ty.kind() - && tcx.def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder + && tcx.is_impl_trait_in_trait(proj.def_id) { Some(MethodViolationCode::ReferencesImplTraitInTrait(tcx.def_span(proj.def_id))) } else { diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 6cb64ad57..cff3d277a 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,9 +1,10 @@ use crate::infer::InferCtxt; use crate::traits::query::type_op::{self, TypeOp, TypeOpOutput}; use crate::traits::query::NoSolution; -use crate::traits::ObligationCause; +use crate::traits::{ObligationCause, ObligationCtxt}; use rustc_data_structures::fx::FxIndexSet; -use rustc_middle::ty::{self, ParamEnv, Ty}; +use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt}; use rustc_span::def_id::LocalDefId; pub use rustc_middle::traits::query::OutlivesBound; @@ -52,6 +53,10 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { body_id: LocalDefId, ty: Ty<'tcx>, ) -> Vec<OutlivesBound<'tcx>> { + let ty = self.resolve_vars_if_possible(ty); + let ty = OpportunisticRegionResolver::new(self).fold_ty(ty); + assert!(!ty.needs_infer()); + let span = self.tcx.def_span(body_id); let result = param_env .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) @@ -71,22 +76,23 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { if let Some(constraints) = constraints { debug!(?constraints); + if !constraints.member_constraints.is_empty() { + span_bug!(span, "{:#?}", constraints.member_constraints); + } + // Instantiation may have produced new inference variables and constraints on those // variables. Process these constraints. + let ocx = ObligationCtxt::new(self); let cause = ObligationCause::misc(span, body_id); - let errors = super::fully_solve_obligations( - self, - constraints.outlives.iter().map(|constraint| { - self.query_outlives_constraint_to_obligation( - *constraint, - cause.clone(), - param_env, - ) - }), - ); - if !constraints.member_constraints.is_empty() { - span_bug!(span, "{:#?}", constraints.member_constraints); + for &constraint in &constraints.outlives { + ocx.register_obligation(self.query_outlives_constraint_to_obligation( + constraint, + cause.clone(), + param_env, + )); } + + let errors = ocx.select_all_or_error(); if !errors.is_empty() { self.tcx.sess.delay_span_bug( span, @@ -104,11 +110,6 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { body_id: LocalDefId, tys: FxIndexSet<Ty<'tcx>>, ) -> Bounds<'a, 'tcx> { - tys.into_iter() - .map(move |ty| { - let ty = self.resolve_vars_if_possible(ty); - self.implied_outlives_bounds(param_env, body_id, ty) - }) - .flatten() + tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty)) } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 870ecc2a9..826fc63ca 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -28,11 +28,11 @@ use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::at::At; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::ImplSourceBuiltinData; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::DefIdTree; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -286,12 +286,12 @@ fn project_and_unify_type<'cx, 'tcx>( ); obligations.extend(new); - match infcx - .at(&obligation.cause, obligation.param_env) - // This is needed to support nested opaque types like `impl Fn() -> impl Trait` - .define_opaque_types(true) - .eq(normalized, actual) - { + // Need to define opaque types to support nested opaque types like `impl Fn() -> impl Trait` + match infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::Yes, + normalized, + actual, + ) { Ok(InferOk { obligations: inferred_obligations, value: () }) => { obligations.extend(inferred_obligations); ProjectAndUnifyResult::Holds(obligations) @@ -468,6 +468,11 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx return ty; } + let (kind, data) = match *ty.kind() { + ty::Alias(kind, alias_ty) => (kind, alias_ty), + _ => return ty.super_fold_with(self), + }; + // We try to be a little clever here as a performance optimization in // cases where there are nested projections under binders. // For example: @@ -491,13 +496,11 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx // replace bound vars if the current type is a `Projection` and we need // to make sure we don't forget to fold the substs regardless. - match *ty.kind() { + match kind { // This is really important. While we *can* handle this, this has // severe performance implications for large opaque types with // late-bound regions. See `issue-88862` benchmark. - ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) - if !substs.has_escaping_bound_vars() => - { + ty::Opaque if !data.substs.has_escaping_bound_vars() => { // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.param_env.reveal() { Reveal::UserFacing => ty.super_fold_with(self), @@ -513,8 +516,8 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx ); } - let substs = substs.fold_with(self); - let generic_ty = self.interner().type_of(def_id); + let substs = data.substs.fold_with(self); + let generic_ty = self.interner().type_of(data.def_id); let concrete_ty = generic_ty.subst(self.interner(), substs); self.depth += 1; let folded_ty = self.fold_ty(concrete_ty); @@ -523,8 +526,9 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx } } } + ty::Opaque => ty.super_fold_with(self), - ty::Alias(ty::Projection, data) if !data.has_escaping_bound_vars() => { + ty::Projection if !data.has_escaping_bound_vars() => { // This branch is *mostly* just an optimization: when we don't // have escaping bound vars, we don't need to replace them with // placeholders (see branch below). *Also*, we know that we can @@ -563,7 +567,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx normalized_ty.ty().unwrap() } - ty::Alias(ty::Projection, data) => { + ty::Projection => { // If there are escaping bound vars, we temporarily replace the // bound vars with placeholders. Note though, that in the case // that we still can't project for whatever reason (e.g. self @@ -612,8 +616,6 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx ); normalized_ty } - - _ => ty.super_fold_with(self), } } @@ -770,7 +772,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { } ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); - let p = ty::PlaceholderRegion { universe, name: br.kind }; + let p = ty::PlaceholderRegion { universe, bound: br }; self.mapped_regions.insert(p, br); self.infcx.tcx.mk_re_placeholder(p) } @@ -788,7 +790,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { } ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); - let p = ty::PlaceholderType { universe, name: bound_ty.kind }; + let p = ty::PlaceholderType { universe, bound: bound_ty }; self.mapped_types.insert(p, bound_ty); self.infcx.tcx.mk_placeholder(p) } @@ -807,7 +809,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { } ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); - let p = ty::PlaceholderConst { universe, name: bound_const }; + let p = ty::PlaceholderConst { universe, bound: bound_const }; self.mapped_consts.insert(p, bound_const); self.infcx.tcx.mk_const(p, ct.ty()) } @@ -871,12 +873,12 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { let r1 = match *r0 { - ty::ReVar(_) => self + ty::ReVar(vid) => self .infcx .inner .borrow_mut() .unwrap_region_constraints() - .opportunistic_resolve_region(self.infcx.tcx, r0), + .opportunistic_resolve_var(self.infcx.tcx, vid), _ => r0, }; @@ -1296,7 +1298,7 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( ) { let tcx = selcx.tcx(); if tcx.def_kind(obligation.predicate.def_id) == DefKind::ImplTraitPlaceholder { - let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.def_id); + let trait_fn_def_id = tcx.impl_trait_in_trait_parent_fn(obligation.predicate.def_id); let trait_def_id = tcx.parent(trait_fn_def_id); let trait_substs = @@ -2065,7 +2067,11 @@ fn confirm_param_env_candidate<'cx, 'tcx>( debug!(?cache_projection, ?obligation_projection); - match infcx.at(cause, param_env).eq(cache_projection, obligation_projection) { + match infcx.at(cause, param_env).eq( + DefineOpaqueTypes::No, + cache_projection, + obligation_projection, + ) { Ok(InferOk { value: _, obligations }) => { nested_obligations.extend(obligations); assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations); @@ -2194,13 +2200,14 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( let tcx = selcx.tcx(); let mut obligations = data.nested; - let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.def_id); + let trait_fn_def_id = tcx.impl_trait_in_trait_parent_fn(obligation.predicate.def_id); let leaf_def = match specialization_graph::assoc_def(tcx, data.impl_def_id, trait_fn_def_id) { Ok(assoc_ty) => assoc_ty, Err(guar) => return Progress::error(tcx, guar), }; // We don't support specialization for RPITITs anyways... yet. - if !leaf_def.is_final() { + // Also don't try to project to an RPITIT that has no value + if !leaf_def.is_final() || !leaf_def.item.defaultness(tcx).has_value() { return Progress { term: tcx.ty_error_misc().into(), obligations }; } diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index f183248f2..edbe2de81 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,9 +1,8 @@ +use rustc_infer::traits::{TraitEngine, TraitEngineExt}; use rustc_middle::ty; -use rustc_session::config::TraitSolver; use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; -use crate::solve::{Certainty, Goal, InferCtxtEvalExt, MaybeCause}; use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext}; pub trait InferCtxtExt<'tcx> { @@ -79,37 +78,30 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { _ => obligation.param_env.without_const(), }; - if self.tcx.sess.opts.unstable_opts.trait_solver != TraitSolver::Next { + if self.tcx.trait_solver_next() { + self.probe(|snapshot| { + let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(); + fulfill_cx.register_predicate_obligation(self, obligation.clone()); + // True errors + // FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK? + if !fulfill_cx.select_where_possible(self).is_empty() { + Ok(EvaluationResult::EvaluatedToErr) + } else if !fulfill_cx.select_all_or_error(self).is_empty() { + Ok(EvaluationResult::EvaluatedToAmbig) + } else if self.opaque_types_added_in_snapshot(snapshot) { + Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes) + } else if self.region_constraints_added_in_snapshot(snapshot).is_some() { + Ok(EvaluationResult::EvaluatedToOkModuloRegions) + } else { + Ok(EvaluationResult::EvaluatedToOk) + } + }) + } else { let c_pred = self.canonicalize_query_keep_static( param_env.and(obligation.predicate), &mut _orig_values, ); self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) - } else { - self.probe(|snapshot| { - if let Ok((_, certainty)) = - self.evaluate_root_goal(Goal::new(self.tcx, param_env, obligation.predicate)) - { - match certainty { - Certainty::Yes => { - if self.opaque_types_added_in_snapshot(snapshot) { - Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes) - } else if self.region_constraints_added_in_snapshot(snapshot).is_some() - { - Ok(EvaluationResult::EvaluatedToOkModuloRegions) - } else { - Ok(EvaluationResult::EvaluatedToOk) - } - } - Certainty::Maybe(MaybeCause::Ambiguity) => { - Ok(EvaluationResult::EvaluatedToAmbig) - } - Certainty::Maybe(MaybeCause::Overflow) => Err(OverflowError::Canonical), - } - } else { - Ok(EvaluationResult::EvaluatedToErr) - } - }) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index b0cec3ce7..a986a9b6a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -197,23 +197,30 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> return Ok(*ty); } + let (kind, data) = match *ty.kind() { + ty::Alias(kind, data) => (kind, data), + _ => { + let res = ty.try_super_fold_with(self)?; + self.cache.insert(ty, res); + return Ok(res); + } + }; + // See note in `rustc_trait_selection::traits::project` about why we // wait to fold the substs. // Wrap this in a closure so we don't accidentally return from the outer function - let res = match *ty.kind() { + let res = match kind { // This is really important. While we *can* handle this, this has // severe performance implications for large opaque types with // late-bound regions. See `issue-88862` benchmark. - ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) - if !substs.has_escaping_bound_vars() => - { + ty::Opaque if !data.substs.has_escaping_bound_vars() => { // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.param_env.reveal() { Reveal::UserFacing => ty.try_super_fold_with(self)?, Reveal::All => { - let substs = substs.try_fold_with(self)?; + let substs = data.substs.try_fold_with(self)?; let recursion_limit = self.interner().recursion_limit(); if !recursion_limit.value_within_limit(self.anon_depth) { // A closure or generator may have itself as in its upvars. @@ -228,7 +235,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> return ty.try_super_fold_with(self); } - let generic_ty = self.interner().type_of(def_id); + let generic_ty = self.interner().type_of(data.def_id); let concrete_ty = generic_ty.subst(self.interner(), substs); self.anon_depth += 1; if concrete_ty == ty { @@ -248,62 +255,22 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> } } - ty::Alias(ty::Projection, data) if !data.has_escaping_bound_vars() => { - // This branch is just an optimization: when we don't have escaping bound vars, - // we don't need to replace them with placeholders (see branch below). - - let tcx = self.infcx.tcx; - let data = data.try_fold_with(self)?; - - let mut orig_values = OriginalQueryValues::default(); - // HACK(matthewjasper) `'static` is special-cased in selection, - // so we cannot canonicalize it. - let c_data = self - .infcx - .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values); - debug!("QueryNormalizer: c_data = {:#?}", c_data); - debug!("QueryNormalizer: orig_values = {:#?}", orig_values); - let result = tcx.normalize_projection_ty(c_data)?; - // We don't expect ambiguity. - if result.is_ambiguous() { - // Rustdoc normalizes possibly not well-formed types, so only - // treat this as a bug if we're not in rustdoc. - if !tcx.sess.opts.actually_rustdoc { - tcx.sess.delay_span_bug( - DUMMY_SP, - format!("unexpected ambiguity: {:?} {:?}", c_data, result), - ); - } - return Err(NoSolution); - } - let InferOk { value: result, obligations } = - self.infcx.instantiate_query_response_and_region_obligations( - self.cause, - self.param_env, - &orig_values, - result, - )?; - debug!("QueryNormalizer: result = {:#?}", result); - debug!("QueryNormalizer: obligations = {:#?}", obligations); - self.obligations.extend(obligations); - - let res = result.normalized_ty; - // `tcx.normalize_projection_ty` may normalize to a type that still has - // unevaluated consts, so keep normalizing here if that's the case. - if res != ty && res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) { - res.try_super_fold_with(self)? - } else { - res - } - } + ty::Opaque => ty.try_super_fold_with(self)?, - ty::Alias(ty::Projection, data) => { + ty::Projection => { // See note in `rustc_trait_selection::traits::project` let tcx = self.infcx.tcx; let infcx = self.infcx; - let (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + // Just an optimization: When we don't have escaping bound vars, + // we don't need to replace them with placeholders. + let (data, maps) = if data.has_escaping_bound_vars() { + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + (data, Some((mapped_regions, mapped_types, mapped_consts))) + } else { + (data, None) + }; let data = data.try_fold_with(self)?; let mut orig_values = OriginalQueryValues::default(); @@ -337,14 +304,18 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> debug!("QueryNormalizer: result = {:#?}", result); debug!("QueryNormalizer: obligations = {:#?}", obligations); self.obligations.extend(obligations); - let res = PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - result.normalized_ty, - ); + let res = if let Some((mapped_regions, mapped_types, mapped_consts)) = maps { + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result.normalized_ty, + ) + } else { + result.normalized_ty + }; // `tcx.normalize_projection_ty` may normalize to a type that still has // unevaluated consts, so keep normalizing here if that's the case. if res != ty && res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) { @@ -353,8 +324,6 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> res } } - - _ => ty.try_super_fold_with(self)?, }; self.cache.insert(ty, res); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 6bf3ed0d0..8f1b05c11 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -1,8 +1,8 @@ use crate::infer::canonical::query_response; use crate::infer::{InferCtxt, InferOk}; -use crate::traits; use crate::traits::query::type_op::TypeOpOutput; use crate::traits::query::Fallible; +use crate::traits::ObligationCtxt; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_span::source_map::DUMMY_SP; @@ -73,7 +73,9 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( ); let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?; - let errors = traits::fully_solve_obligations(infcx, obligations); + let ocx = ObligationCtxt::new(infcx); + ocx.register_obligations(obligations); + let errors = ocx.select_all_or_error(); if !errors.is_empty() { infcx.tcx.sess.diagnostic().delay_span_bug( DUMMY_SP, @@ -82,9 +84,7 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( } let region_obligations = infcx.take_registered_region_obligations(); - let region_constraint_data = infcx.take_and_reset_region_constraints(); - let region_constraints = query_response::make_query_region_constraints( infcx.tcx, region_obligations diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index e91057356..1f5bbc178 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -5,12 +5,14 @@ //! candidates. See the [rustc dev guide] for more details. //! //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly + +use hir::def_id::DefId; use hir::LangItem; use rustc_hir as hir; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; -use rustc_target::spec::abi::Abi; use crate::traits; use crate::traits::query::evaluate_obligation::InferCtxtExt; @@ -95,7 +97,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else if lang_items.tuple_trait() == Some(def_id) { self.assemble_candidate_for_tuple(obligation, &mut candidates); } else if lang_items.pointer_like() == Some(def_id) { - self.assemble_candidate_for_ptr_sized(obligation, &mut candidates); + self.assemble_candidate_for_pointer_like(obligation, &mut candidates); + } else if lang_items.fn_ptr_trait() == Some(def_id) { + self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -290,6 +294,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } + // Keep this function in sync with extract_tupled_inputs_and_output_from_callable + // until the old solver (and thus this function) is removed. + // Okay to skip binder because what we are inspecting doesn't involve bound regions. let self_ty = obligation.self_ty().skip_binder(); match *self_ty.kind() { @@ -298,31 +305,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.ambiguous = true; // Could wind up being a fn() type. } // Provide an impl, but only for suitable `fn` pointers. - ty::FnPtr(_) => { - if let ty::FnSig { - unsafety: hir::Unsafety::Normal, - abi: Abi::Rust, - c_variadic: false, - .. - } = self_ty.fn_sig(self.tcx()).skip_binder() - { + ty::FnPtr(sig) => { + if sig.is_fn_trait_compatible() { candidates.vec.push(FnPointerCandidate { is_const: false }); } } // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). ty::FnDef(def_id, _) => { - if let ty::FnSig { - unsafety: hir::Unsafety::Normal, - abi: Abi::Rust, - c_variadic: false, - .. - } = self_ty.fn_sig(self.tcx()).skip_binder() + if self.tcx().fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() { - if self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() { - candidates - .vec - .push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) }); - } + candidates + .vec + .push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) }); } } _ => {} @@ -330,13 +325,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } /// Searches for impls that might apply to `obligation`. + #[instrument(level = "debug", skip(self, candidates))] fn assemble_candidates_from_impls( &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - debug!(?obligation, "assemble_candidates_from_impls"); - // Essentially any user-written impl will match with an error type, // so creating `ImplCandidates` isn't useful. However, we might // end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized`) @@ -350,6 +344,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; + let obligation_substs = obligation.predicate.skip_binder().trait_ref.substs; self.tcx().for_each_relevant_impl( obligation.predicate.def_id(), obligation.predicate.skip_binder().trait_ref.self_ty(), @@ -358,7 +354,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // consider a "quick reject". This avoids creating more types // and so forth that we need to. let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); - if self.fast_reject_trait_refs(obligation, &impl_trait_ref.0) { + if !drcx.substs_refs_may_unify(obligation_substs, impl_trait_ref.0.substs) { + return; + } + if self.reject_fn_ptr_impls( + impl_def_id, + obligation, + impl_trait_ref.skip_binder().self_ty(), + ) { return; } @@ -371,6 +374,99 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } + /// The various `impl<T: FnPtr> Trait for T` in libcore are more like builtin impls for all function items + /// and function pointers and less like blanket impls. Rejecting them when they can't possibly apply (because + /// the obligation's self-type does not implement `FnPtr`) avoids reporting that the self type does not implement + /// `FnPtr`, when we wanted to report that it doesn't implement `Trait`. + #[instrument(level = "trace", skip(self), ret)] + fn reject_fn_ptr_impls( + &self, + impl_def_id: DefId, + obligation: &TraitObligation<'tcx>, + impl_self_ty: Ty<'tcx>, + ) -> bool { + // Let `impl<T: FnPtr> Trait for Vec<T>` go through the normal rejection path. + if !matches!(impl_self_ty.kind(), ty::Param(..)) { + return false; + } + let Some(fn_ptr_trait) = self.tcx().lang_items().fn_ptr_trait() else { + return false; + }; + + for &(predicate, _) in self.tcx().predicates_of(impl_def_id).predicates { + let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) + = predicate.kind().skip_binder() else { continue }; + if fn_ptr_trait != pred.trait_ref.def_id { + continue; + } + trace!(?pred); + // Not the bound we're looking for + if pred.self_ty() != impl_self_ty { + continue; + } + + match obligation.self_ty().skip_binder().kind() { + // Fast path to avoid evaluating an obligation that trivially holds. + // There may be more bounds, but these are checked by the regular path. + ty::FnPtr(..) => return false, + // These may potentially implement `FnPtr` + ty::Placeholder(..) + | ty::Dynamic(_, _, _) + | ty::Alias(_, _) + | ty::Infer(_) + | ty::Param(..) => {} + + ty::Bound(_, _) => span_bug!( + obligation.cause.span(), + "cannot have escaping bound var in self type of {obligation:#?}" + ), + // These can't possibly implement `FnPtr` as they are concrete types + // and not `FnPtr` + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::Closure(_, _) + | ty::Generator(_, _, _) + | ty::GeneratorWitness(_) + | ty::GeneratorWitnessMIR(_, _) + | ty::Never + | ty::Tuple(_) + | ty::Error(_) => return true, + // FIXME: Function definitions could actually implement `FnPtr` by + // casting the ZST function def to a function pointer. + ty::FnDef(_, _) => return true, + } + + // Generic params can implement `FnPtr` if the predicate + // holds within its own environment. + let obligation = Obligation::new( + self.tcx(), + obligation.cause.clone(), + obligation.param_env, + self.tcx().mk_predicate(obligation.predicate.map_bound(|mut pred| { + pred.trait_ref = + self.tcx().mk_trait_ref(fn_ptr_trait, [pred.trait_ref.self_ty()]); + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) + })), + ); + if let Ok(r) = self.infcx.evaluate_obligation(&obligation) { + if !r.may_apply() { + return true; + } + } + } + false + } + fn assemble_candidates_from_auto_impls( &mut self, obligation: &TraitObligation<'tcx>, @@ -783,6 +879,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let relevant_impl = self.tcx().find_map_relevant_impl( self.tcx().require_lang_item(LangItem::Drop, None), obligation.predicate.skip_binder().trait_ref.self_ty(), + TreatProjections::ForLookup, Some, ); @@ -845,15 +942,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - fn assemble_candidate_for_ptr_sized( + fn assemble_candidate_for_pointer_like( &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // The regions of a type don't affect the size of the type - let self_ty = self - .tcx() - .erase_regions(self.tcx().erase_late_bound_regions(obligation.predicate.self_ty())); + let tcx = self.tcx(); + let self_ty = + tcx.erase_regions(tcx.erase_late_bound_regions(obligation.predicate.self_ty())); // But if there are inference variables, we have to wait until it's resolved. if self_ty.has_non_region_infer() { @@ -861,13 +958,55 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } - let usize_layout = - self.tcx().layout_of(ty::ParamEnv::empty().and(self.tcx().types.usize)).unwrap().layout; - if let Ok(layout) = self.tcx().layout_of(obligation.param_env.and(self_ty)) - && layout.layout.size() == usize_layout.size() - && layout.layout.align().abi == usize_layout.align().abi + if let Ok(layout) = tcx.layout_of(obligation.param_env.and(self_ty)) + && layout.layout.is_pointer_like(&tcx.data_layout) { candidates.vec.push(BuiltinCandidate { has_nested: false }); } } + + fn assemble_candidates_for_fn_ptr_trait( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + match self_ty.skip_binder().kind() { + ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Foreign(..) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::Placeholder(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | ty::Never + | ty::Tuple(..) + | ty::Alias(..) + | ty::Param(..) + | ty::Bound(..) + | ty::Error(_) + | ty::Infer( + ty::InferTy::IntVar(_) + | ty::InferTy::FloatVar(_) + | ty::InferTy::FreshIntTy(_) + | ty::InferTy::FreshFloatTy(_), + ) => {} + ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)) => { + candidates.ambiguous = true; + } + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 21c158fd0..88121f865 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -8,8 +8,8 @@ //! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::InferOk; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; +use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::ty::{ self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt, @@ -18,7 +18,7 @@ use rustc_session::config::TraitSolver; use rustc_span::def_id::DefId; use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; -use crate::traits::util::{self, closure_trait_ref_and_return_type, predicate_for_trait_def}; +use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::vtable::{ count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset, VtblSegment, @@ -131,6 +131,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } }; + // The obligations returned by confirmation are recursively evaluated + // so we need to make sure they have the correct depth. + for subobligation in impl_src.borrow_nested_obligations_mut() { + subobligation.set_depth_from_parent(obligation.recursion_depth); + } + if !obligation.predicate.is_const_if_const() { // normalize nested predicates according to parent predicate's constness. impl_src = impl_src.map(|mut o| { @@ -177,7 +183,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations.extend(self.infcx.commit_if_ok(|_| { self.infcx .at(&obligation.cause, obligation.param_env) - .sup(placeholder_trait_predicate, candidate) + .sup(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate) .map(|InferOk { obligations, .. }| obligations) .map_err(|_| Unimplemented) })?); @@ -253,15 +259,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let cause = obligation.derived_cause(BuiltinDerivedObligation); - ensure_sufficient_stack(|| { - self.collect_predicates_for_types( - obligation.param_env, - cause, - obligation.recursion_depth + 1, - trait_def, - nested, - ) - }) + self.collect_predicates_for_types( + obligation.param_env, + cause, + obligation.recursion_depth + 1, + trait_def, + nested, + ) } else { vec![] }; @@ -462,7 +466,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested.extend(self.infcx.commit_if_ok(|_| { self.infcx .at(&obligation.cause, obligation.param_env) - .sup(obligation_trait_ref, upcast_trait_ref) + .sup(DefineOpaqueTypes::No, obligation_trait_ref, upcast_trait_ref) .map(|InferOk { obligations, .. }| obligations) .map_err(|_| Unimplemented) })?); @@ -601,10 +605,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligation, "confirm_fn_pointer_candidate"); let tcx = self.tcx(); - let self_ty = self + + let Some(self_ty) = self .infcx - .shallow_resolve(obligation.self_ty().no_bound_vars()) - .expect("fn pointer should not capture bound vars from predicate"); + .shallow_resolve(obligation.self_ty().no_bound_vars()) else + { + // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`, + // but we do not currently. Luckily, such a bound is not + // particularly useful, so we don't expect users to write + // them often. + return Err(SelectionError::Unimplemented); + }; + let sig = self_ty.fn_sig(tcx); let trait_ref = closure_trait_ref_and_return_type( tcx, @@ -819,11 +831,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) }); + // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs self.infcx .at(&obligation.cause, obligation.param_env) - // needed for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs - .define_opaque_types(true) - .sup(obligation_trait_ref, expected_trait_ref) + .sup(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) .map(|InferOk { mut obligations, .. }| { obligations.extend(nested); obligations @@ -888,7 +899,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) - .sup(target, source_trait) + .sup(DefineOpaqueTypes::No, target, source_trait) .map_err(|_| Unimplemented)?; nested.extend(obligations); @@ -987,7 +998,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) - .sup(target, source_trait) + .sup(DefineOpaqueTypes::No, target, source_trait) .map_err(|_| Unimplemented)?; nested.extend(obligations); @@ -1058,7 +1069,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) - .eq(b, a) + .eq(DefineOpaqueTypes::No, b, a) .map_err(|_| Unimplemented)?; nested.extend(obligations); } @@ -1073,6 +1084,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tail_field = def .non_enum_variant() .fields + .raw .last() .expect("expected unsized ADT to have a tail field"); let tail_field_ty = tcx.type_of(tail_field.did); @@ -1106,19 +1118,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) - .eq(target, new_struct) + .eq(DefineOpaqueTypes::No, target, new_struct) .map_err(|_| Unimplemented)?; nested.extend(obligations); // Construct the nested `TailField<T>: Unsize<TailField<U>>` predicate. - nested.push(predicate_for_trait_def( + let tail_unsize_obligation = obligation.with( tcx, - obligation.param_env, - obligation.cause.clone(), - obligation.predicate.def_id(), - obligation.recursion_depth + 1, - [source_tail, target_tail], - )); + tcx.mk_trait_ref(obligation.predicate.def_id(), [source_tail, target_tail]), + ); + nested.push(tail_unsize_obligation); } // `(.., T)` -> `(.., U)` @@ -1136,21 +1145,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) - .eq(target, new_tuple) + .eq(DefineOpaqueTypes::No, target, new_tuple) .map_err(|_| Unimplemented)?; nested.extend(obligations); - // Construct the nested `T: Unsize<U>` predicate. - nested.push(ensure_sufficient_stack(|| { - predicate_for_trait_def( - tcx, - obligation.param_env, - obligation.cause.clone(), - obligation.predicate.def_id(), - obligation.recursion_depth + 1, - [a_last, b_last], - ) - })); + // Add a nested `T: Unsize<U>` predicate. + let last_unsize_obligation = obligation + .with(tcx, tcx.mk_trait_ref(obligation.predicate.def_id(), [a_last, b_last])); + nested.push(last_unsize_obligation); } _ => bug!("source: {source}, target: {target}"), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 7f454fbb3..6bb53418b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2,12 +2,6 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection -// FIXME: The `map` field in ProvisionalEvaluationCache should be changed to -// a `FxIndexMap` to avoid query instability, but right now it causes a perf regression. This would be -// fixed or at least lightened by the addition of the `drain_filter` method to `FxIndexMap` -// Relevant: https://github.com/rust-lang/rust/pull/103723 and https://github.com/bluss/indexmap/issues/242 -#![allow(rustc::potential_query_instability)] - use self::EvaluationResult::*; use self::SelectionCandidate::*; @@ -17,7 +11,7 @@ use super::project; use super::project::normalize_with_depth_to; use super::project::ProjectionTyObligation; use super::util; -use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; +use super::util::closure_trait_ref_and_return_type; use super::wf; use super::{ ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, Normalized, Obligation, @@ -32,25 +26,23 @@ use crate::traits::project::ProjectAndUnifyResult; use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; use crate::traits::Unimplemented; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::traits::TraitEngine; use rustc_infer::traits::TraitEngineExt; use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::NotConstEvaluatable; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; -use rustc_session::config::TraitSolver; use rustc_span::symbol::sym; use std::cell::{Cell, RefCell}; @@ -178,14 +170,14 @@ struct TraitObligationStack<'prev, 'tcx> { } struct SelectionCandidateSet<'tcx> { - // A list of candidates that definitely apply to the current - // obligation (meaning: types unify). + /// A list of candidates that definitely apply to the current + /// obligation (meaning: types unify). vec: Vec<SelectionCandidate<'tcx>>, - // If `true`, then there were candidates that might or might - // not have applied, but we couldn't tell. This occurs when some - // of the input types are type variables, in which case there are - // various "builtin" rules that might or might not trigger. + /// If `true`, then there were candidates that might or might + /// not have applied, but we couldn't tell. This occurs when some + /// of the input types are type variables, in which case there are + /// various "builtin" rules that might or might not trigger. ambiguous: bool, } @@ -211,7 +203,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx, - freshener: infcx.freshener_keep_static(), + freshener: infcx.freshener(), intercrate_ambiguity_causes: None, query_mode: TraitQueryMode::Standard, } @@ -465,14 +457,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if candidates.len() > 1 { let mut i = 0; while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { self.candidate_should_be_dropped_in_favor_of( &candidates[i], &candidates[j], needs_infer, - ) + ) == DropVictim::Yes }); - if is_dup { + if should_drop_i { debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); candidates.swap_remove(i); } else { @@ -545,13 +537,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PredicateObligation<'tcx>, ) -> Result<EvaluationResult, OverflowError> { self.evaluation_probe(|this| { - if this.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next { + if this.tcx().trait_solver_next() { + this.evaluate_predicates_recursively_in_new_solver([obligation.clone()]) + } else { this.evaluate_predicate_recursively( TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), obligation.clone(), ) - } else { - this.evaluate_predicates_recursively_in_new_solver([obligation.clone()]) } }) } @@ -591,9 +583,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug, { - if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next { + if self.tcx().trait_solver_next() { + self.evaluate_predicates_recursively_in_new_solver(predicates) + } else { let mut result = EvaluatedToOk; - for obligation in predicates { + for mut obligation in predicates { + obligation.set_depth_from_parent(stack.depth()); let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; if let EvaluatedToErr = eval { // fast-path - EvaluatedToErr is the top of the lattice, @@ -604,8 +599,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } Ok(result) - } else { - self.evaluate_predicates_recursively_in_new_solver(predicates) } } @@ -617,6 +610,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(); fulfill_cx.register_predicate_obligations(self.infcx, predicates); // True errors + // FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK? if !fulfill_cx.select_where_possible(self.infcx).is_empty() { return Ok(EvaluatedToErr); } @@ -661,12 +655,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let p = bound_predicate.rebind(p); // Does this code ever run? match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { - Ok(Ok(InferOk { mut obligations, .. })) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively( - previous_stack, - obligations.into_iter(), - ) + Ok(Ok(InferOk { obligations, .. })) => { + self.evaluate_predicates_recursively(previous_stack, obligations) } Ok(Err(_)) => Ok(EvaluatedToErr), Err(..) => Ok(EvaluatedToAmbig), @@ -677,12 +667,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let p = bound_predicate.rebind(p); // Does this code ever run? match self.infcx.coerce_predicate(&obligation.cause, obligation.param_env, p) { - Ok(Ok(InferOk { mut obligations, .. })) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively( - previous_stack, - obligations.into_iter(), - ) + Ok(Ok(InferOk { obligations, .. })) => { + self.evaluate_predicates_recursively(previous_stack, obligations) } Ok(Err(_)) => Ok(EvaluatedToErr), Err(..) => Ok(EvaluatedToAmbig), @@ -755,9 +741,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { arg, obligation.cause.span, ) { - Some(mut obligations) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - + Some(obligations) => { cache.wf_args.borrow_mut().push((arg, previous_stack.depth())); let result = self.evaluate_predicates_recursively(previous_stack, obligations); @@ -778,14 +762,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateKind::Clause(ty::Clause::TypeOutlives(pred)) => { - // A global type with no late-bound regions can only - // contain the "'static" lifetime (any other lifetime - // would either be late-bound or local), so it is guaranteed - // to outlive any other lifetime - if pred.0.is_global() && !pred.0.has_late_bound_vars() { - Ok(EvaluatedToOk) - } else { + // A global type with no free lifetimes or generic parameters + // outlives anything. + if pred.0.has_free_regions() + || pred.0.has_late_bound_regions() + || pred.0.has_non_region_infer() + || pred.0.has_non_region_infer() + { Ok(EvaluatedToOkModuloRegions) + } else { + Ok(EvaluatedToOk) } } @@ -826,10 +812,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - self.add_depth( - subobligations.iter_mut(), - obligation.recursion_depth, - ); + // Need to explicitly set the depth of nested goals here as + // projection obligations can cycle by themselves and in + // `evaluate_predicates_recursively` we only add the depth + // for parent trait goals because only these get added to the + // `TraitObligationStackList`. + for subobligation in subobligations.iter_mut() { + subobligation.set_depth_from_parent(obligation.recursion_depth); + } let res = self.evaluate_predicates_recursively( previous_stack, subobligations, @@ -909,38 +899,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if a.def.did == b.def.did && tcx.def_kind(a.def.did) == DefKind::AssocConst => { - if let Ok(new_obligations) = self + if let Ok(InferOk { obligations, value: () }) = self .infcx .at(&obligation.cause, obligation.param_env) .trace(c1, c2) - .eq(a.substs, b.substs) + .eq(DefineOpaqueTypes::No, a.substs, b.substs) { - let mut obligations = new_obligations.obligations; - self.add_depth( - obligations.iter_mut(), - obligation.recursion_depth, - ); return self.evaluate_predicates_recursively( previous_stack, - obligations.into_iter(), + obligations, ); } } (_, Unevaluated(_)) | (Unevaluated(_), _) => (), (_, _) => { - if let Ok(new_obligations) = self + if let Ok(InferOk { obligations, value: () }) = self .infcx .at(&obligation.cause, obligation.param_env) - .eq(c1, c2) + .eq(DefineOpaqueTypes::No, c1, c2) { - let mut obligations = new_obligations.obligations; - self.add_depth( - obligations.iter_mut(), - obligation.recursion_depth, - ); return self.evaluate_predicates_recursively( previous_stack, - obligations.into_iter(), + obligations, ); } } @@ -965,8 +945,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { - match self.infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2) - { + match self.infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::No, + c1, + c2, + ) { Ok(inf_ok) => self.evaluate_predicates_recursively( previous_stack, inf_ok.into_obligations(), @@ -989,12 +972,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for chalk") } - ty::PredicateKind::AliasEq(..) => { - bug!("AliasEq is only used for new solver") + ty::PredicateKind::AliasRelate(..) => { + bug!("AliasRelate is only used for new solver") } ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { - match self.infcx.at(&obligation.cause, obligation.param_env).eq(ct.ty(), ty) { + match self.infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::No, + ct.ty(), + ty, + ) { Ok(inf_ok) => self.evaluate_predicates_recursively( previous_stack, inf_ok.into_obligations(), @@ -1359,24 +1346,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.evaluation_cache.insert((param_env, trait_pred), dep_node, result); } - /// For various reasons, it's possible for a subobligation - /// to have a *lower* recursion_depth than the obligation used to create it. - /// Projection sub-obligations may be returned from the projection cache, - /// which results in obligations with an 'old' `recursion_depth`. - /// Additionally, methods like `InferCtxt.subtype_predicate` produce - /// subobligations without taking in a 'parent' depth, causing the - /// generated subobligations to have a `recursion_depth` of `0`. - /// - /// To ensure that obligation_depth never decreases, we force all subobligations - /// to have at least the depth of the original obligation. - fn add_depth<T: 'cx, I: Iterator<Item = &'cx mut Obligation<'tcx, T>>>( - &self, - it: I, - min_depth: usize, - ) { - it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); - } - fn check_recursion_depth<T>( &self, depth: usize, @@ -1752,7 +1721,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }); self.infcx .at(&obligation.cause, obligation.param_env) - .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) + .sup(DefineOpaqueTypes::No, ty::Binder::dummy(placeholder_trait_ref), trait_bound) .map(|InferOk { obligations: _, value: () }| { // This method is called within a probe, so we can't have // inference variables and placeholders escape. @@ -1814,7 +1783,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let is_match = self .infcx .at(&obligation.cause, obligation.param_env) - .sup(obligation.predicate, infer_projection) + .sup(DefineOpaqueTypes::No, obligation.predicate, infer_projection) .map_or(false, |InferOk { obligations, value: () }| { self.evaluate_predicates_recursively( TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), @@ -1842,16 +1811,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ProjectionMatchesProjection::No } } +} - /////////////////////////////////////////////////////////////////////////// - // WINNOW - // - // Winnowing is the process of attempting to resolve ambiguity by - // probing further. During the winnowing process, we unify all - // type variables and then we also attempt to evaluate recursive - // bounds to see if they are satisfied. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum DropVictim { + Yes, + No, +} + +impl DropVictim { + fn drop_if(should_drop: bool) -> DropVictim { + if should_drop { DropVictim::Yes } else { DropVictim::No } + } +} - /// Returns `true` if `victim` should be dropped in favor of +/// ## Winnowing +/// +/// Winnowing is the process of attempting to resolve ambiguity by +/// probing further. During the winnowing process, we unify all +/// type variables and then we also attempt to evaluate recursive +/// bounds to see if they are satisfied. +impl<'tcx> SelectionContext<'_, 'tcx> { + /// Returns `DropVictim::Yes` if `victim` should be dropped in favor of /// `other`. Generally speaking we will drop duplicate /// candidates and prefer where-clause candidates. /// @@ -1861,9 +1842,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { victim: &EvaluatedCandidate<'tcx>, other: &EvaluatedCandidate<'tcx>, needs_infer: bool, - ) -> bool { + ) -> DropVictim { if victim.candidate == other.candidate { - return true; + return DropVictim::Yes; } // Check if a bound would previously have been removed when normalizing @@ -1887,11 +1868,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // FIXME(@jswrenn): this should probably be more sophisticated - (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false, + (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, // (*) - (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => true, - (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => false, + (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => { + DropVictim::Yes + } + (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => { + DropVictim::No + } (ParamCandidate(other), ParamCandidate(victim)) => { let same_except_bound_vars = other.skip_binder().trait_ref @@ -1905,28 +1890,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // or the current one if tied (they should both evaluate to the same answer). This is // probably best characterized as a "hack", since we might prefer to just do our // best to *not* create essentially duplicate candidates in the first place. - other.bound_vars().len() <= victim.bound_vars().len() + DropVictim::drop_if(other.bound_vars().len() <= victim.bound_vars().len()) } else if other.skip_binder().trait_ref == victim.skip_binder().trait_ref && victim.skip_binder().constness == ty::BoundConstness::NotConst && other.skip_binder().polarity == victim.skip_binder().polarity { // Drop otherwise equivalent non-const candidates in favor of const candidates. - true + DropVictim::Yes } else { - false + DropVictim::No } } // Drop otherwise equivalent non-const fn pointer candidates - (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => true, + (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => DropVictim::Yes, - // Global bounds from the where clause should be ignored - // here (see issue #50825). Otherwise, we have a where - // clause so don't go around looking for impls. - // Arbitrarily give param candidates priority - // over projection and object candidates. ( - ParamCandidate(ref cand), + ParamCandidate(ref other_cand), ImplCandidate(..) | ClosureCandidate { .. } | GeneratorCandidate @@ -1939,11 +1919,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitAliasCandidate | ObjectCandidate(_) | ProjectionCandidate(..), - ) => !is_global(cand), - (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref cand)) => { + ) => { + // We have a where clause so don't go around looking + // for impls. Arbitrarily give param candidates priority + // over projection and object candidates. + // + // Global bounds from the where clause should be ignored + // here (see issue #50825). + DropVictim::drop_if(!is_global(other_cand)) + } + (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref victim_cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(cand) + if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No } } ( ImplCandidate(_) @@ -1956,18 +1944,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate, - ParamCandidate(ref cand), + ParamCandidate(ref victim_cand), ) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(cand) && other.evaluation.must_apply_modulo_regions() + DropVictim::drop_if( + is_global(victim_cand) && other.evaluation.must_apply_modulo_regions(), + ) } (ProjectionCandidate(i, _), ProjectionCandidate(j, _)) | (ObjectCandidate(i), ObjectCandidate(j)) => { // Arbitrarily pick the lower numbered candidate for backwards // compatibility reasons. Don't let this affect inference. - i < j && !needs_infer + DropVictim::drop_if(i < j && !needs_infer) } (ObjectCandidate(_), ProjectionCandidate(..)) | (ProjectionCandidate(..), ObjectCandidate(_)) => { @@ -1987,7 +1977,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate, - ) => true, + ) => DropVictim::Yes, ( ImplCandidate(..) @@ -2001,7 +1991,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinCandidate { .. } | TraitAliasCandidate, ObjectCandidate(_) | ProjectionCandidate(..), - ) => false, + ) => DropVictim::No, (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { // See if we can toss out `victim` based on specialization. @@ -2014,59 +2004,69 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); if other.evaluation.must_apply_modulo_regions() { if tcx.specializes((other_def, victim_def)) { - return true; + return DropVictim::Yes; } } - if other.evaluation.must_apply_considering_regions() { - match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { - Some(ty::ImplOverlapKind::Permitted { marker: true }) => { - // Subtle: If the predicate we are evaluating has inference - // variables, do *not* allow discarding candidates due to - // marker trait impls. - // - // Without this restriction, we could end up accidentally - // constraining inference variables based on an arbitrarily - // chosen trait impl. - // - // Imagine we have the following code: - // - // ```rust - // #[marker] trait MyTrait {} - // impl MyTrait for u8 {} - // impl MyTrait for bool {} - // ``` - // - // And we are evaluating the predicate `<_#0t as MyTrait>`. - // - // During selection, we will end up with one candidate for each - // impl of `MyTrait`. If we were to discard one impl in favor - // of the other, we would be left with one candidate, causing - // us to "successfully" select the predicate, unifying - // _#0t with (for example) `u8`. - // - // However, we have no reason to believe that this unification - // is correct - we've essentially just picked an arbitrary - // *possibility* for _#0t, and required that this be the *only* - // possibility. - // - // Eventually, we will either: - // 1) Unify all inference variables in the predicate through - // some other means (e.g. type-checking of a function). We will - // then be in a position to drop marker trait candidates - // without constraining inference variables (since there are - // none left to constrain) - // 2) Be left with some unconstrained inference variables. We - // will then correctly report an inference error, since the - // existence of multiple marker trait impls tells us nothing - // about which one should actually apply. - !needs_infer - } - Some(_) => true, - None => false, + match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { + // For #33140 the impl headers must be exactly equal, the trait must not have + // any associated items and there are no where-clauses. + // + // We can just arbitrarily drop one of the impls. + Some(ty::ImplOverlapKind::Issue33140) => { + assert_eq!(other.evaluation, victim.evaluation); + DropVictim::Yes } - } else { - false + // For candidates which already reference errors it doesn't really + // matter what we do 🤷 + Some(ty::ImplOverlapKind::Permitted { marker: false }) => { + DropVictim::drop_if(other.evaluation.must_apply_considering_regions()) + } + Some(ty::ImplOverlapKind::Permitted { marker: true }) => { + // Subtle: If the predicate we are evaluating has inference + // variables, do *not* allow discarding candidates due to + // marker trait impls. + // + // Without this restriction, we could end up accidentally + // constraining inference variables based on an arbitrarily + // chosen trait impl. + // + // Imagine we have the following code: + // + // ```rust + // #[marker] trait MyTrait {} + // impl MyTrait for u8 {} + // impl MyTrait for bool {} + // ``` + // + // And we are evaluating the predicate `<_#0t as MyTrait>`. + // + // During selection, we will end up with one candidate for each + // impl of `MyTrait`. If we were to discard one impl in favor + // of the other, we would be left with one candidate, causing + // us to "successfully" select the predicate, unifying + // _#0t with (for example) `u8`. + // + // However, we have no reason to believe that this unification + // is correct - we've essentially just picked an arbitrary + // *possibility* for _#0t, and required that this be the *only* + // possibility. + // + // Eventually, we will either: + // 1) Unify all inference variables in the predicate through + // some other means (e.g. type-checking of a function). We will + // then be in a position to drop marker trait candidates + // without constraining inference variables (since there are + // none left to constrain) + // 2) Be left with some unconstrained inference variables. We + // will then correctly report an inference error, since the + // existence of multiple marker trait impls tells us nothing + // about which one should actually apply. + DropVictim::drop_if( + !needs_infer && other.evaluation.must_apply_considering_regions(), + ) + } + None => DropVictim::No, } } @@ -2092,10 +2092,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate, - ) => false, + ) => DropVictim::No, } } +} +impl<'tcx> SelectionContext<'_, 'tcx> { fn sized_conditions( &mut self, obligation: &TraitObligation<'tcx>, @@ -2149,7 +2151,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None, ty::Infer(ty::TyVar(_)) => Ambiguous, - // We can make this an ICE if/once we actually instantiate the trait obligation. + // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. ty::Bound(..) => None, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { @@ -2257,7 +2259,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::Adt(..) | ty::Alias(..) | ty::Param(..) => { + ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => { // Fallback to whatever user-defined impls exist in this case. None } @@ -2269,9 +2271,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ambiguous } - ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. + ty::Bound(..) => None, + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); } } @@ -2405,15 +2408,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { placeholder_ty, ) }); - let placeholder_obligation = predicate_for_trait_def( + + let obligation = Obligation::new( self.tcx(), - param_env, cause.clone(), - trait_def_id, - recursion_depth, - [normalized_ty], + param_env, + self.tcx().mk_trait_ref(trait_def_id, [normalized_ty]), ); - obligations.push(placeholder_obligation); + obligations.push(obligation); obligations }) .collect() @@ -2507,7 +2509,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self .infcx .at(&cause, obligation.param_env) - .eq(placeholder_obligation_trait_ref, impl_trait_ref) + .eq(DefineOpaqueTypes::No, placeholder_obligation_trait_ref, impl_trait_ref) .map_err(|e| { debug!("match_impl: failed eq_trait_refs due to `{}`", e.to_string(self.tcx())) })?; @@ -2523,19 +2525,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Normalized { value: impl_substs, obligations: nested_obligations }) } - fn fast_reject_trait_refs( - &mut self, - obligation: &TraitObligation<'tcx>, - impl_trait_ref: &ty::TraitRef<'tcx>, - ) -> bool { - // We can avoid creating type variables and doing the full - // substitution if we find that any of the input types, when - // simplified, do not match. - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; - iter::zip(obligation.predicate.skip_binder().trait_ref.substs, impl_trait_ref.substs) - .any(|(obl, imp)| !drcx.generic_args_may_unify(obl, imp)) - } - /// Normalize `where_clause_trait_ref` and try to match it against /// `obligation`. If successful, return any predicates that /// result from the normalization. @@ -2557,7 +2546,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result<Vec<PredicateObligation<'tcx>>, ()> { self.infcx .at(&obligation.cause, obligation.param_env) - .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref) + .sup(DefineOpaqueTypes::No, obligation.predicate.to_poly_trait_ref(), poly_trait_ref) .map(|InferOk { obligations, .. }| obligations) .map_err(|_| ()) } @@ -2786,7 +2775,7 @@ struct ProvisionalEvaluationCache<'tcx> { /// - then we determine that `E` is in error -- we will then clear /// all cache values whose DFN is >= 4 -- in this case, that /// means the cached value for `F`. - map: RefCell<FxHashMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>, + map: RefCell<FxIndexMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>, /// The stack of args that we assume to be true because a `WF(arg)` predicate /// is on the stack above (and because of wellformedness is coinductive). @@ -2934,12 +2923,13 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { /// have a performance impact in practice. fn on_completion(&self, dfn: usize) { debug!(?dfn, "on_completion"); - - for (fresh_trait_pred, eval) in - self.map.borrow_mut().drain_filter(|_k, eval| eval.from_dfn >= dfn) - { - debug!(?fresh_trait_pred, ?eval, "on_completion"); - } + self.map.borrow_mut().retain(|fresh_trait_pred, eval| { + if eval.from_dfn >= dfn { + debug!(?fresh_trait_pred, ?eval, "on_completion"); + return false; + } + true + }); } } @@ -3021,7 +3011,7 @@ fn bind_generator_hidden_types_above<'tcx>( if let ty::ReErased = r.kind() { let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), - kind: ty::BrAnon(counter, None), + kind: ty::BrAnon(None), }; counter += 1; r = tcx.mk_re_late_bound(current_depth, br); @@ -3037,7 +3027,7 @@ fn bind_generator_hidden_types_above<'tcx>( debug_assert!(!hidden_types.has_erased_regions()); } let bound_vars = tcx.mk_bound_variable_kinds_from_iter(bound_vars.iter().chain( - (num_bound_variables..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i, None))), + (num_bound_variables..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon(None))), )); ty::Binder::bind_with_vars(hidden_types, bound_vars) } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index d1d6a7a90..8546bbe52 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -10,6 +10,7 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html pub mod specialization_graph; +use rustc_infer::infer::DefineOpaqueTypes; use specialization_graph::GraphExt; use crate::errors::NegativePositiveConflict; @@ -21,7 +22,7 @@ use crate::traits::{ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{error_code, DelayDm, Diagnostic}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt}; +use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; @@ -99,10 +100,10 @@ pub fn translate_substs<'tcx>( } fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( - |_| { + |()| { bug!( - "When translating substitutions for specialization, the expected \ - specialization failed to hold" + "When translating substitutions from {source_impl:?} to {target_impl:?}, \ + the expected specialization failed to hold" ) }, ) @@ -193,7 +194,7 @@ fn fulfill_implication<'tcx>( // do the impls unify? If not, no specialization. let Ok(InferOk { obligations: more_obligations, .. }) = - infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait, target_trait) + infcx.at(&ObligationCause::dummy(), param_env, ).eq(DefineOpaqueTypes::No,source_trait, target_trait) else { debug!( "fulfill_implication: {:?} does not unify with {:?}", @@ -349,6 +350,10 @@ fn report_conflicting_impls<'tcx>( impl_span: Span, err: &mut Diagnostic, ) { + if (overlap.trait_ref, overlap.self_ty).references_error() { + err.downgrade_to_delayed_bug(); + } + match tcx.span_of_impl(overlap.with_impl) { Ok(span) => { err.span_label(span, "first implementation here"); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 61ed9ef2e..aa5c624f4 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -21,6 +21,7 @@ pub struct FutureCompatOverlapError<'tcx> { } /// The result of attempting to insert an impl into a group of children. +#[derive(Debug)] enum Inserted<'tcx> { /// The impl was inserted as a new child in this group of children. BecameNewSibling(Option<FutureCompatOverlapError<'tcx>>), @@ -49,7 +50,8 @@ impl<'tcx> ChildrenExt<'tcx> for Children { /// Insert an impl into this set of children without comparing to any existing impls. fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder(); - if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) + if let Some(st) = + fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey) { debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); self.non_blanket_impls.entry(st).or_default().push(impl_def_id) @@ -65,7 +67,8 @@ impl<'tcx> ChildrenExt<'tcx> for Children { fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder(); let vec: &mut Vec<DefId>; - if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) + if let Some(st) = + fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey) { debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); vec = self.non_blanket_impls.get_mut(&st).unwrap(); @@ -80,6 +83,7 @@ impl<'tcx> ChildrenExt<'tcx> for Children { /// Attempt to insert an impl into this set of children, while comparing for /// specialization relationships. + #[instrument(level = "debug", skip(self, tcx), ret)] fn insert( &mut self, tcx: TyCtxt<'tcx>, @@ -90,18 +94,13 @@ impl<'tcx> ChildrenExt<'tcx> for Children { let mut last_lint = None; let mut replace_children = Vec::new(); - debug!("insert(impl_def_id={:?}, simplified_self={:?})", impl_def_id, simplified_self,); - let possible_siblings = match simplified_self { Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)), None => PotentialSiblings::Unfiltered(iter_children(self)), }; for possible_sibling in possible_siblings { - debug!( - "insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}", - impl_def_id, simplified_self, possible_sibling, - ); + debug!(?possible_sibling); let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| { let trait_ref = overlap.impl_header.trait_ref.unwrap(); @@ -302,7 +301,8 @@ impl<'tcx> GraphExt<'tcx> for Graph { let mut parent = trait_def_id; let mut last_lint = None; - let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer); + let simplified = + fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey); // Descend the specialization tree, where `parent` is the current parent node. loop { diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index bcf63d5a6..20357d4d2 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,15 +1,14 @@ -use rustc_errors::Diagnostic; -use rustc_span::Span; -use smallvec::SmallVec; - +use super::NormalizeExt; +use super::{ObligationCause, PredicateObligation, SelectionContext}; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Diagnostic; use rustc_hir::def_id::DefId; +use rustc_infer::infer::InferOk; +use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::ty::{GenericArg, SubstsRef}; +use rustc_span::Span; +use smallvec::SmallVec; -use super::NormalizeExt; -use super::{Obligation, ObligationCause, PredicateObligation, SelectionContext}; -use rustc_infer::infer::InferOk; pub use rustc_infer::traits::{self, util::*}; /////////////////////////////////////////////////////////////////////////// @@ -116,7 +115,7 @@ impl<'tcx> TraitAliasExpander<'tcx> { } // Get components of trait alias. - let predicates = tcx.super_predicates_of(trait_ref.def_id()); + let predicates = tcx.implied_predicates_of(trait_ref.def_id()); debug!(?predicates); let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { @@ -199,8 +198,9 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( impl_def_id: DefId, impl_substs: SubstsRef<'tcx>, ) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) { - let subject = selcx.tcx().bound_impl_subject(impl_def_id); + let subject = selcx.tcx().impl_subject(impl_def_id); let subject = subject.subst(selcx.tcx(), impl_substs); + let InferOk { value: subject, obligations: normalization_obligations1 } = selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(subject); @@ -218,33 +218,6 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( (subject, impl_obligations) } -pub fn predicate_for_trait_ref<'tcx>( - tcx: TyCtxt<'tcx>, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - recursion_depth: usize, -) -> PredicateObligation<'tcx> { - Obligation { - cause, - param_env, - recursion_depth, - predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx), - } -} - -pub fn predicate_for_trait_def<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - trait_def_id: DefId, - recursion_depth: usize, - params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>, -) -> PredicateObligation<'tcx> { - let trait_ref = tcx.mk_trait_ref(trait_def_id, params); - predicate_for_trait_ref(tcx, cause, param_env, trait_ref, recursion_depth) -} - /// Casts a trait reference into a reference to one of its super /// traits; returns `None` if `target_trait_def_id` is not a /// supertrait. diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index d498af359..3d026506a 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -191,8 +191,8 @@ pub fn predicate_obligations<'tcx>( ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } - ty::PredicateKind::AliasEq(..) => { - bug!("We should only wf check where clauses and `AliasEq` is not a `Clause`") + ty::PredicateKind::AliasRelate(..) => { + bug!("We should only wf check where clauses and `AliasRelate` is not a `Clause`") } } @@ -364,7 +364,7 @@ impl<'tcx> WfPredicates<'tcx> { }; if let Elaborate::All = elaborate { - let implied_obligations = traits::util::elaborate_obligations(tcx, obligations); + let implied_obligations = traits::util::elaborate(tcx, obligations); let implied_obligations = implied_obligations.map(extend); self.out.extend(implied_obligations); } else { @@ -920,10 +920,10 @@ pub(crate) fn required_region_bounds<'tcx>( ) -> Vec<ty::Region<'tcx>> { assert!(!erased_self_ty.has_escaping_bound_vars()); - traits::elaborate_predicates(tcx, predicates) - .filter_map(|obligation| { - debug!(?obligation); - match obligation.predicate.kind().skip_binder() { + traits::elaborate(tcx, predicates) + .filter_map(|pred| { + debug!(?pred); + match pred.kind().skip_binder() { ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) @@ -936,7 +936,7 @@ pub(crate) fn required_region_bounds<'tcx>( | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous - | ty::PredicateKind::AliasEq(..) + | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( ref t, |