summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection')
-rw-r--r--compiler/rustc_trait_selection/messages.ftl (renamed from compiler/rustc_trait_selection/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs35
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/assembly.rs)267
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs (renamed from compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs)120
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonicalize.rs (renamed from compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs)100
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs537
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs (renamed from compiler/rustc_trait_selection/src/solve/canonical/mod.rs)58
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs47
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs605
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs250
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/cache.rs27
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs149
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs26
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs338
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs43
-rw-r--r--compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs86
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs35
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs314
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs137
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs72
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs89
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs76
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs41
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs59
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs48
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs103
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs205
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs86
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs412
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs15
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs18
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs47
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs16
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(&param_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 &region_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(&current_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,