summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:19 +0000
commita0b8f38ab54ac451646aa00cd5e91b6c76f22a84 (patch)
treefc451898ccaf445814e26b46664d78702178101d /compiler/rustc_trait_selection
parentAdding debian version 1.71.1+dfsg1-2. (diff)
downloadrustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.tar.xz
rustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.zip
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_trait_selection')
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs11
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs195
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs153
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs20
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonicalize.rs31
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs392
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs30
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs67
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs307
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs147
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs435
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs166
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs220
-rw-r--r--compiler/rustc_trait_selection/src/solve/opaques.rs23
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs269
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs17
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs86
-rw-r--r--compiler/rustc_trait_selection/src/solve/weak_types.rs19
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs40
-rw-r--r--compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs151
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs306
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs71
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs518
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs22
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs259
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs80
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs120
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs176
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs16
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs314
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs32
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs46
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs62
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs253
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs214
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs70
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs60
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs111
52 files changed, 3727 insertions, 1877 deletions
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 312bd3817..6efc1e730 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -1,5 +1,5 @@
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::{self, ObligationCtxt};
+use crate::traits::{self, DefiningAnchor, ObligationCtxt};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
@@ -80,7 +80,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
pub trait InferCtxtBuilderExt<'tcx> {
fn enter_canonical_trait_query<K, R>(
- &mut self,
+ self,
canonical_key: &Canonical<'tcx, K>,
operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>,
) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution>
@@ -108,7 +108,7 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
/// have `'tcx` be free on this function so that we can talk about
/// `K: TypeFoldable<TyCtxt<'tcx>>`.)
fn enter_canonical_trait_query<K, R>(
- &mut self,
+ self,
canonical_key: &Canonical<'tcx, K>,
operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>,
) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution>
@@ -117,8 +117,9 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
{
- let (infcx, key, canonical_inference_vars) =
- self.build_with_canonical(DUMMY_SP, canonical_key);
+ let (infcx, key, canonical_inference_vars) = self
+ .with_opaque_type_inference(DefiningAnchor::Bubble)
+ .build_with_canonical(DUMMY_SP, canonical_key);
let ocx = ObligationCtxt::new(&infcx);
let value = operation(&ocx, key)?;
ocx.make_canonicalized_query_response(canonical_inference_vars, value)
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index ed3994be9..56d37d58d 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -14,8 +14,7 @@
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
-#![feature(drain_filter)]
-#![feature(hash_drain_filter)]
+#![feature(extract_if)]
#![feature(let_chains)]
#![feature(if_let_guard)]
#![feature(never_type)]
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
new file mode 100644
index 000000000..422a6ee34
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -0,0 +1,195 @@
+use super::{EvalCtxt, SolverMode};
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+/// We may need to invert the alias relation direction if dealing an alias on the RHS.
+#[derive(Debug)]
+enum Invert {
+ No,
+ Yes,
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ #[instrument(level = "debug", skip(self), ret)]
+ pub(super) fn compute_alias_relate_goal(
+ &mut self,
+ goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
+ if lhs.is_infer() || rhs.is_infer() {
+ bug!(
+ "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated"
+ );
+ }
+
+ match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
+ (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
+
+ // RHS is not a projection, only way this is true is if LHS normalizes-to RHS
+ (Some(alias_lhs), None) => self.assemble_normalizes_to_candidate(
+ param_env,
+ 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)) => self.assemble_normalizes_to_candidate(
+ param_env,
+ alias_rhs,
+ lhs,
+ direction,
+ Invert::Yes,
+ ),
+
+ (Some(alias_lhs), Some(alias_rhs)) => {
+ debug!("both sides are aliases");
+
+ let mut candidates = Vec::new();
+ // LHS normalizes-to RHS
+ candidates.extend(self.assemble_normalizes_to_candidate(
+ param_env,
+ alias_lhs,
+ rhs,
+ direction,
+ Invert::No,
+ ));
+ // RHS normalizes-to RHS
+ candidates.extend(self.assemble_normalizes_to_candidate(
+ param_env,
+ alias_rhs,
+ lhs,
+ direction,
+ Invert::Yes,
+ ));
+ // Relate via substs
+ let subst_relate_response = self
+ .assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction);
+ candidates.extend(subst_relate_response);
+ debug!(?candidates);
+
+ if let Some(merged) = self.try_merge_responses(&candidates) {
+ Ok(merged)
+ } else {
+ // When relating two aliases and we have ambiguity, we prefer
+ // relating the generic arguments of the aliases over normalizing
+ // them. This is necessary for inference during typeck.
+ //
+ // As this is incomplete, we must not do so during coherence.
+ match self.solver_mode() {
+ SolverMode::Normal => {
+ if let Ok(subst_relate_response) = subst_relate_response {
+ Ok(subst_relate_response)
+ } else if let Ok(bidirectional_normalizes_to_response) = self
+ .assemble_bidirectional_normalizes_to_candidate(
+ param_env, lhs, rhs, direction,
+ )
+ {
+ Ok(bidirectional_normalizes_to_response)
+ } else {
+ self.flounder(&candidates)
+ }
+ }
+ SolverMode::Coherence => self.flounder(&candidates),
+ }
+ }
+ }
+ }
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ fn assemble_normalizes_to_candidate(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ alias: ty::AliasTy<'tcx>,
+ other: ty::Term<'tcx>,
+ direction: ty::AliasRelationDirection,
+ invert: Invert,
+ ) -> QueryResult<'tcx> {
+ self.probe_candidate("normalizes-to").enter(|ecx| {
+ ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+
+ fn normalizes_to_inner(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ alias: ty::AliasTy<'tcx>,
+ other: ty::Term<'tcx>,
+ direction: ty::AliasRelationDirection,
+ invert: Invert,
+ ) -> Result<(), NoSolution> {
+ let other = match direction {
+ // This is purely an optimization.
+ ty::AliasRelationDirection::Equate => other,
+
+ ty::AliasRelationDirection::Subtype => {
+ let fresh = self.next_term_infer_of_kind(other);
+ let (sub, sup) = match invert {
+ Invert::No => (fresh, other),
+ Invert::Yes => (other, fresh),
+ };
+ self.sub(param_env, sub, sup)?;
+ fresh
+ }
+ };
+ self.add_goal(Goal::new(
+ self.tcx(),
+ param_env,
+ ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }),
+ ));
+
+ Ok(())
+ }
+
+ fn assemble_subst_relate_candidate(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ alias_lhs: ty::AliasTy<'tcx>,
+ alias_rhs: ty::AliasTy<'tcx>,
+ direction: ty::AliasRelationDirection,
+ ) -> QueryResult<'tcx> {
+ self.probe_candidate("substs relate").enter(|ecx| {
+ match direction {
+ ty::AliasRelationDirection::Equate => {
+ ecx.eq(param_env, alias_lhs, alias_rhs)?;
+ }
+ ty::AliasRelationDirection::Subtype => {
+ ecx.sub(param_env, alias_lhs, alias_rhs)?;
+ }
+ }
+
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+
+ fn assemble_bidirectional_normalizes_to_candidate(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ lhs: ty::Term<'tcx>,
+ rhs: ty::Term<'tcx>,
+ direction: ty::AliasRelationDirection,
+ ) -> QueryResult<'tcx> {
+ self.probe_candidate("bidir normalizes-to").enter(|ecx| {
+ ecx.normalizes_to_inner(
+ param_env,
+ lhs.to_alias_ty(ecx.tcx()).unwrap(),
+ rhs,
+ direction,
+ Invert::No,
+ )?;
+ ecx.normalizes_to_inner(
+ param_env,
+ rhs.to_alias_ty(ecx.tcx()).unwrap(),
+ lhs,
+ direction,
+ Invert::Yes,
+ )?;
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index f32ff0442..28138054a 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -8,6 +8,7 @@ use rustc_hir::def_id::DefId;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::elaborate;
use rustc_infer::traits::Reveal;
+use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
use rustc_middle::ty::fast_reject::TreatProjections;
use rustc_middle::ty::TypeFoldable;
@@ -48,7 +49,7 @@ pub(super) enum CandidateSource {
/// Notable examples are auto traits, `Sized`, and `DiscriminantKind`.
/// For a list of all traits with builtin impls, check out the
/// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not
- BuiltinImpl,
+ BuiltinImpl(BuiltinImplSource),
/// An assumption from the environment.
///
/// More precisely we've used the `n-th` assumption in the `param_env`.
@@ -86,6 +87,16 @@ pub(super) enum CandidateSource {
AliasBound,
}
+/// Records additional information about what kind of built-in impl this is.
+/// This should only be used by selection.
+#[derive(Debug, Clone, Copy)]
+pub(super) enum BuiltinImplSource {
+ TraitUpcasting,
+ Object,
+ Misc,
+ Ambiguity,
+}
+
/// Methods used to assemble candidates for either trait or projection goals.
pub(super) trait GoalKind<'tcx>:
TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
@@ -105,7 +116,7 @@ pub(super) trait GoalKind<'tcx>:
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
) -> QueryResult<'tcx>;
@@ -115,7 +126,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_implied_clause(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
) -> QueryResult<'tcx> {
Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
@@ -131,7 +142,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_alias_bound_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
) -> QueryResult<'tcx> {
Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
ecx.validate_alias_bound_self_from_param_env(goal)
@@ -144,7 +155,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_object_bound_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
) -> QueryResult<'tcx> {
Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
let tcx = ecx.tcx();
@@ -294,7 +305,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// least structurally resolve the type one layer.
if goal.predicate.self_ty().is_ty_var() {
return vec![Candidate {
- source: CandidateSource::BuiltinImpl,
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
result: self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
.unwrap(),
@@ -320,11 +331,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
candidates
}
- /// If the self type of a goal is a projection, computing the relevant candidates is difficult.
+ /// If the self type of a goal is an alias we first try to normalize the self type
+ /// and compute the candidates for the normalized self type in case that succeeds.
+ ///
+ /// These candidates are used in addition to the ones with the alias as a self type.
+ /// We do this to simplify both builtin candidates and for better performance.
+ ///
+ /// We generate the builtin candidates on the fly by looking at the self type, e.g.
+ /// add `FnPtr` candidates if the self type is a function pointer. Handling builtin
+ /// candidates while the self type is still an alias seems difficult. This is similar
+ /// to `try_structurally_resolve_type` during hir typeck (FIXME once implemented).
///
- /// 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. We also have to consider candidates with the
- /// projection as a self type as well
+ /// Looking at all impls for some trait goal is prohibitively expensive. We therefore
+ /// only look at implementations with a matching self type. Because of this function,
+ /// we can avoid looking at all existing impls if the self type is an alias.
#[instrument(level = "debug", skip_all)]
fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
&mut self,
@@ -336,37 +356,41 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
return
};
- 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().inspect_err(|_| {
- debug!("self type normalization failed");
- })?;
- let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
- debug!(?normalized_ty, "self type normalized");
- // 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))
- },
- )
- });
+ let normalized_self_candidates: Result<_, NoSolution> =
+ self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|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(BuiltinImplSource::Ambiguity),
+ 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().inspect_err(|_| {
+ debug!("self type normalization failed");
+ })?;
+ let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
+ debug!(?normalized_ty, "self type normalized");
+ // 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);
@@ -445,9 +469,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
};
match result {
- Ok(result) => {
- candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
- }
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+ result,
+ }),
Err(NoSolution) => (),
}
@@ -455,7 +480,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
if lang_items.unsize_trait() == Some(trait_def_id) {
for result in G::consider_builtin_dyn_upcast_candidates(self, goal) {
- candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result });
+ candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
+ result,
+ });
}
}
}
@@ -508,10 +536,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
| ty::Placeholder(..)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Alias(ty::Inherent, _)
+ | ty::Alias(ty::Weak, _)
| ty::Error(_) => return,
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
| ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
- // Excluding IATs here as they don't have meaningful item bounds.
+ // Excluding IATs and type aliases here as they don't have meaningful item bounds.
ty::Alias(ty::Projection | ty::Opaque, alias_ty) => alias_ty,
};
@@ -618,9 +647,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
};
match result {
- Ok(result) => {
- candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
- }
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+ result,
+ }),
Err(NoSolution) => (),
}
}
@@ -631,6 +661,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
) {
+ let tcx = self.tcx();
+ if !tcx.trait_def(goal.predicate.trait_def_id(tcx)).implement_via_object {
+ return;
+ }
+
let self_ty = goal.predicate.self_ty();
let bounds = match *self_ty.kind() {
ty::Bool
@@ -663,7 +698,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
ty::Dynamic(bounds, ..) => bounds,
};
- let tcx = self.tcx();
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())
@@ -675,17 +709,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// 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)
- {
+ // We can remove this when we have implemented lifetime intersections in responses.
+ if assumption.as_projection_clause().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 })
- }
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+ result,
+ }),
Err(NoSolution) => (),
}
}
@@ -706,8 +739,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Err(_) => match self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
{
- Ok(result) => candidates
- .push(Candidate { source: CandidateSource::BuiltinImpl, result }),
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
+ 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.
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 0ede32c75..3bb8cad15 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -28,12 +28,12 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
| ty::Char => Ok(vec![]),
// Treat `str` like it's defined as `struct str([u8]);`
- ty::Str => Ok(vec![tcx.mk_slice(tcx.types.u8)]),
+ ty::Str => Ok(vec![Ty::new_slice(tcx, tcx.types.u8)]),
ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
- | ty::Alias(ty::Projection | ty::Inherent, ..)
+ | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
| ty::Placeholder(..)
| ty::Bound(..)
| ty::Infer(_) => {
@@ -96,7 +96,7 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>(
let br =
ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon(None) };
counter += 1;
- tcx.mk_re_late_bound(current_depth, br)
+ ty::Region::new_late_bound(tcx, current_depth, br)
}
// All free regions should be erased here.
r => bug!("unexpected region: {r:?}"),
@@ -148,11 +148,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
ty::Adt(def, substs) => {
let sized_crit = def.sized_constraint(ecx.tcx());
- Ok(sized_crit
- .0
- .iter()
- .map(|ty| sized_crit.rebind(*ty).subst(ecx.tcx(), substs))
- .collect())
+ Ok(sized_crit.subst_iter_copied(ecx.tcx(), substs).collect())
}
}
}
@@ -237,7 +233,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
{
Ok(Some(
sig.subst(tcx, substs)
- .map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())),
+ .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())),
))
} else {
Err(NoSolution)
@@ -246,7 +242,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
// 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()))))
+ Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output()))))
} else {
Err(NoSolution)
}
@@ -347,7 +343,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-) -> Vec<ty::Predicate<'tcx>> {
+) -> Vec<ty::Clause<'tcx>> {
let tcx = ecx.tcx();
let mut requirements = vec![];
requirements.extend(
@@ -357,7 +353,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
// FIXME(associated_const_equality): Also add associated consts to
// the requirements here.
if item.kind == ty::AssocKind::Type {
- requirements.extend(tcx.item_bounds(item.def_id).subst(tcx, trait_ref.substs));
+ requirements.extend(tcx.item_bounds(item.def_id).subst_iter(tcx, trait_ref.substs));
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
index ff4bff10c..255620489 100644
--- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
@@ -208,8 +208,25 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
t
}
- fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
- let r = self.infcx.shallow_resolve(r);
+ fn fold_region(&mut self, mut r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ match self.canonicalize_mode {
+ CanonicalizeMode::Input => {
+ // Don't resolve infer vars in input, since it affects
+ // caching and may cause trait selection bugs which rely
+ // on regions to be equal.
+ }
+ CanonicalizeMode::Response { .. } => {
+ if let ty::ReVar(vid) = *r {
+ r = self
+ .infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .opportunistic_resolve_var(self.infcx.tcx, vid);
+ }
+ }
+ }
+
let kind = match *r {
ty::ReLateBound(..) => return r,
@@ -255,7 +272,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
}),
);
let br = ty::BoundRegion { var, kind: BrAnon(None) };
- self.interner().mk_re_late_bound(self.binder_index, br)
+ ty::Region::new_late_bound(self.interner(), self.binder_index, br)
}
fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
@@ -266,7 +283,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
// 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);
+ t = Ty::new_var(self.infcx.tcx, root_vid);
vid = root_vid;
}
@@ -349,7 +366,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
}),
);
let bt = ty::BoundTy { var, kind: BoundTyKind::Anon };
- self.interner().mk_bound(self.binder_index, bt)
+ Ty::new_bound(self.infcx.tcx, self.binder_index, bt)
}
fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> {
@@ -360,7 +377,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
// 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());
+ c = ty::Const::new_var(self.infcx.tcx, root_vid, c.ty());
vid = root_vid;
}
@@ -409,6 +426,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
var
}),
);
- self.interner().mk_const(ty::ConstKind::Bound(self.binder_index, var), c.ty())
+ ty::Const::new_bound(self.infcx.tcx, self.binder_index, var, 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 f91c67277..74dfbdddb 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -9,25 +9,32 @@ use rustc_infer::infer::{
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::ObligationCause;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::traits::solve::inspect;
use rustc_middle::traits::solve::{
- CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques,
- PredefinedOpaquesData, QueryResult,
+ CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, MaybeCause,
+ PredefinedOpaques, PredefinedOpaquesData, QueryResult,
};
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{
- self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
- TypeVisitor,
+ self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
+ TypeVisitableExt, TypeVisitor,
};
+use rustc_session::config::DumpSolverProofTree;
use rustc_span::DUMMY_SP;
+use std::io::Write;
use std::ops::ControlFlow;
use crate::traits::specialization_graph;
+use super::inspect::ProofTreeBuilder;
use super::search_graph::{self, OverflowHandler};
use super::SolverMode;
use super::{search_graph::SearchGraph, Goal};
+pub use select::InferCtxtSelectExt;
mod canonical;
+mod probe;
+mod select;
pub struct EvalCtxt<'a, 'tcx> {
/// The inference context that backs (mostly) inference and placeholder terms
@@ -73,12 +80,8 @@ pub struct EvalCtxt<'a, 'tcx> {
// ambiguous goals. Instead, a probe needs to be introduced somewhere in the
// evaluation code.
tainted: Result<(), NoSolution>,
-}
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub(super) enum IsNormalizesToHack {
- Yes,
- No,
+ inspect: ProofTreeBuilder<'tcx>,
}
#[derive(Debug, Clone)]
@@ -110,6 +113,26 @@ impl NestedGoals<'_> {
}
}
+#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+pub enum GenerateProofTree {
+ Yes(UseGlobalCache),
+ No,
+}
+
+#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+pub enum UseGlobalCache {
+ Yes,
+ No,
+}
+impl UseGlobalCache {
+ pub fn from_bool(use_cache: bool) -> Self {
+ match use_cache {
+ true => UseGlobalCache::Yes,
+ false => UseGlobalCache::No,
+ }
+ }
+}
+
pub trait InferCtxtEvalExt<'tcx> {
/// Evaluates a goal from **outside** of the trait solver.
///
@@ -118,7 +141,11 @@ pub trait InferCtxtEvalExt<'tcx> {
fn evaluate_root_goal(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
- ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>;
+ generate_proof_tree: GenerateProofTree,
+ ) -> (
+ Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
+ Option<inspect::GoalEvaluation<'tcx>>,
+ );
}
impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
@@ -126,16 +153,39 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
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);
+ generate_proof_tree: GenerateProofTree,
+ ) -> (
+ Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
+ Option<inspect::GoalEvaluation<'tcx>>,
+ ) {
+ EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
+ ecx.evaluate_goal(IsNormalizesToHack::No, goal)
+ })
+ }
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+ pub(super) fn solver_mode(&self) -> SolverMode {
+ self.search_graph.solver_mode()
+ }
+
+ /// Creates a root evaluation context and search graph. This should only be
+ /// used from outside of any evaluation, and other methods should be preferred
+ /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
+ fn enter_root<R>(
+ infcx: &InferCtxt<'tcx>,
+ generate_proof_tree: GenerateProofTree,
+ f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R,
+ ) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
+ let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
+ let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);
let mut ecx = EvalCtxt {
search_graph: &mut search_graph,
- infcx: self,
+ infcx: infcx,
// Only relevant when canonicalizing the response,
// which we don't do within this evaluation context.
- predefined_opaques_in_body: self
+ predefined_opaques_in_body: infcx
.tcx
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
// Only relevant when canonicalizing the response.
@@ -143,8 +193,18 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
var_values: CanonicalVarValues::dummy(),
nested_goals: NestedGoals::new(),
tainted: Ok(()),
+ inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
};
- let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
+ let result = f(&mut ecx);
+
+ let tree = ecx.inspect.finalize();
+ if let (Some(tree), DumpSolverProofTree::Always) =
+ (&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree)
+ {
+ let mut lock = std::io::stdout().lock();
+ let _ = lock.write_fmt(format_args!("{tree:?}"));
+ let _ = lock.flush();
+ }
assert!(
ecx.nested_goals.is_empty(),
@@ -152,13 +212,68 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
);
assert!(search_graph.is_empty());
- result
+ (result, tree)
}
-}
-impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
- pub(super) fn solver_mode(&self) -> SolverMode {
- self.search_graph.solver_mode()
+ /// Creates a nested evaluation context that shares the same search graph as the
+ /// one passed in. This is suitable for evaluation, granted that the search graph
+ /// has had the nested goal recorded on its stack ([`SearchGraph::with_new_goal`]),
+ /// but it's preferable to use other methods that call this one rather than this
+ /// method directly.
+ ///
+ /// This function takes care of setting up the inference context, setting the anchor,
+ /// and registering opaques from the canonicalized input.
+ fn enter_canonical<R>(
+ tcx: TyCtxt<'tcx>,
+ search_graph: &'a mut search_graph::SearchGraph<'tcx>,
+ canonical_input: CanonicalInput<'tcx>,
+ goal_evaluation: &mut ProofTreeBuilder<'tcx>,
+ f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
+ ) -> R {
+ let intercrate = match search_graph.solver_mode() {
+ SolverMode::Normal => false,
+ SolverMode::Coherence => true,
+ };
+ let (ref infcx, input, var_values) = tcx
+ .infer_ctxt()
+ .intercrate(intercrate)
+ .with_next_trait_solver(true)
+ .with_opaque_type_inference(canonical_input.value.anchor)
+ .build_with_canonical(DUMMY_SP, &canonical_input);
+
+ let mut ecx = EvalCtxt {
+ infcx,
+ var_values,
+ predefined_opaques_in_body: input.predefined_opaques_in_body,
+ max_input_universe: canonical_input.max_universe,
+ search_graph,
+ nested_goals: NestedGoals::new(),
+ tainted: Ok(()),
+ inspect: goal_evaluation.new_goal_evaluation_step(input),
+ };
+
+ for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
+ ecx.insert_hidden_type(key, input.goal.param_env, ty)
+ .expect("failed to prepopulate opaque types");
+ }
+
+ if !ecx.nested_goals.is_empty() {
+ panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
+ }
+
+ let result = f(&mut ecx, input.goal);
+
+ goal_evaluation.goal_evaluation_step(ecx.inspect);
+
+ // When creating a query response we clone the opaque type constraints
+ // instead of taking them. This would cause an ICE here, since we have
+ // assertions against dropping an `InferCtxt` without taking opaques.
+ // FIXME: Once we remove support for the old impl we can remove this.
+ if input.anchor != DefiningAnchor::Error {
+ let _ = infcx.take_opaque_types();
+ }
+
+ result
}
/// The entry point of the solver.
@@ -170,58 +285,36 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
/// 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)]
+ #[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
fn evaluate_canonical_goal(
tcx: TyCtxt<'tcx>,
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
canonical_input: CanonicalInput<'tcx>,
+ mut goal_evaluation: &mut ProofTreeBuilder<'tcx>,
) -> QueryResult<'tcx> {
+ goal_evaluation.canonicalized_goal(canonical_input);
+
// Deal with overflow, caching, and coinduction.
//
// The actual solver logic happens in `ecx.compute_goal`.
- search_graph.with_new_goal(tcx, canonical_input, |search_graph| {
- let intercrate = match search_graph.solver_mode() {
- SolverMode::Normal => false,
- SolverMode::Coherence => true,
- };
- let (ref infcx, input, var_values) = tcx
- .infer_ctxt()
- .intercrate(intercrate)
- .with_opaque_type_inference(canonical_input.value.anchor)
- .build_with_canonical(DUMMY_SP, &canonical_input);
-
- for &(a, b) in &input.predefined_opaques_in_body.opaque_types {
- let InferOk { value: (), obligations } = infcx
- .register_hidden_type_in_new_solver(a, input.goal.param_env, b)
- .expect("expected opaque type instantiation to succeed");
- // We're only registering opaques already defined by the caller,
- // so we're not responsible for proving that they satisfy their
- // item bounds, unless we use them in a normalizes-to goal,
- // which is handled in `EvalCtxt::unify_existing_opaque_tys`.
- let _ = obligations;
- }
- let mut ecx = EvalCtxt {
- infcx,
- var_values,
- predefined_opaques_in_body: input.predefined_opaques_in_body,
- max_input_universe: canonical_input.max_universe,
- search_graph,
- nested_goals: NestedGoals::new(),
- tainted: Ok(()),
- };
-
- let result = ecx.compute_goal(input.goal);
-
- // When creating a query response we clone the opaque type constraints
- // instead of taking them. This would cause an ICE here, since we have
- // assertions against dropping an `InferCtxt` without taking opaques.
- // FIXME: Once we remove support for the old impl we can remove this.
- if input.anchor != DefiningAnchor::Error {
- let _ = infcx.take_opaque_types();
- }
-
- result
- })
+ search_graph.with_new_goal(
+ tcx,
+ canonical_input,
+ goal_evaluation,
+ |search_graph, goal_evaluation| {
+ EvalCtxt::enter_canonical(
+ tcx,
+ search_graph,
+ canonical_input,
+ goal_evaluation,
+ |ecx, goal| {
+ let result = ecx.compute_goal(goal);
+ ecx.inspect.query_result(result);
+ result
+ },
+ )
+ },
+ )
}
/// Recursively evaluates `goal`, returning whether any inference vars have
@@ -232,16 +325,37 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
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 mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack);
+ let canonical_response = EvalCtxt::evaluate_canonical_goal(
+ self.tcx(),
+ self.search_graph,
+ canonical_goal,
+ &mut goal_evaluation,
+ );
+ goal_evaluation.query_result(canonical_response);
+ let canonical_response = match canonical_response {
+ Err(e) => {
+ self.inspect.goal_evaluation(goal_evaluation);
+ return Err(e);
+ }
+ Ok(response) => response,
+ };
let has_changed = !canonical_response.value.var_values.is_identity()
|| !canonical_response.value.external_constraints.opaque_types.is_empty();
- let (certainty, nested_goals) = self.instantiate_and_apply_query_response(
+ let (certainty, nested_goals) = match self.instantiate_and_apply_query_response(
goal.param_env,
orig_values,
canonical_response,
- )?;
+ ) {
+ Err(e) => {
+ self.inspect.goal_evaluation(goal_evaluation);
+ return Err(e);
+ }
+ Ok(response) => response,
+ };
+ goal_evaluation.returned_goals(&nested_goals);
+ self.inspect.goal_evaluation(goal_evaluation);
if !has_changed && !nested_goals.is_empty() {
bug!("an unchanged goal shouldn't have any side-effects on instantiation");
@@ -261,9 +375,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
{
debug!("rerunning goal to check result is stable");
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
- let new_canonical_response =
- EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
- if !new_canonical_response.value.var_values.is_identity() {
+ let new_canonical_response = EvalCtxt::evaluate_canonical_goal(
+ self.tcx(),
+ self.search_graph,
+ canonical_goal,
+ // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal`
+ &mut ProofTreeBuilder::new_noop(),
+ )?;
+ // We only check for modulo regions as we convert all regions in
+ // the input to new existentials, even if they're expected to be
+ // `'static` or a placeholder region.
+ if !new_canonical_response.value.var_values.is_identity_modulo_regions() {
bug!(
"unstable result: re-canonicalized goal={canonical_goal:#?} \
first_response={canonical_response:#?} \
@@ -287,19 +409,19 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let kind = predicate.kind();
if let Some(kind) = kind.no_bound_vars() {
match kind {
- ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
self.compute_trait_goal(Goal { param_env, predicate })
}
- ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
self.compute_projection_goal(Goal { param_env, predicate })
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => {
self.compute_type_outlives_goal(Goal { param_env, predicate })
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => {
self.compute_region_outlives_goal(Goal { param_env, predicate })
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
}
ty::PredicateKind::Subtype(predicate) => {
@@ -316,24 +438,23 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
ty::PredicateKind::ObjectSafe(trait_def_id) => {
self.compute_object_safe_goal(trait_def_id)
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::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::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
+ self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct })
}
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("TypeWellFormedFromEnv is only used for Chalk")
+ ty::PredicateKind::ConstEquate(_, _) => {
+ bug!("ConstEquate should not be emitted when `-Ztrait-solver=next` is active")
}
ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self
.compute_alias_relate_goal(Goal {
param_env,
predicate: (lhs, rhs, direction),
}),
+ ty::PredicateKind::Ambiguous => {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+ }
}
} else {
let kind = self.infcx.instantiate_binder_with_placeholders(kind);
@@ -347,12 +468,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
// the certainty of all the goals.
#[instrument(level = "debug", skip(self))]
pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> {
+ let inspect = self.inspect.new_evaluate_added_goals();
+ let inspect = core::mem::replace(&mut self.inspect, inspect);
+
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| {
+ this.inspect.evaluate_added_goals_loop_start();
+
let mut has_changed = Err(Certainty::Yes);
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
@@ -441,29 +567,21 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
},
);
+ self.inspect.eval_added_goals_result(response);
+
if response.is_err() {
self.tainted = Err(NoSolution);
}
+ let goal_evaluations = std::mem::replace(&mut self.inspect, inspect);
+ self.inspect.added_goals_evaluation(goal_evaluations);
+
self.nested_goals = goals;
response
}
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
- pub(super) fn probe<T>(&mut self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T {
- let mut ecx = EvalCtxt {
- infcx: self.infcx,
- var_values: self.var_values,
- predefined_opaques_in_body: self.predefined_opaques_in_body,
- max_input_universe: self.max_input_universe,
- search_graph: self.search_graph,
- nested_goals: self.nested_goals.clone(),
- tainted: self.tainted,
- };
- self.infcx.probe(|_| f(&mut ecx))
- }
-
pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
@@ -706,6 +824,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
scope: Ty<'tcx>,
assume: rustc_transmute::Assume,
) -> Result<Certainty, NoSolution> {
+ use rustc_transmute::Answer;
// FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
ObligationCause::dummy(),
@@ -713,30 +832,53 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
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),
+ Answer::Yes => Ok(Certainty::Yes),
+ Answer::No(_) | Answer::If(_) => Err(NoSolution),
}
}
- pub(super) fn can_define_opaque_ty(&mut self, def_id: LocalDefId) -> bool {
+ pub(super) fn can_define_opaque_ty(&self, def_id: LocalDefId) -> bool {
self.infcx.opaque_type_origin(def_id).is_some()
}
- pub(super) fn register_opaque_ty(
+ pub(super) fn insert_hidden_type(
&mut self,
- a: ty::OpaqueTypeKey<'tcx>,
- b: Ty<'tcx>,
+ opaque_type_key: OpaqueTypeKey<'tcx>,
param_env: ty::ParamEnv<'tcx>,
+ hidden_ty: Ty<'tcx>,
) -> Result<(), NoSolution> {
- let InferOk { value: (), obligations } =
- self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?;
- self.add_goals(obligations.into_iter().map(|obligation| obligation.into()));
+ let mut obligations = Vec::new();
+ self.infcx.insert_hidden_type(
+ opaque_type_key,
+ &ObligationCause::dummy(),
+ param_env,
+ hidden_ty,
+ true,
+ &mut obligations,
+ )?;
+ self.add_goals(obligations.into_iter().map(|o| o.into()));
Ok(())
}
+ pub(super) fn add_item_bounds_for_hidden_type(
+ &mut self,
+ opaque_def_id: DefId,
+ opaque_substs: ty::SubstsRef<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ hidden_ty: Ty<'tcx>,
+ ) {
+ let mut obligations = Vec::new();
+ self.infcx.add_item_bounds_for_hidden_type(
+ opaque_def_id,
+ opaque_substs,
+ ObligationCause::dummy(),
+ param_env,
+ hidden_ty,
+ &mut obligations,
+ );
+ self.add_goals(obligations.into_iter().map(|o| o.into()));
+ }
+
// Do something for each opaque/hidden pair defined with `def_id` in the
// current inference context.
pub(super) fn unify_existing_opaque_tys(
@@ -753,23 +895,37 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if candidate_key.def_id != key.def_id {
continue;
}
- values.extend(self.probe(|ecx| {
+ values.extend(self.probe_candidate("opaque type storage").enter(|ecx| {
for (a, b) in std::iter::zip(candidate_key.substs, key.substs) {
ecx.eq(param_env, a, b)?;
}
ecx.eq(param_env, candidate_ty, ty)?;
- let mut obl = vec![];
- ecx.infcx.add_item_bounds_for_hidden_type(
- candidate_key,
- ObligationCause::dummy(),
+ ecx.add_item_bounds_for_hidden_type(
+ candidate_key.def_id.to_def_id(),
+ candidate_key.substs,
param_env,
candidate_ty,
- &mut obl,
);
- ecx.add_goals(obl.into_iter().map(Into::into));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}));
}
values
}
+
+ // Try to evaluate a const, or return `None` if the const is too generic.
+ // This doesn't mean the const isn't evaluatable, though, and should be treated
+ // as an ambiguity rather than no-solution.
+ pub(super) fn try_const_eval_resolve(
+ &self,
+ param_env: ty::ParamEnv<'tcx>,
+ unevaluated: ty::UnevaluatedConst<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Option<ty::Const<'tcx>> {
+ use rustc_middle::mir::interpret::ErrorHandled;
+ match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) {
+ Ok(ct) => Some(ct),
+ Err(ErrorHandled::Reported(e)) => Some(ty::Const::new_error(self.tcx(), e.into(), ty)),
+ Err(ErrorHandled::TooGeneric) => None,
+ }
+ }
}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
index fdb209fbf..637d45888 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -11,16 +11,16 @@
use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer};
use crate::solve::{CanonicalResponse, QueryResult, Response};
+use rustc_data_structures::fx::FxHashSet;
use rustc_index::IndexVec;
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::infer::InferOk;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{
ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
};
-use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty};
+use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
use rustc_span::DUMMY_SP;
use std::iter;
use std::ops::Deref;
@@ -28,10 +28,10 @@ use std::ops::Deref;
impl<'tcx> EvalCtxt<'_, 'tcx> {
/// Canonicalizes the goal remembering the original values
/// for each bound variable.
- pub(super) fn canonicalize_goal(
+ pub(super) fn canonicalize_goal<T: TypeFoldable<TyCtxt<'tcx>>>(
&self,
- goal: Goal<'tcx, ty::Predicate<'tcx>>,
- ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx>) {
+ goal: Goal<'tcx, T>,
+ ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
let mut orig_values = Default::default();
let canonical_goal = Canonicalizer::canonicalize(
self.infcx,
@@ -137,10 +137,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self), ret)]
fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
+ // We only check for leaks from universes which were entered inside
+ // of the query.
+ self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
+ debug!(?e, "failed the leak check");
+ NoSolution
+ })?;
+
// Cannot use `take_registered_region_obligations` as we may compute the response
// inside of a `probe` whenever we have multiple choices inside of the solver.
let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
- let region_constraints = self.infcx.with_region_constraints(|region_constraints| {
+ let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| {
make_query_region_constraints(
self.tcx(),
region_obligations
@@ -150,6 +157,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
)
});
+ let mut seen = FxHashSet::default();
+ region_constraints.outlives.retain(|outlives| seen.insert(*outlives));
+
let mut opaque_types = self.infcx.clone_opaque_types_for_query_response();
// Only return opaque type keys for newly-defined opaques
opaque_types.retain(|(a, _)| {
@@ -310,12 +320,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)],
) -> Result<(), NoSolution> {
- for &(a, b) in opaque_types {
- let InferOk { value: (), obligations } =
- self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?;
- // It's sound to drop these obligations, since the normalizes-to goal
- // is responsible for proving these obligations.
- let _ = obligations;
+ for &(key, ty) in opaque_types {
+ self.insert_hidden_type(key, param_env, ty)?;
}
Ok(())
}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
new file mode 100644
index 000000000..4477ea7d5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -0,0 +1,67 @@
+use super::EvalCtxt;
+use rustc_middle::traits::solve::{inspect, QueryResult};
+use std::marker::PhantomData;
+
+pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> {
+ ecx: &'me mut EvalCtxt<'a, 'tcx>,
+ probe_kind: F,
+ _result: PhantomData<T>,
+}
+
+impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T>
+where
+ F: FnOnce(&T) -> inspect::CandidateKind<'tcx>,
+{
+ pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T {
+ let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self;
+
+ let mut nested_ecx = EvalCtxt {
+ infcx: outer_ecx.infcx,
+ var_values: outer_ecx.var_values,
+ predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
+ max_input_universe: outer_ecx.max_input_universe,
+ search_graph: outer_ecx.search_graph,
+ nested_goals: outer_ecx.nested_goals.clone(),
+ tainted: outer_ecx.tainted,
+ inspect: outer_ecx.inspect.new_goal_candidate(),
+ };
+ let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx));
+ if !outer_ecx.inspect.is_noop() {
+ let cand_kind = probe_kind(&r);
+ nested_ecx.inspect.candidate_kind(cand_kind);
+ outer_ecx.inspect.goal_candidate(nested_ecx.inspect);
+ }
+ r
+ }
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+ /// `probe_kind` is only called when proof tree building is enabled so it can be
+ /// as expensive as necessary to output the desired information.
+ pub(in crate::solve) fn probe<F, T>(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, 'tcx, F, T>
+ where
+ F: FnOnce(&T) -> inspect::CandidateKind<'tcx>,
+ {
+ ProbeCtxt { ecx: self, probe_kind, _result: PhantomData }
+ }
+
+ pub(in crate::solve) fn probe_candidate(
+ &mut self,
+ name: &'static str,
+ ) -> ProbeCtxt<
+ '_,
+ 'a,
+ 'tcx,
+ impl FnOnce(&QueryResult<'tcx>) -> inspect::CandidateKind<'tcx>,
+ QueryResult<'tcx>,
+ > {
+ ProbeCtxt {
+ ecx: self,
+ probe_kind: move |result: &QueryResult<'tcx>| inspect::CandidateKind::Candidate {
+ name: name.to_string(),
+ result: *result,
+ },
+ _result: PhantomData,
+ }
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
new file mode 100644
index 000000000..bf6cbef8c
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -0,0 +1,307 @@
+use std::ops::ControlFlow;
+
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
+use rustc_infer::traits::util::supertraits;
+use rustc_infer::traits::{
+ Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult,
+};
+use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
+use rustc_middle::traits::{
+ ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
+ ObligationCause, SelectionError,
+};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::DUMMY_SP;
+
+use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
+use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
+use crate::solve::inspect::ProofTreeBuilder;
+use crate::solve::search_graph::OverflowHandler;
+use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
+
+pub trait InferCtxtSelectExt<'tcx> {
+ fn select_in_new_trait_solver(
+ &self,
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> SelectionResult<'tcx, Selection<'tcx>>;
+}
+
+impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
+ fn select_in_new_trait_solver(
+ &self,
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> SelectionResult<'tcx, Selection<'tcx>> {
+ assert!(self.next_trait_solver());
+
+ let trait_goal = Goal::new(
+ self.tcx,
+ obligation.param_env,
+ self.instantiate_binder_with_placeholders(obligation.predicate),
+ );
+
+ let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::No, |ecx| {
+ let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
+ let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
+ let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
+
+ // pseudo-winnow
+ if candidates.len() == 0 {
+ return Err(SelectionError::Unimplemented);
+ } else if candidates.len() > 1 {
+ let mut i = 0;
+ while i < candidates.len() {
+ let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
+ candidate_should_be_dropped_in_favor_of(
+ ecx.tcx(),
+ &candidates[i],
+ &candidates[j],
+ )
+ });
+ if should_drop_i {
+ candidates.swap_remove(i);
+ } else {
+ i += 1;
+ if i > 1 {
+ return Ok(None);
+ }
+ }
+ }
+ }
+
+ let candidate = candidates.pop().unwrap();
+ let (certainty, nested_goals) = ecx
+ .instantiate_and_apply_query_response(
+ trait_goal.param_env,
+ orig_values,
+ candidate.result,
+ )
+ .map_err(|_| SelectionError::Unimplemented)?;
+
+ Ok(Some((candidate, certainty, nested_goals)))
+ });
+
+ let (candidate, certainty, nested_goals) = match result {
+ Ok(Some((candidate, certainty, nested_goals))) => (candidate, certainty, nested_goals),
+ Ok(None) => return Ok(None),
+ Err(e) => return Err(e),
+ };
+
+ let nested_obligations: Vec<_> = nested_goals
+ .into_iter()
+ .map(|goal| {
+ Obligation::new(self.tcx, ObligationCause::dummy(), goal.param_env, goal.predicate)
+ })
+ .collect();
+
+ let goal = self.resolve_vars_if_possible(trait_goal);
+ match (certainty, candidate.source) {
+ // Rematching the implementation will instantiate the same nested goals that
+ // would have caused the ambiguity, so we can still make progress here regardless.
+ (_, CandidateSource::Impl(def_id)) => {
+ rematch_impl(self, goal, def_id, nested_obligations)
+ }
+
+ // Rematching the dyn upcast or object goal will instantiate the same nested
+ // goals that would have caused the ambiguity, so we can still make progress here
+ // regardless.
+ // FIXME: This doesn't actually check the object bounds hold here.
+ (
+ _,
+ CandidateSource::BuiltinImpl(
+ BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting,
+ ),
+ ) => rematch_object(self, goal, nested_obligations),
+
+ // Technically some builtin impls have nested obligations, but if
+ // `Certainty::Yes`, then they should've all been verified and don't
+ // need re-checking.
+ (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
+ Ok(Some(ImplSource::Builtin(nested_obligations)))
+ }
+
+ // It's fine not to do anything to rematch these, since there are no
+ // nested obligations.
+ (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
+ Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst)))
+ }
+
+ (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity))
+ | (Certainty::Maybe(_), _) => Ok(None),
+ }
+ }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ fn compute_canonical_trait_candidates(
+ &mut self,
+ canonical_input: CanonicalInput<'tcx>,
+ ) -> Vec<Candidate<'tcx>> {
+ // This doesn't record the canonical goal on the stack during the
+ // candidate assembly step, but that's fine. Selection is conceptually
+ // outside of the solver, and if there were any cycles, we'd encounter
+ // the cycle anyways one step later.
+ EvalCtxt::enter_canonical(
+ self.tcx(),
+ self.search_graph(),
+ canonical_input,
+ // FIXME: This is wrong, idk if we even want to track stuff here.
+ &mut ProofTreeBuilder::new_noop(),
+ |ecx, goal| {
+ let trait_goal = Goal {
+ param_env: goal.param_env,
+ predicate: goal
+ .predicate
+ .to_opt_poly_trait_pred()
+ .expect("we canonicalized a trait goal")
+ .no_bound_vars()
+ .expect("we instantiated all bound vars"),
+ };
+ ecx.assemble_and_evaluate_candidates(trait_goal)
+ },
+ )
+ }
+}
+
+fn candidate_should_be_dropped_in_favor_of<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ victim: &Candidate<'tcx>,
+ other: &Candidate<'tcx>,
+) -> bool {
+ match (victim.source, other.source) {
+ (CandidateSource::ParamEnv(victim_idx), CandidateSource::ParamEnv(other_idx)) => {
+ victim_idx >= other_idx
+ }
+ (_, CandidateSource::ParamEnv(_)) => true,
+
+ (
+ CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+ CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+ ) => false,
+ (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true,
+
+ (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
+ tcx.specializes((other_def_id, victim_def_id))
+ && other.result.value.certainty == Certainty::Yes
+ }
+
+ _ => false,
+ }
+}
+
+fn rematch_impl<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
+ impl_def_id: DefId,
+ mut nested: Vec<PredicateObligation<'tcx>>,
+) -> SelectionResult<'tcx, Selection<'tcx>> {
+ let substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+ let impl_trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap().subst(infcx.tcx, substs);
+
+ nested.extend(
+ infcx
+ .at(&ObligationCause::dummy(), goal.param_env)
+ .eq(DefineOpaqueTypes::No, goal.predicate.trait_ref, impl_trait_ref)
+ .map_err(|_| SelectionError::Unimplemented)?
+ .into_obligations(),
+ );
+
+ nested.extend(
+ infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, substs).into_iter().map(
+ |(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred),
+ ),
+ );
+
+ Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, substs, nested })))
+}
+
+fn rematch_object<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
+ mut nested: Vec<PredicateObligation<'tcx>>,
+) -> SelectionResult<'tcx, Selection<'tcx>> {
+ let self_ty = goal.predicate.self_ty();
+ let ty::Dynamic(data, _, source_kind) = *self_ty.kind()
+ else {
+ bug!()
+ };
+ let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, self_ty);
+
+ let (is_upcasting, target_trait_ref_unnormalized) = if Some(goal.predicate.def_id())
+ == infcx.tcx.lang_items().unsize_trait()
+ {
+ assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*");
+ if let ty::Dynamic(data, _, ty::Dyn) = goal.predicate.trait_ref.substs.type_at(1).kind() {
+ (true, data.principal().unwrap().with_self_ty(infcx.tcx, self_ty))
+ } else {
+ bug!()
+ }
+ } else {
+ (false, ty::Binder::dummy(goal.predicate.trait_ref))
+ };
+
+ let mut target_trait_ref = None;
+ for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) {
+ let result = infcx.commit_if_ok(|_| {
+ infcx.at(&ObligationCause::dummy(), goal.param_env).eq(
+ DefineOpaqueTypes::No,
+ target_trait_ref_unnormalized,
+ candidate_trait_ref,
+ )
+
+ // FIXME: We probably should at least shallowly verify these...
+ });
+
+ match result {
+ Ok(InferOk { value: (), obligations }) => {
+ target_trait_ref = Some(candidate_trait_ref);
+ nested.extend(obligations);
+ break;
+ }
+ Err(_) => continue,
+ }
+ }
+
+ let target_trait_ref = target_trait_ref.unwrap();
+
+ let mut offset = 0;
+ let Some((vtable_base, vtable_vptr_slot)) =
+ prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| {
+ match segment {
+ VtblSegment::MetadataDSA => {
+ offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
+ }
+ VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+ let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref);
+
+ if trait_ref == target_trait_ref {
+ if emit_vptr {
+ return ControlFlow::Break((
+ offset,
+ Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)),
+ ));
+ } else {
+ return ControlFlow::Break((offset, None));
+ }
+ }
+
+ offset += own_vtable_entries;
+ if emit_vptr {
+ offset += 1;
+ }
+ }
+ }
+ ControlFlow::Continue(())
+ })
+ else {
+ bug!();
+ };
+
+ // If we're upcasting, get the offset of the vtable pointer, otherwise get
+ // the base of the vtable.
+ Ok(Some(if is_upcasting {
+ ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
+ } else {
+ ImplSource::Object(ImplSourceObjectData { vtable_base, nested })
+ }))
+}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 4a403196c..88ee14c4d 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -10,6 +10,7 @@ use rustc_infer::traits::{
use rustc_middle::ty;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use super::eval_ctxt::GenerateProofTree;
use super::{Certainty, InferCtxtEvalExt};
/// A trait engine using the new trait solver.
@@ -25,20 +26,28 @@ use super::{Certainty, InferCtxtEvalExt};
/// here as this will have to deal with far more root goals than `evaluate_all`.
pub struct FulfillmentCtxt<'tcx> {
obligations: Vec<PredicateObligation<'tcx>>,
+
+ /// The snapshot in which this context was created. Using the context
+ /// outside of this snapshot leads to subtle bugs if the snapshot
+ /// gets rolled back. Because of this we explicitly check that we only
+ /// use the context in exactly this snapshot.
+ usable_in_snapshot: usize,
}
impl<'tcx> FulfillmentCtxt<'tcx> {
- pub fn new() -> FulfillmentCtxt<'tcx> {
- FulfillmentCtxt { obligations: Vec::new() }
+ pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
+ FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() }
}
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
+ #[instrument(level = "debug", skip(self, infcx))]
fn register_predicate_obligation(
&mut self,
- _infcx: &InferCtxt<'tcx>,
+ infcx: &InferCtxt<'tcx>,
obligation: PredicateObligation<'tcx>,
) {
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
self.obligations.push(obligation);
}
@@ -46,8 +55,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
self.obligations
.drain(..)
.map(|obligation| {
- let code =
- infcx.probe(|_| match infcx.evaluate_root_goal(obligation.clone().into()) {
+ let code = infcx.probe(|_| {
+ match infcx
+ .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::No)
+ .0
+ {
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => {
FulfillmentErrorCode::CodeAmbiguity { overflow: false }
}
@@ -60,7 +72,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
Err(_) => {
bug!("did not expect selection error when collecting ambiguity errors")
}
- });
+ }
+ });
FulfillmentError {
obligation: obligation.clone(),
@@ -72,6 +85,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
}
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
let mut errors = Vec::new();
for i in 0.. {
if !infcx.tcx.recursion_limit().value_within_limit(i) {
@@ -81,72 +95,61 @@ 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, nested_goals) = match infcx.evaluate_root_goal(goal) {
- Ok(result) => result,
- Err(NoSolution) => {
- errors.push(FulfillmentError {
- obligation: obligation.clone(),
- code: match goal.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(_)) => {
- FulfillmentErrorCode::CodeProjectionError(
- // FIXME: This could be a `Sorts` if the term is a type
- MismatchedProjectionTypes { err: TypeError::Mismatch },
- )
- }
- ty::PredicateKind::AliasRelate(_, _, _) => {
- FulfillmentErrorCode::CodeProjectionError(
- MismatchedProjectionTypes { err: TypeError::Mismatch },
- )
- }
- ty::PredicateKind::Subtype(pred) => {
- let (a, b) = infcx.instantiate_binder_with_placeholders(
- goal.predicate.kind().rebind((pred.a, pred.b)),
- );
- let expected_found = ExpectedFound::new(true, a, b);
- FulfillmentErrorCode::CodeSubtypeError(
- expected_found,
- TypeError::Sorts(expected_found),
- )
- }
- ty::PredicateKind::Coerce(pred) => {
- let (a, b) = infcx.instantiate_binder_with_placeholders(
- goal.predicate.kind().rebind((pred.a, pred.b)),
- );
- let expected_found = ExpectedFound::new(false, a, b);
- FulfillmentErrorCode::CodeSubtypeError(
- expected_found,
- TypeError::Sorts(expected_found),
- )
- }
- ty::PredicateKind::ConstEquate(a, b) => {
- let (a, b) = infcx.instantiate_binder_with_placeholders(
- goal.predicate.kind().rebind((a, b)),
- );
- let expected_found = ExpectedFound::new(true, a, b);
- FulfillmentErrorCode::CodeConstEquateError(
- expected_found,
- TypeError::ConstMismatch(expected_found),
- )
- }
- ty::PredicateKind::Clause(_)
- | ty::PredicateKind::WellFormed(_)
- | ty::PredicateKind::ObjectSafe(_)
- | ty::PredicateKind::ClosureKind(_, _, _)
- | ty::PredicateKind::ConstEvaluatable(_)
- | ty::PredicateKind::Ambiguous => {
- FulfillmentErrorCode::CodeSelectionError(
- SelectionError::Unimplemented,
- )
- }
- ty::PredicateKind::TypeWellFormedFromEnv(_) => {
- bug!("unexpected goal: {goal:?}")
- }
- },
- root_obligation: obligation,
- });
- continue;
- }
- };
+ let (changed, certainty, nested_goals) =
+ match infcx.evaluate_root_goal(goal, GenerateProofTree::No).0 {
+ Ok(result) => result,
+ Err(NoSolution) => {
+ errors.push(FulfillmentError {
+ obligation: obligation.clone(),
+ code: match goal.predicate.kind().skip_binder() {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
+ FulfillmentErrorCode::CodeProjectionError(
+ // FIXME: This could be a `Sorts` if the term is a type
+ MismatchedProjectionTypes { err: TypeError::Mismatch },
+ )
+ }
+ ty::PredicateKind::AliasRelate(_, _, _) => {
+ FulfillmentErrorCode::CodeProjectionError(
+ MismatchedProjectionTypes { err: TypeError::Mismatch },
+ )
+ }
+ ty::PredicateKind::Subtype(pred) => {
+ let (a, b) = infcx.instantiate_binder_with_placeholders(
+ goal.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(true, a, b);
+ FulfillmentErrorCode::CodeSubtypeError(
+ expected_found,
+ TypeError::Sorts(expected_found),
+ )
+ }
+ ty::PredicateKind::Coerce(pred) => {
+ let (a, b) = infcx.instantiate_binder_with_placeholders(
+ goal.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(false, a, b);
+ FulfillmentErrorCode::CodeSubtypeError(
+ expected_found,
+ TypeError::Sorts(expected_found),
+ )
+ }
+ ty::PredicateKind::Clause(_)
+ | ty::PredicateKind::ObjectSafe(_)
+ | ty::PredicateKind::ClosureKind(_, _, _)
+ | ty::PredicateKind::Ambiguous => {
+ FulfillmentErrorCode::CodeSelectionError(
+ SelectionError::Unimplemented,
+ )
+ }
+ ty::PredicateKind::ConstEquate(..) => {
+ bug!("unexpected goal: {goal:?}")
+ }
+ },
+ root_obligation: obligation,
+ });
+ 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| {
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
new file mode 100644
index 000000000..2d6717fda
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -0,0 +1,435 @@
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::inspect::{self, CacheHit, CandidateKind};
+use rustc_middle::traits::solve::{
+ CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult,
+};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::config::DumpSolverProofTree;
+
+use super::eval_ctxt::UseGlobalCache;
+use super::GenerateProofTree;
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipGoalEvaluation<'tcx> {
+ pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ pub canonicalized_goal: Option<CanonicalInput<'tcx>>,
+
+ pub evaluation_steps: Vec<WipGoalEvaluationStep<'tcx>>,
+
+ pub cache_hit: Option<CacheHit>,
+ pub is_normalizes_to_hack: IsNormalizesToHack,
+ pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+
+ pub result: Option<QueryResult<'tcx>>,
+}
+
+impl<'tcx> WipGoalEvaluation<'tcx> {
+ pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> {
+ inspect::GoalEvaluation {
+ uncanonicalized_goal: self.uncanonicalized_goal,
+ canonicalized_goal: self.canonicalized_goal.unwrap(),
+ kind: match self.cache_hit {
+ Some(hit) => inspect::GoalEvaluationKind::CacheHit(hit),
+ None => inspect::GoalEvaluationKind::Uncached {
+ revisions: self
+ .evaluation_steps
+ .into_iter()
+ .map(WipGoalEvaluationStep::finalize)
+ .collect(),
+ },
+ },
+ is_normalizes_to_hack: self.is_normalizes_to_hack,
+ returned_goals: self.returned_goals,
+ result: self.result.unwrap(),
+ }
+ }
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipAddedGoalsEvaluation<'tcx> {
+ pub evaluations: Vec<Vec<WipGoalEvaluation<'tcx>>>,
+ pub result: Option<Result<Certainty, NoSolution>>,
+}
+
+impl<'tcx> WipAddedGoalsEvaluation<'tcx> {
+ pub fn finalize(self) -> inspect::AddedGoalsEvaluation<'tcx> {
+ inspect::AddedGoalsEvaluation {
+ evaluations: self
+ .evaluations
+ .into_iter()
+ .map(|evaluations| {
+ evaluations.into_iter().map(WipGoalEvaluation::finalize).collect()
+ })
+ .collect(),
+ result: self.result.unwrap(),
+ }
+ }
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipGoalEvaluationStep<'tcx> {
+ pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+
+ pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
+ pub candidates: Vec<WipGoalCandidate<'tcx>>,
+
+ pub result: Option<QueryResult<'tcx>>,
+}
+
+impl<'tcx> WipGoalEvaluationStep<'tcx> {
+ pub fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> {
+ inspect::GoalEvaluationStep {
+ instantiated_goal: self.instantiated_goal,
+ nested_goal_evaluations: self
+ .nested_goal_evaluations
+ .into_iter()
+ .map(WipAddedGoalsEvaluation::finalize)
+ .collect(),
+ candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(),
+ result: self.result.unwrap(),
+ }
+ }
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipGoalCandidate<'tcx> {
+ pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
+ pub candidates: Vec<WipGoalCandidate<'tcx>>,
+ pub kind: Option<CandidateKind<'tcx>>,
+}
+
+impl<'tcx> WipGoalCandidate<'tcx> {
+ pub fn finalize(self) -> inspect::GoalCandidate<'tcx> {
+ inspect::GoalCandidate {
+ nested_goal_evaluations: self
+ .nested_goal_evaluations
+ .into_iter()
+ .map(WipAddedGoalsEvaluation::finalize)
+ .collect(),
+ candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(),
+ kind: self.kind.unwrap(),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum DebugSolver<'tcx> {
+ Root,
+ GoalEvaluation(WipGoalEvaluation<'tcx>),
+ AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>),
+ GoalEvaluationStep(WipGoalEvaluationStep<'tcx>),
+ GoalCandidate(WipGoalCandidate<'tcx>),
+}
+
+impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
+ fn from(g: WipGoalEvaluation<'tcx>) -> DebugSolver<'tcx> {
+ DebugSolver::GoalEvaluation(g)
+ }
+}
+
+impl<'tcx> From<WipAddedGoalsEvaluation<'tcx>> for DebugSolver<'tcx> {
+ fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> {
+ DebugSolver::AddedGoalsEvaluation(g)
+ }
+}
+
+impl<'tcx> From<WipGoalEvaluationStep<'tcx>> for DebugSolver<'tcx> {
+ fn from(g: WipGoalEvaluationStep<'tcx>) -> DebugSolver<'tcx> {
+ DebugSolver::GoalEvaluationStep(g)
+ }
+}
+
+impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> {
+ fn from(g: WipGoalCandidate<'tcx>) -> DebugSolver<'tcx> {
+ DebugSolver::GoalCandidate(g)
+ }
+}
+
+pub struct ProofTreeBuilder<'tcx> {
+ state: Option<Box<BuilderData<'tcx>>>,
+}
+
+struct BuilderData<'tcx> {
+ tree: DebugSolver<'tcx>,
+ use_global_cache: UseGlobalCache,
+}
+
+impl<'tcx> ProofTreeBuilder<'tcx> {
+ fn new(
+ state: impl Into<DebugSolver<'tcx>>,
+ use_global_cache: UseGlobalCache,
+ ) -> ProofTreeBuilder<'tcx> {
+ ProofTreeBuilder {
+ state: Some(Box::new(BuilderData { tree: state.into(), use_global_cache })),
+ }
+ }
+
+ fn nested(&self, state: impl Into<DebugSolver<'tcx>>) -> Self {
+ match &self.state {
+ Some(prev_state) => Self {
+ state: Some(Box::new(BuilderData {
+ tree: state.into(),
+ use_global_cache: prev_state.use_global_cache,
+ })),
+ },
+ None => Self { state: None },
+ }
+ }
+
+ fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> {
+ self.state.as_mut().map(|boxed| &mut boxed.tree)
+ }
+
+ pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> {
+ match self.state?.tree {
+ DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
+ Some(wip_goal_evaluation.finalize())
+ }
+ root => unreachable!("unexpected proof tree builder root node: {:?}", root),
+ }
+ }
+
+ pub fn use_global_cache(&self) -> bool {
+ self.state
+ .as_ref()
+ .map(|state| matches!(state.use_global_cache, UseGlobalCache::Yes))
+ .unwrap_or(true)
+ }
+
+ pub fn new_maybe_root(
+ tcx: TyCtxt<'tcx>,
+ generate_proof_tree: GenerateProofTree,
+ ) -> ProofTreeBuilder<'tcx> {
+ let generate_proof_tree = match (
+ tcx.sess.opts.unstable_opts.dump_solver_proof_tree,
+ tcx.sess.opts.unstable_opts.dump_solver_proof_tree_use_cache,
+ generate_proof_tree,
+ ) {
+ (_, Some(use_cache), GenerateProofTree::Yes(_)) => {
+ GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
+ }
+
+ (DumpSolverProofTree::Always, use_cache, GenerateProofTree::No) => {
+ let use_cache = use_cache.unwrap_or(true);
+ GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
+ }
+
+ (_, None, GenerateProofTree::Yes(_)) => generate_proof_tree,
+ (DumpSolverProofTree::Never, _, _) => generate_proof_tree,
+ (DumpSolverProofTree::OnError, _, _) => generate_proof_tree,
+ };
+
+ match generate_proof_tree {
+ GenerateProofTree::No => ProofTreeBuilder::new_noop(),
+ GenerateProofTree::Yes(global_cache_disabled) => {
+ ProofTreeBuilder::new_root(global_cache_disabled)
+ }
+ }
+ }
+
+ pub fn new_root(use_global_cache: UseGlobalCache) -> ProofTreeBuilder<'tcx> {
+ ProofTreeBuilder::new(DebugSolver::Root, use_global_cache)
+ }
+
+ pub fn new_noop() -> ProofTreeBuilder<'tcx> {
+ ProofTreeBuilder { state: None }
+ }
+
+ pub fn is_noop(&self) -> bool {
+ self.state.is_none()
+ }
+
+ pub fn new_goal_evaluation(
+ &mut self,
+ goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ is_normalizes_to_hack: IsNormalizesToHack,
+ ) -> ProofTreeBuilder<'tcx> {
+ if self.state.is_none() {
+ return ProofTreeBuilder { state: None };
+ }
+
+ self.nested(WipGoalEvaluation {
+ uncanonicalized_goal: goal,
+ canonicalized_goal: None,
+ evaluation_steps: vec![],
+ is_normalizes_to_hack,
+ cache_hit: None,
+ returned_goals: vec![],
+ result: None,
+ })
+ }
+
+ pub fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalEvaluation(goal_evaluation) => {
+ assert_eq!(goal_evaluation.canonicalized_goal.replace(canonical_goal), None);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn cache_hit(&mut self, cache_hit: CacheHit) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalEvaluation(goal_evaluation) => {
+ assert_eq!(goal_evaluation.cache_hit.replace(cache_hit), None);
+ }
+ _ => unreachable!(),
+ };
+ }
+ }
+
+ pub fn returned_goals(&mut self, goals: &[Goal<'tcx, ty::Predicate<'tcx>>]) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalEvaluation(evaluation) => {
+ assert!(evaluation.returned_goals.is_empty());
+ evaluation.returned_goals.extend(goals);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+ pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match (this, goal_evaluation.state.unwrap().tree) {
+ (
+ DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation {
+ evaluations, ..
+ }),
+ DebugSolver::GoalEvaluation(goal_evaluation),
+ ) => evaluations.last_mut().unwrap().push(goal_evaluation),
+ (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation,
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn new_goal_evaluation_step(
+ &mut self,
+ instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+ ) -> ProofTreeBuilder<'tcx> {
+ if self.state.is_none() {
+ return ProofTreeBuilder { state: None };
+ }
+
+ self.nested(WipGoalEvaluationStep {
+ instantiated_goal,
+ nested_goal_evaluations: vec![],
+ candidates: vec![],
+ result: None,
+ })
+ }
+ pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match (this, goal_eval_step.state.unwrap().tree) {
+ (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => {
+ goal_eval.evaluation_steps.push(step);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> {
+ if self.state.is_none() {
+ return ProofTreeBuilder { state: None };
+ }
+
+ self.nested(WipGoalCandidate {
+ nested_goal_evaluations: vec![],
+ candidates: vec![],
+ kind: None,
+ })
+ }
+
+ pub fn candidate_kind(&mut self, candidate_kind: CandidateKind<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalCandidate(this) => {
+ assert_eq!(this.kind.replace(candidate_kind), None)
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match (this, candidate.state.unwrap().tree) {
+ (
+ DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. })
+ | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }),
+ DebugSolver::GoalCandidate(candidate),
+ ) => candidates.push(candidate),
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
+ if self.state.is_none() {
+ return ProofTreeBuilder { state: None };
+ }
+
+ self.nested(WipAddedGoalsEvaluation { evaluations: vec![], result: None })
+ }
+
+ pub fn evaluate_added_goals_loop_start(&mut self) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::AddedGoalsEvaluation(this) => {
+ this.evaluations.push(vec![]);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn eval_added_goals_result(&mut self, result: Result<Certainty, NoSolution>) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::AddedGoalsEvaluation(this) => {
+ assert_eq!(this.result.replace(result), None);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match (this, goals_evaluation.state.unwrap().tree) {
+ (
+ DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+ nested_goal_evaluations,
+ ..
+ })
+ | DebugSolver::GoalCandidate(WipGoalCandidate {
+ nested_goal_evaluations, ..
+ }),
+ DebugSolver::AddedGoalsEvaluation(added_goals_evaluation),
+ ) => nested_goal_evaluations.push(added_goals_evaluation),
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn query_result(&mut self, result: QueryResult<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalEvaluation(goal_evaluation) => {
+ assert_eq!(goal_evaluation.result.replace(result), None);
+ }
+ DebugSolver::GoalEvaluationStep(evaluation_step) => {
+ assert_eq!(evaluation_step.result.replace(result), None);
+ }
+ DebugSolver::Root
+ | DebugSolver::AddedGoalsEvaluation(_)
+ | DebugSolver::GoalCandidate(_) => unreachable!(),
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 56a254d9c..77809d8d2 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -20,17 +20,24 @@ use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
};
+mod alias_relate;
mod assembly;
mod canonicalize;
mod eval_ctxt;
mod fulfill;
+pub mod inspect;
+mod normalize;
mod opaques;
mod project_goals;
mod search_graph;
mod trait_goals;
+mod weak_types;
-pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
+pub use eval_ctxt::{
+ EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
+};
pub use fulfill::FulfillmentCtxt;
+pub(crate) use normalize::deeply_normalize;
#[derive(Debug, Clone, Copy)]
enum SolverMode {
@@ -154,139 +161,40 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
}
}
- #[instrument(level = "debug", skip(self), ret)]
- fn compute_alias_relate_goal(
+ #[instrument(level = "debug", skip(self))]
+ fn compute_const_evaluatable_goal(
&mut self,
- goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
+ Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>,
) -> 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 (lhs, rhs, direction) = goal.predicate;
-
- if lhs.is_infer() || rhs.is_infer() {
- bug!(
- "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated"
- );
- }
-
- match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
- (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
-
- // 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)
- }
-
- (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,
- ));
- // RHS normalizes-to RHS
- candidates.extend(evaluate_normalizes_to(
- self,
- alias_rhs,
- lhs,
- direction,
- Invert::Yes,
- ));
- // Relate via substs
- let subst_relate_response = 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)
- });
- candidates.extend(subst_relate_response);
- debug!(?candidates);
-
- if let Some(merged) = self.try_merge_responses(&candidates) {
- Ok(merged)
+ match ct.kind() {
+ ty::ConstKind::Unevaluated(uv) => {
+ // We never return `NoSolution` here as `try_const_eval_resolve` emits an
+ // error itself when failing to evaluate, so emitting an additional fulfillment
+ // error in that case is unnecessary noise. This may change in the future once
+ // evaluation failures are allowed to impact selection, e.g. generic const
+ // expressions in impl headers or `where`-clauses.
+
+ // FIXME(generic_const_exprs): Implement handling for generic
+ // const expressions here.
+ if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv, ct.ty()) {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
- // When relating two aliases and we have ambiguity, we prefer
- // relating the generic arguments of the aliases over normalizing
- // them. This is necessary for inference during typeck.
- //
- // As this is incomplete, we must not do so during coherence.
- match (self.solver_mode(), subst_relate_response) {
- (SolverMode::Normal, Ok(response)) => Ok(response),
- (SolverMode::Normal, Err(NoSolution)) | (SolverMode::Coherence, _) => {
- self.flounder(&candidates)
- }
- }
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
+ ty::ConstKind::Infer(_) => {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+ }
+ ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+ // We can freely ICE here as:
+ // - `Param` gets replaced with a placeholder during canonicalization
+ // - `Bound` cannot exist as we don't have a binder around the self Type
+ // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet
+ ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => {
+ bug!("unexpect const kind: {:?}", ct)
+ }
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
new file mode 100644
index 000000000..c388850d8
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -0,0 +1,220 @@
+use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_infer::infer::at::At;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::traits::TraitEngineExt;
+use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine};
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::traits::Reveal;
+use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex};
+use rustc_middle::ty::{FallibleTypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
+
+use super::FulfillmentCtxt;
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
+ at: At<'_, 'tcx>,
+ value: T,
+) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+ let fulfill_cx = FulfillmentCtxt::new(at.infcx);
+ let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes: Vec::new() };
+
+ value.try_fold_with(&mut folder)
+}
+
+struct NormalizationFolder<'me, 'tcx> {
+ at: At<'me, 'tcx>,
+ fulfill_cx: FulfillmentCtxt<'tcx>,
+ depth: usize,
+ universes: Vec<Option<UniverseIndex>>,
+}
+
+impl<'tcx> NormalizationFolder<'_, 'tcx> {
+ fn normalize_alias_ty(
+ &mut self,
+ alias: AliasTy<'tcx>,
+ ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
+ let infcx = self.at.infcx;
+ let tcx = infcx.tcx;
+ let recursion_limit = tcx.recursion_limit();
+ if !recursion_limit.value_within_limit(self.depth) {
+ self.at.infcx.err_ctxt().report_overflow_error(
+ &alias.to_ty(tcx),
+ self.at.cause.span,
+ true,
+ |_| {},
+ );
+ }
+
+ self.depth += 1;
+
+ let new_infer_ty = infcx.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::NormalizeProjectionType,
+ span: self.at.cause.span,
+ });
+ let obligation = Obligation::new(
+ tcx,
+ self.at.cause.clone(),
+ self.at.param_env,
+ ty::Binder::dummy(ty::ProjectionPredicate {
+ projection_ty: alias,
+ term: new_infer_ty.into(),
+ }),
+ );
+
+ // Do not emit an error if normalization is known to fail but instead
+ // keep the projection unnormalized. This is the case for projections
+ // with a `T: Trait` where-clause and opaque types outside of the defining
+ // scope.
+ let result = if infcx.predicate_may_hold(&obligation) {
+ self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+ let errors = self.fulfill_cx.select_all_or_error(infcx);
+ if !errors.is_empty() {
+ return Err(errors);
+ }
+ let ty = infcx.resolve_vars_if_possible(new_infer_ty);
+ ty.try_fold_with(self)?
+ } else {
+ alias.to_ty(tcx).try_super_fold_with(self)?
+ };
+
+ self.depth -= 1;
+ Ok(result)
+ }
+
+ fn normalize_unevaluated_const(
+ &mut self,
+ ty: Ty<'tcx>,
+ uv: ty::UnevaluatedConst<'tcx>,
+ ) -> Result<ty::Const<'tcx>, Vec<FulfillmentError<'tcx>>> {
+ let infcx = self.at.infcx;
+ let tcx = infcx.tcx;
+ let recursion_limit = tcx.recursion_limit();
+ if !recursion_limit.value_within_limit(self.depth) {
+ self.at.infcx.err_ctxt().report_overflow_error(
+ &ty::Const::new_unevaluated(tcx, uv, ty),
+ self.at.cause.span,
+ true,
+ |_| {},
+ );
+ }
+
+ self.depth += 1;
+
+ let new_infer_ct = infcx.next_const_var(
+ ty,
+ ConstVariableOrigin {
+ kind: ConstVariableOriginKind::MiscVariable,
+ span: self.at.cause.span,
+ },
+ );
+ let obligation = Obligation::new(
+ tcx,
+ self.at.cause.clone(),
+ self.at.param_env,
+ ty::Binder::dummy(ty::ProjectionPredicate {
+ projection_ty: tcx.mk_alias_ty(uv.def, uv.substs),
+ term: new_infer_ct.into(),
+ }),
+ );
+
+ let result = if infcx.predicate_may_hold(&obligation) {
+ self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+ let errors = self.fulfill_cx.select_all_or_error(infcx);
+ if !errors.is_empty() {
+ return Err(errors);
+ }
+ let ct = infcx.resolve_vars_if_possible(new_infer_ct);
+ ct.try_fold_with(self)?
+ } else {
+ ty::Const::new_unevaluated(tcx, uv, ty).try_super_fold_with(self)?
+ };
+
+ self.depth -= 1;
+ Ok(result)
+ }
+}
+
+impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
+ type Error = Vec<FulfillmentError<'tcx>>;
+
+ fn interner(&self) -> TyCtxt<'tcx> {
+ self.at.infcx.tcx
+ }
+
+ fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+ &mut self,
+ t: ty::Binder<'tcx, T>,
+ ) -> Result<ty::Binder<'tcx, T>, Self::Error> {
+ self.universes.push(None);
+ let t = t.try_super_fold_with(self)?;
+ self.universes.pop();
+ Ok(t)
+ }
+
+ fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
+ let reveal = self.at.param_env.reveal();
+ let infcx = self.at.infcx;
+ debug_assert_eq!(ty, infcx.shallow_resolve(ty));
+ if !needs_normalization(&ty, reveal) {
+ return Ok(ty);
+ }
+
+ // We don't normalize opaque types unless we have
+ // `Reveal::All`, even if we're in the defining scope.
+ let data = match *ty.kind() {
+ ty::Alias(kind, alias_ty) if kind != ty::Opaque || reveal == Reveal::All => alias_ty,
+ _ => return ty.try_super_fold_with(self),
+ };
+
+ if data.has_escaping_bound_vars() {
+ let (data, mapped_regions, mapped_types, mapped_consts) =
+ BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+ let result = ensure_sufficient_stack(|| self.normalize_alias_ty(data))?;
+ Ok(PlaceholderReplacer::replace_placeholders(
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ &mut self.universes,
+ result,
+ ))
+ } else {
+ ensure_sufficient_stack(|| self.normalize_alias_ty(data))
+ }
+ }
+
+ fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
+ let reveal = self.at.param_env.reveal();
+ let infcx = self.at.infcx;
+ debug_assert_eq!(ct, infcx.shallow_resolve(ct));
+ if !needs_normalization(&ct, reveal) {
+ return Ok(ct);
+ }
+
+ let uv = match ct.kind() {
+ ty::ConstKind::Unevaluated(ct) => ct,
+ _ => return ct.try_super_fold_with(self),
+ };
+
+ if uv.has_escaping_bound_vars() {
+ let (uv, mapped_regions, mapped_types, mapped_consts) =
+ BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv);
+ let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))?;
+ Ok(PlaceholderReplacer::replace_placeholders(
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ &mut self.universes,
+ result,
+ ))
+ } else {
+ ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))
+ }
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs
index a5de4ddee..16194f5ad 100644
--- a/compiler/rustc_trait_selection/src/solve/opaques.rs
+++ b/compiler/rustc_trait_selection/src/solve/opaques.rs
@@ -20,8 +20,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else {
return Err(NoSolution);
};
- let opaque_ty =
- ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs };
// FIXME: at some point we should call queries without defining
// new opaque types but having the existing opaque type definitions.
// This will require moving this below "Prefer opaques registered already".
@@ -41,7 +39,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Ok(()) => {}
}
// Prefer opaques registered already.
- let matches = self.unify_existing_opaque_tys(goal.param_env, opaque_ty, expected);
+ let opaque_type_key =
+ ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs };
+ let matches =
+ self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected);
if !matches.is_empty() {
if let Some(response) = self.try_merge_responses(&matches) {
return Ok(response);
@@ -50,10 +51,24 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}
// Otherwise, define a new opaque type
- self.register_opaque_ty(opaque_ty, expected, goal.param_env)?;
+ self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
+ self.add_item_bounds_for_hidden_type(
+ opaque_ty.def_id,
+ opaque_ty.substs,
+ goal.param_env,
+ expected,
+ );
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
(Reveal::UserFacing, SolverMode::Coherence) => {
+ // An impossible opaque type bound is the only way this goal will fail
+ // e.g. assigning `impl Copy := NotCopy`
+ self.add_item_bounds_for_hidden_type(
+ opaque_ty.def_id,
+ opaque_ty.substs,
+ goal.param_env,
+ expected,
+ );
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
(Reveal::All, _) => {
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 7d7dfa2c8..e53b784a7 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -9,6 +9,7 @@ use rustc_hir::LangItem;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::specialization_graph::LeafDef;
use rustc_infer::traits::Reveal;
+use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::ProjectionPredicate;
@@ -22,25 +23,66 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
&mut self,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> {
- match goal.predicate.projection_ty.kind(self.tcx()) {
- ty::AliasKind::Projection => {
+ let def_id = goal.predicate.def_id();
+ match self.tcx().def_kind(def_id) {
+ DefKind::AssocTy | DefKind::AssocConst => {
// To only compute normalization once for each projection we only
- // normalize if the expected term is an unconstrained inference variable.
+ // assemble normalization candidates if the expected term is an
+ // unconstrained inference variable.
+ //
+ // Why: For better cache hits, since if we have an unconstrained RHS then
+ // there are only as many cache keys as there are (canonicalized) alias
+ // types in each normalizes-to goal. This also weakens inference in a
+ // forwards-compatible way so we don't use the value of the RHS term to
+ // affect candidate assembly for projections.
//
// E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
// `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
// `U` and equate it with `u32`. This means that we don't need a separate
- // projection cache in the solver.
+ // projection cache in the solver, since we're piggybacking off of regular
+ // goal caching.
if self.term_is_fully_unconstrained(goal) {
- let candidates = self.assemble_and_evaluate_candidates(goal);
- self.merge_candidates(candidates)
+ match self.tcx().associated_item(def_id).container {
+ ty::AssocItemContainer::TraitContainer => {
+ let candidates = self.assemble_and_evaluate_candidates(goal);
+ self.merge_candidates(candidates)
+ }
+ ty::AssocItemContainer::ImplContainer => {
+ bug!("IATs not supported here yet")
+ }
+ }
} else {
self.set_normalizes_to_hack_goal(goal);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
- ty::AliasKind::Opaque => self.normalize_opaque_type(goal),
- ty::AliasKind::Inherent => bug!("IATs not supported here yet"),
+ DefKind::AnonConst => self.normalize_anon_const(goal),
+ DefKind::OpaqueTy => self.normalize_opaque_type(goal),
+ DefKind::TyAlias => self.normalize_weak_type(goal),
+ kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
+ }
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ fn normalize_anon_const(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ if let Some(normalized_const) = self.try_const_eval_resolve(
+ goal.param_env,
+ ty::UnevaluatedConst::new(
+ goal.predicate.projection_ty.def_id,
+ goal.predicate.projection_ty.substs,
+ ),
+ self.tcx()
+ .type_of(goal.predicate.projection_ty.def_id)
+ .no_bound_vars()
+ .expect("const ty should not rely on other generics"),
+ ) {
+ self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ } else {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
}
@@ -65,24 +107,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
) -> QueryResult<'tcx> {
- if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
- && poly_projection_pred.projection_def_id() == goal.predicate.def_id()
- {
- ecx.probe(|ecx| {
- let assumption_projection_pred =
- ecx.instantiate_binder_with_infer(poly_projection_pred);
- ecx.eq(
- goal.param_env,
- goal.predicate.projection_ty,
- assumption_projection_pred.projection_ty,
- )?;
- ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
- .expect("expected goal term to be fully unconstrained");
- then(ecx)
- })
+ if let Some(projection_pred) = assumption.as_projection_clause() {
+ if projection_pred.projection_def_id() == goal.predicate.def_id() {
+ ecx.probe_candidate("assumption").enter(|ecx| {
+ let assumption_projection_pred =
+ ecx.instantiate_binder_with_infer(projection_pred);
+ ecx.eq(
+ goal.param_env,
+ goal.predicate.projection_ty,
+ assumption_projection_pred.projection_ty,
+ )?;
+ ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
+ .expect("expected goal term to be fully unconstrained");
+ then(ecx)
+ })
+ } else {
+ Err(NoSolution)
+ }
} else {
Err(NoSolution)
}
@@ -102,101 +146,100 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
return Err(NoSolution);
}
- ecx.probe(|ecx| {
- let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
- let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
-
- 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));
- 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,
- goal.param_env,
- goal_trait_ref,
- goal.predicate.def_id(),
- impl_def_id
- )? else {
- return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
- };
+ ecx.probe(
+ |r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter(
+ |ecx| {
+ let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
+ let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
+
+ 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));
+ 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,
+ goal.param_env,
+ goal_trait_ref,
+ goal.predicate.def_id(),
+ impl_def_id
+ )? else {
+ return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ };
- if !assoc_def.item.defaultness(tcx).has_value() {
- let guar = tcx.sess.delay_span_bug(
- tcx.def_span(assoc_def.item.def_id),
- "missing value for assoc item in impl",
- );
- let error_term = match assoc_def.item.kind {
- ty::AssocKind::Const => tcx
- .const_error(
+ if !assoc_def.item.defaultness(tcx).has_value() {
+ let guar = tcx.sess.delay_span_bug(
+ tcx.def_span(assoc_def.item.def_id),
+ "missing value for assoc item in impl",
+ );
+ let error_term = match assoc_def.item.kind {
+ ty::AssocKind::Const => ty::Const::new_error(tcx,
+ guar,
tcx.type_of(goal.predicate.def_id())
.subst(tcx, goal.predicate.projection_ty.substs),
- guar,
- )
- .into(),
- ty::AssocKind::Type => tcx.ty_error(guar).into(),
- ty::AssocKind::Fn => unreachable!(),
- };
- ecx.eq(goal.param_env, goal.predicate.term, error_term)
- .expect("expected goal term to be fully unconstrained");
- return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
- }
+ )
+ .into(),
+ ty::AssocKind::Type => Ty::new_error(tcx,guar).into(),
+ ty::AssocKind::Fn => unreachable!(),
+ };
+ ecx.eq(goal.param_env, goal.predicate.term, error_term)
+ .expect("expected goal term to be fully unconstrained");
+ return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+ }
- // Getting the right substitutions here is complex, e.g. given:
- // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
- // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
- // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
- //
- // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]`
- // to `[u32, u64]`.
- //
- // And then map these substs to the substs of the defining impl of `Assoc`, going
- // from `[u32, u64]` to `[u32, i32, u64]`.
- let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
- tcx,
- goal_trait_ref.def_id,
- impl_substs,
- );
- let substs = ecx.translate_substs(
- goal.param_env,
- impl_def_id,
- impl_substs_with_gat,
- assoc_def.defining_node,
- );
-
- // Finally we construct the actual value of the associated type.
- let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst);
- let ty = tcx.type_of(assoc_def.item.def_id);
- let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
- let identity_substs =
- ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id);
- let did = assoc_def.item.def_id;
- let kind =
- ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs));
- ty.map_bound(|ty| tcx.mk_const(kind, ty).into())
- } else {
- ty.map_bound(|ty| ty.into())
- };
+ // Getting the right substitutions here is complex, e.g. given:
+ // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
+ // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
+ // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
+ //
+ // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]`
+ // to `[u32, u64]`.
+ //
+ // And then map these substs to the substs of the defining impl of `Assoc`, going
+ // from `[u32, u64]` to `[u32, i32, u64]`.
+ let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
+ tcx,
+ goal_trait_ref.def_id,
+ impl_substs,
+ );
+ let substs = ecx.translate_substs(
+ goal.param_env,
+ impl_def_id,
+ impl_substs_with_gat,
+ assoc_def.defining_node,
+ );
- ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
- .expect("expected goal term to be fully unconstrained");
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- })
+ // Finally we construct the actual value of the associated type.
+ let term = match assoc_def.item.kind {
+ ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()),
+ ty::AssocKind::Const => bug!("associated const projection is not supported yet"),
+ ty::AssocKind::Fn => unreachable!("we should never project to a fn"),
+ };
+
+ ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
+ .expect("expected goal term to be fully unconstrained");
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ },
+ )
}
fn consider_auto_trait_candidate(
- _ecx: &mut EvalCtxt<'_, 'tcx>,
+ ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
- bug!("auto traits do not have associated types: {:?}", goal);
+ ecx.tcx().sess.delay_span_bug(
+ ecx.tcx().def_span(goal.predicate.def_id()),
+ "associated types not allowed on auto traits",
+ );
+ Err(NoSolution)
}
fn consider_trait_alias_candidate(
@@ -280,7 +323,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
let tcx = ecx.tcx();
- ecx.probe(|ecx| {
+ ecx.probe_candidate("builtin pointee").enter(|ecx| {
let metadata_ty = match goal.predicate.self_ty().kind() {
ty::Bool
| ty::Char
@@ -300,7 +343,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
| ty::Never
| ty::Foreign(..) => tcx.types.unit,
- ty::Error(e) => tcx.ty_error(*e),
+ ty::Error(e) => Ty::new_error(tcx, *e),
ty::Str | ty::Slice(_) => tcx.types.usize,
@@ -323,7 +366,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
}
ty::Adt(def, substs) if def.is_struct() => {
- match def.non_enum_variant().fields.raw.last() {
+ match def.non_enum_variant().tail_opt() {
None => tcx.types.unit,
Some(field_def) => {
let self_ty = field_def.ty(tcx, substs);
@@ -497,7 +540,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
),
};
- ecx.probe(|ecx| {
+ ecx.probe_candidate("builtin discriminant kind").enter(|ecx| {
ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
.expect("expected goal term to be fully unconstrained");
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
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 19e4b2300..f00456e26 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -2,6 +2,7 @@ mod cache;
mod overflow;
pub(super) use overflow::OverflowHandler;
+use rustc_middle::traits::solve::inspect::CacheHit;
use self::cache::ProvisionalEntry;
use cache::ProvisionalCache;
@@ -12,6 +13,7 @@ use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryRe
use rustc_middle::ty::TyCtxt;
use std::{collections::hash_map::Entry, mem};
+use super::inspect::ProofTreeBuilder;
use super::SolverMode;
rustc_index::newtype_index! {
@@ -88,11 +90,12 @@ impl<'tcx> SearchGraph<'tcx> {
/// Tries putting the new goal on the stack, returning an error if it is already cached.
///
/// This correctly updates the provisional cache if there is a cycle.
- #[instrument(level = "debug", skip(self, tcx), ret)]
+ #[instrument(level = "debug", skip(self, tcx, inspect), ret)]
fn try_push_stack(
&mut self,
tcx: TyCtxt<'tcx>,
input: CanonicalInput<'tcx>,
+ inspect: &mut ProofTreeBuilder<'tcx>,
) -> Result<(), QueryResult<'tcx>> {
// Look at the provisional cache to check for cycles.
let cache = &mut self.provisional_cache;
@@ -119,6 +122,8 @@ impl<'tcx> SearchGraph<'tcx> {
// Finally we can return either the provisional response for that goal if we have a
// coinductive cycle or an ambiguous result if the cycle is inductive.
Entry::Occupied(entry_index) => {
+ inspect.cache_hit(CacheHit::Provisional);
+
let entry_index = *entry_index.get();
let stack_depth = cache.depth(entry_index);
@@ -205,16 +210,18 @@ impl<'tcx> SearchGraph<'tcx> {
&mut self,
tcx: TyCtxt<'tcx>,
canonical_input: CanonicalInput<'tcx>,
- mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
+ inspect: &mut ProofTreeBuilder<'tcx>,
+ mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
) -> QueryResult<'tcx> {
- if self.should_use_global_cache() {
+ if self.should_use_global_cache() && inspect.use_global_cache() {
if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
debug!(?canonical_input, ?result, "cache hit");
+ inspect.cache_hit(CacheHit::Global);
return result;
}
}
- match self.try_push_stack(tcx, canonical_input) {
+ match self.try_push_stack(tcx, canonical_input, inspect) {
Ok(()) => {}
// Our goal is already on the stack, eager return.
Err(response) => return response,
@@ -231,7 +238,7 @@ impl<'tcx> SearchGraph<'tcx> {
result
},
|this| {
- let result = loop_body(this);
+ let result = loop_body(this, inspect);
this.try_finalize_goal(canonical_input, result).then(|| result)
},
)
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index f722f2813..ef5f25b1f 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -7,6 +7,7 @@ use rustc_hir::{LangItem, Movability};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::supertraits;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
+use rustc_middle::traits::Reveal;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
@@ -61,7 +62,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
},
};
- ecx.probe(|ecx| {
+ ecx.probe_candidate("impl").enter(|ecx| {
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
@@ -81,24 +82,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
) -> QueryResult<'tcx> {
- if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
- && poly_trait_pred.def_id() == goal.predicate.def_id()
- && poly_trait_pred.polarity() == goal.predicate.polarity
- {
- // FIXME: Constness
- ecx.probe(|ecx| {
- let assumption_trait_pred =
- ecx.instantiate_binder_with_infer(poly_trait_pred);
- ecx.eq(
- goal.param_env,
- goal.predicate.trait_ref,
- assumption_trait_pred.trait_ref,
- )?;
- then(ecx)
- })
+ if let Some(trait_clause) = assumption.as_trait_clause() {
+ if trait_clause.def_id() == goal.predicate.def_id()
+ && trait_clause.polarity() == goal.predicate.polarity
+ {
+ // FIXME: Constness
+ ecx.probe_candidate("assumption").enter(|ecx| {
+ let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
+ ecx.eq(
+ goal.param_env,
+ goal.predicate.trait_ref,
+ assumption_trait_pred.trait_ref,
+ )?;
+ then(ecx)
+ })
+ } else {
+ Err(NoSolution)
+ }
} else {
Err(NoSolution)
}
@@ -116,6 +119,32 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
return result;
}
+ // Don't call `type_of` on a local TAIT that's in the defining scope,
+ // since that may require calling `typeck` on the same item we're
+ // currently type checking, which will result in a fatal cycle that
+ // ideally we want to avoid, since we can make progress on this goal
+ // via an alias bound or a locally-inferred hidden type instead.
+ //
+ // Also, don't call `type_of` on a TAIT in `Reveal::All` mode, since
+ // we already normalize the self type in
+ // `assemble_candidates_after_normalizing_self_ty`, and we'd
+ // just be registering an identical candidate here.
+ //
+ // Returning `Err(NoSolution)` here is ok in `SolverMode::Coherence`
+ // since we'll always be registering an ambiguous candidate in
+ // `assemble_candidates_after_normalizing_self_ty` due to normalizing
+ // the TAIT.
+ if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
+ if matches!(goal.param_env.reveal(), Reveal::All)
+ || opaque_ty
+ .def_id
+ .as_local()
+ .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id))
+ {
+ return Err(NoSolution);
+ }
+ }
+
ecx.probe_and_evaluate_goal_for_constituent_tys(
goal,
structural_traits::instantiate_constituent_tys_for_auto_trait,
@@ -132,7 +161,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let tcx = ecx.tcx();
- ecx.probe(|ecx| {
+ ecx.probe_candidate("trait alias").enter(|ecx| {
let nested_obligations = tcx
.predicates_of(goal.predicate.def_id())
.instantiate(tcx, goal.predicate.trait_ref.substs);
@@ -344,7 +373,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
if b_ty.is_ty_var() {
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}
- ecx.probe(|ecx| {
+ ecx.probe_candidate("builtin unsize").enter(|ecx| {
match (a_ty.kind(), b_ty.kind()) {
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
@@ -396,12 +425,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
return Err(NoSolution);
}
- let tail_field = a_def
- .non_enum_variant()
- .fields
- .raw
- .last()
- .expect("expected unsized ADT to have a tail field");
+ let tail_field = a_def.non_enum_variant().tail();
let tail_field_ty = tcx.type_of(tail_field.did);
let a_tail_ty = tail_field_ty.subst(tcx, a_substs);
@@ -414,7 +438,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
tcx.mk_substs_from_iter(a_substs.iter().enumerate().map(|(i, a)| {
if unsizing_params.contains(i as u32) { b_substs[i] } else { a }
}));
- let unsized_a_ty = tcx.mk_adt(a_def, new_a_substs);
+ let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_substs);
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
// types.
@@ -434,7 +458,7 @@ 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());
+ Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
// Similar to ADTs, require that the rest of the fields are equal.
@@ -476,7 +500,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
}
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
- ecx.probe(|ecx| -> Result<_, NoSolution> {
+ ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> {
// Require that all of the trait predicates from A match B, except for
// the auto traits. We do this by constructing a new A type with B's
// auto traits, and equating these types.
@@ -493,7 +517,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
.map(ty::Binder::dummy),
);
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
- let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn);
+ let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
// We also require that A's lifetime outlives B's lifetime.
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
@@ -618,7 +642,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
- | ty::Alias(ty::Projection | ty::Inherent, ..)
+ | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..)
| ty::Placeholder(..) => Some(Err(NoSolution)),
ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
@@ -698,7 +722,7 @@ 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(|ecx| {
+ self.probe_candidate("constituent tys").enter(|ecx| {
ecx.add_goals(
constituent_tys(ecx, goal.predicate.self_ty())?
.into_iter()
diff --git a/compiler/rustc_trait_selection/src/solve/weak_types.rs b/compiler/rustc_trait_selection/src/solve/weak_types.rs
new file mode 100644
index 000000000..b095b54c5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/weak_types.rs
@@ -0,0 +1,19 @@
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+use super::EvalCtxt;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ pub(super) fn normalize_weak_type(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let weak_ty = goal.predicate.projection_ty;
+ let expected = goal.predicate.term.ty().expect("no such thing as a const alias");
+
+ let actual = tcx.type_of(weak_ty.def_id).subst(tcx, weak_ty.substs);
+ self.eq(goal.param_env, expected, actual)?;
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 62d2aad52..cb38d0ac8 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -95,7 +95,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
tcx,
ObligationCause::dummy(),
orig_env,
- ty::Binder::dummy(ty::TraitPredicate {
+ ty::TraitPredicate {
trait_ref,
constness: ty::BoundConstness::NotConst,
polarity: if polarity {
@@ -103,7 +103,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
} else {
ImplPolarity::Negative
},
- }),
+ },
));
if let Ok(Some(ImplSource::UserDefined(_))) = result {
debug!(
@@ -255,7 +255,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
// that are already in the `ParamEnv` (modulo regions): we already
// know that they must hold.
for predicate in param_env.caller_bounds() {
- fresh_preds.insert(self.clean_pred(infcx, predicate));
+ fresh_preds.insert(self.clean_pred(infcx, predicate.as_predicate()));
}
let mut select = SelectionContext::new(&infcx);
@@ -270,8 +270,9 @@ impl<'tcx> AutoTraitFinder<'tcx> {
polarity: ty::ImplPolarity::Positive,
}));
- let computed_preds = param_env.caller_bounds().iter();
- let mut user_computed_preds: FxIndexSet<_> = user_env.caller_bounds().iter().collect();
+ let computed_preds = param_env.caller_bounds().iter().map(|c| c.as_predicate());
+ let mut user_computed_preds: FxIndexSet<_> =
+ user_env.caller_bounds().iter().map(|c| c.as_predicate()).collect();
let mut new_env = param_env;
let dummy_cause = ObligationCause::dummy();
@@ -291,7 +292,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
new_env,
pred,
));
- let result = select.select(&obligation);
+ let result = select.poly_select(&obligation);
match result {
Ok(Some(ref impl_source)) => {
@@ -349,14 +350,14 @@ impl<'tcx> AutoTraitFinder<'tcx> {
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),
+ tcx.mk_clauses_from_iter(normalized_preds.filter_map(|p| p.as_clause())),
param_env.reveal(),
param_env.constness(),
);
}
let final_user_env = ty::ParamEnv::new(
- tcx.mk_predicates_from_iter(user_computed_preds.into_iter()),
+ tcx.mk_clauses_from_iter(user_computed_preds.into_iter().filter_map(|p| p.as_clause())),
user_env.reveal(),
user_env.constness(),
);
@@ -400,8 +401,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
let mut should_add_new = true;
user_computed_preds.retain(|&old_pred| {
if let (
- ty::PredicateKind::Clause(ty::Clause::Trait(new_trait)),
- ty::PredicateKind::Clause(ty::Clause::Trait(old_trait)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(new_trait)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(old_trait)),
) = (new_pred.kind().skip_binder(), old_pred.kind().skip_binder())
{
if new_trait.def_id() == old_trait.def_id() {
@@ -621,14 +622,14 @@ impl<'tcx> AutoTraitFinder<'tcx> {
let bound_predicate = predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(p)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => {
// Add this to `predicates` so that we end up calling `select`
// with it. If this predicate ends up being unimplemented,
// then `evaluate_predicates` will handle adding it the `ParamEnv`
// if possible.
predicates.push_back(bound_predicate.rebind(p));
}
- ty::PredicateKind::Clause(ty::Clause::Projection(p)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
let p = bound_predicate.rebind(p);
debug!(
"evaluate_nested_obligations: examining projection predicate {:?}",
@@ -758,11 +759,11 @@ impl<'tcx> AutoTraitFinder<'tcx> {
}
}
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(binder)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(binder)) => {
let binder = bound_predicate.rebind(binder);
selcx.infcx.region_outlives_predicate(&dummy_cause, binder)
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(binder)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(binder)) => {
let binder = bound_predicate.rebind(binder);
match (
binder.no_bound_vars(),
@@ -793,7 +794,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
unevaluated,
Some(obligation.cause.span),
) {
- Ok(Some(valtree)) => Ok(selcx.tcx().mk_const(valtree, c.ty())),
+ Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())),
Ok(None) => {
let tcx = self.tcx;
let reported =
@@ -826,18 +827,15 @@ impl<'tcx> AutoTraitFinder<'tcx> {
// we start out with a `ParamEnv` with no inference variables,
// and these don't correspond to adding any new bounds to
// the `ParamEnv`.
- ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::Coerce(..) => {}
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("predicate should only exist in the environment: {bound_predicate:?}")
- }
ty::PredicateKind::Ambiguous => return false,
};
}
diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
deleted file mode 100644
index 28967e1cc..000000000
--- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-//! Defines a Chalk-based `TraitEngine`
-
-use crate::infer::canonical::OriginalQueryValues;
-use crate::infer::InferCtxt;
-use crate::traits::query::NoSolution;
-use crate::traits::{
- ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
- SelectionError, TraitEngine,
-};
-use rustc_data_structures::fx::FxIndexSet;
-use rustc_middle::ty::TypeVisitableExt;
-
-pub struct FulfillmentContext<'tcx> {
- obligations: FxIndexSet<PredicateObligation<'tcx>>,
-
- usable_in_snapshot: bool,
-}
-
-impl FulfillmentContext<'_> {
- pub(super) fn new() -> Self {
- FulfillmentContext { obligations: FxIndexSet::default(), usable_in_snapshot: false }
- }
-
- pub(crate) fn new_in_snapshot() -> Self {
- FulfillmentContext { usable_in_snapshot: true, ..Self::new() }
- }
-}
-
-impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
- fn register_predicate_obligation(
- &mut self,
- infcx: &InferCtxt<'tcx>,
- obligation: PredicateObligation<'tcx>,
- ) {
- if !self.usable_in_snapshot {
- assert!(!infcx.is_in_snapshot());
- }
- let obligation = infcx.resolve_vars_if_possible(obligation);
-
- self.obligations.insert(obligation);
- }
-
- 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 { 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(),
- })
- .collect()
- }
-
- fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
- if !self.usable_in_snapshot {
- assert!(!infcx.is_in_snapshot());
- }
-
- let mut errors = Vec::new();
- let mut next_round = FxIndexSet::default();
- let mut making_progress;
-
- loop {
- making_progress = false;
-
- // We iterate over all obligations, and record if we are able
- // to unambiguously prove at least one obligation.
- for obligation in self.obligations.drain(..) {
- let obligation = infcx.resolve_vars_if_possible(obligation);
- let environment = obligation.param_env.caller_bounds();
- let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate };
- let mut orig_values = OriginalQueryValues::default();
- if goal.references_error() {
- continue;
- }
-
- let canonical_goal =
- infcx.canonicalize_query_preserving_universes(goal, &mut orig_values);
-
- match infcx.tcx.evaluate_goal(canonical_goal) {
- Ok(response) => {
- if response.is_proven() {
- making_progress = true;
-
- match infcx.instantiate_query_response_and_region_obligations(
- &obligation.cause,
- obligation.param_env,
- &orig_values,
- &response,
- ) {
- Ok(infer_ok) => next_round.extend(
- infer_ok.obligations.into_iter().map(|obligation| {
- assert!(!infcx.is_in_snapshot());
- infcx.resolve_vars_if_possible(obligation)
- }),
- ),
-
- Err(_err) => errors.push(FulfillmentError {
- obligation: obligation.clone(),
- code: FulfillmentErrorCode::CodeSelectionError(
- SelectionError::Unimplemented,
- ),
- // 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,
- }),
- }
- } else {
- // Ambiguous: retry at next round.
- next_round.insert(obligation);
- }
- }
-
- Err(NoSolution) => errors.push(FulfillmentError {
- obligation: obligation.clone(),
- code: FulfillmentErrorCode::CodeSelectionError(
- SelectionError::Unimplemented,
- ),
- // 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,
- }),
- }
- }
- next_round = std::mem::replace(&mut self.obligations, next_round);
-
- if !making_progress {
- break;
- }
- }
-
- errors
- }
-
- fn drain_unstalled_obligations(
- &mut self,
- _: &InferCtxt<'tcx>,
- ) -> Vec<PredicateObligation<'tcx>> {
- unimplemented!()
- }
-
- fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
- self.obligations.iter().cloned().collect()
- }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index e8c5a8fab..1b1285e1b 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -5,7 +5,7 @@
//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
use crate::infer::outlives::env::OutlivesEnvironment;
-use crate::infer::{CombinedSnapshot, InferOk};
+use crate::infer::InferOk;
use crate::traits::outlives_bounds::InferCtxtExt as _;
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::util::impl_subject_and_oblig;
@@ -23,13 +23,14 @@ use rustc_middle::traits::specialization_graph::OverlapMode;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
-use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
use rustc_span::symbol::sym;
use rustc_span::DUMMY_SP;
use std::fmt::Debug;
use std::iter;
use std::ops::ControlFlow;
+use super::query::evaluate_obligation::InferCtxtExt;
use super::NormalizeExt;
/// Whether we do the orphan check relative to this crate or
@@ -62,6 +63,21 @@ pub fn add_placeholder_note(err: &mut Diagnostic) {
);
}
+#[derive(Debug, Clone, Copy)]
+enum TrackAmbiguityCauses {
+ Yes,
+ No,
+}
+
+impl TrackAmbiguityCauses {
+ fn is_yes(self) -> bool {
+ match self {
+ TrackAmbiguityCauses::Yes => true,
+ TrackAmbiguityCauses::No => false,
+ }
+ }
+}
+
/// If there are types that satisfy both impls, returns `Some`
/// with a suitably-freshened `ImplHeader` with those types
/// substituted. Otherwise, returns `None`.
@@ -97,29 +113,28 @@ pub fn overlapping_impls(
return None;
}
- 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();
- if !overlaps {
- return None;
- }
+ let _overlap_with_bad_diagnostics = overlap(
+ tcx,
+ TrackAmbiguityCauses::No,
+ skip_leak_check,
+ impl1_def_id,
+ impl2_def_id,
+ overlap_mode,
+ )?;
// 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(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())
+ let overlap = overlap(
+ tcx,
+ TrackAmbiguityCauses::Yes,
+ skip_leak_check,
+ impl1_def_id,
+ impl2_def_id,
+ overlap_mode,
+ )
+ .unwrap();
+ Some(overlap)
}
fn with_fresh_ty_vars<'cx, 'tcx>(
@@ -134,7 +149,12 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
impl_def_id,
self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs),
trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)),
- predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates,
+ predicates: tcx
+ .predicates_of(impl_def_id)
+ .instantiate(tcx, impl_substs)
+ .iter()
+ .map(|(c, _)| c.as_predicate())
+ .collect(),
};
let InferOk { value: mut header, obligations } =
@@ -146,40 +166,35 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
/// Can both impl `a` and impl `b` be satisfied by a common type (including
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
-fn overlap<'cx, 'tcx>(
- selcx: &mut SelectionContext<'cx, 'tcx>,
+#[instrument(level = "debug", skip(tcx))]
+fn overlap<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ track_ambiguity_causes: TrackAmbiguityCauses,
skip_leak_check: SkipLeakCheck,
impl1_def_id: DefId,
impl2_def_id: DefId,
overlap_mode: OverlapMode,
) -> Option<OverlapResult<'tcx>> {
- debug!(
- "overlap(impl1_def_id={:?}, impl2_def_id={:?}, overlap_mode={:?})",
- impl1_def_id, impl2_def_id, overlap_mode
- );
-
- selcx.infcx.probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
- overlap_within_probe(selcx, impl1_def_id, impl2_def_id, overlap_mode, snapshot)
- })
-}
-
-fn overlap_within_probe<'cx, 'tcx>(
- selcx: &mut SelectionContext<'cx, 'tcx>,
- impl1_def_id: DefId,
- impl2_def_id: DefId,
- overlap_mode: OverlapMode,
- snapshot: &CombinedSnapshot<'tcx>,
-) -> Option<OverlapResult<'tcx>> {
- let infcx = selcx.infcx;
-
if overlap_mode.use_negative_impl() {
- if negative_impl(infcx.tcx, impl1_def_id, impl2_def_id)
- || negative_impl(infcx.tcx, impl2_def_id, impl1_def_id)
+ if impl_intersection_has_negative_obligation(tcx, impl1_def_id, impl2_def_id)
+ || impl_intersection_has_negative_obligation(tcx, impl2_def_id, impl1_def_id)
{
return None;
}
}
+ let infcx = tcx
+ .infer_ctxt()
+ .with_opaque_type_inference(DefiningAnchor::Bubble)
+ .skip_leak_check(skip_leak_check.is_yes())
+ .intercrate(true)
+ .with_next_trait_solver(tcx.next_trait_solver_in_coherence())
+ .build();
+ let selcx = &mut SelectionContext::new(&infcx);
+ if track_ambiguity_causes.is_yes() {
+ selcx.enable_tracking_intercrate_ambiguity_causes();
+ }
+
// For the purposes of this check, we don't bring any placeholder
// types into scope; instead, we replace the generic types with
// fresh type variables, and hence we do our evaluations in an
@@ -189,27 +204,40 @@ 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.infcx, &impl1_header, &impl2_header)?;
+ // Equate the headers to find their intersection (the general type, with infer vars,
+ // that may apply both impls).
+ let equate_obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?;
debug!("overlap: unification check succeeded");
- if overlap_mode.use_implicit_negative() {
- if implicit_negative(selcx, param_env, &impl1_header, impl2_header, obligations) {
- return None;
- }
+ if overlap_mode.use_implicit_negative()
+ && impl_intersection_has_impossible_obligation(
+ selcx,
+ param_env,
+ &impl1_header,
+ impl2_header,
+ equate_obligations,
+ )
+ {
+ return None;
}
- // We disable the leak when creating the `snapshot` by using
- // `infcx.probe_maybe_disable_leak_check`.
- if infcx.leak_check(true, snapshot).is_err() {
+ // We toggle the `leak_check` by using `skip_leak_check` when constructing the
+ // inference context, so this may be a noop.
+ if infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() {
debug!("overlap: leak check failed");
return None;
}
let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
-
- let involves_placeholder =
- matches!(selcx.infcx.region_constraints_added_in_snapshot(snapshot), Some(true));
+ let involves_placeholder = infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .data()
+ .constraints
+ .iter()
+ .any(|c| c.0.involves_placeholders());
let impl_header = selcx.infcx.resolve_vars_if_possible(impl1_header);
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
@@ -236,55 +264,55 @@ fn equate_impl_headers<'tcx>(
result.map(|infer_ok| infer_ok.obligations).ok()
}
-/// Given impl1 and impl2 check if both impls can be satisfied by a common type (including
-/// where-clauses) If so, return false, otherwise return true, they are disjoint.
-fn implicit_negative<'cx, 'tcx>(
+/// Check if both impls can be satisfied by a common type by considering whether
+/// any of either impl's obligations is not known to hold.
+///
+/// For example, given these two impls:
+/// `impl From<MyLocalType> for Box<dyn Error>` (in my crate)
+/// `impl<E> From<E> for Box<dyn Error> where E: Error` (in libstd)
+///
+/// After replacing both impl headers with inference vars (which happens before
+/// this function is called), we get:
+/// `Box<dyn Error>: From<MyLocalType>`
+/// `Box<dyn Error>: From<?E>`
+///
+/// This gives us `?E = MyLocalType`. We then certainly know that `MyLocalType: Error`
+/// never holds in intercrate mode since a local impl does not exist, and a
+/// downstream impl cannot be added -- therefore can consider the intersection
+/// of the two impls above to be empty.
+///
+/// Importantly, this works even if there isn't a `impl !Error for MyLocalType`.
+fn impl_intersection_has_impossible_obligation<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
impl1_header: &ty::ImplHeader<'tcx>,
impl2_header: ty::ImplHeader<'tcx>,
obligations: PredicateObligations<'tcx>,
) -> bool {
- // There's no overlap if obligations are unsatisfiable or if the obligation negated is
- // satisfied.
- //
- // For example, given these two impl headers:
- //
- // `impl<'a> From<&'a str> for Box<dyn Error>`
- // `impl<E> From<E> for Box<dyn Error> where E: Error`
- //
- // So we have:
- //
- // `Box<dyn Error>: From<&'?a str>`
- // `Box<dyn Error>: From<?E>`
- //
- // After equating the two headers:
- //
- // `Box<dyn Error> = Box<dyn Error>`
- // So, `?E = &'?a str` and then given the where clause `&'?a str: Error`.
- //
- // If the obligation `&'?a str: Error` holds, it means that there's overlap. If that doesn't
- // hold we need to check if `&'?a str: !Error` holds, if doesn't hold there's overlap because
- // at some point an impl for `&'?a str: Error` could be added.
- debug!(
- "implicit_negative(impl1_header={:?}, impl2_header={:?}, obligations={:?})",
- impl1_header, impl2_header, obligations
- );
let infcx = selcx.infcx;
- let opt_failing_obligation = impl1_header
- .predicates
- .iter()
- .copied()
- .chain(impl2_header.predicates)
- .map(|p| infcx.resolve_vars_if_possible(p))
- .map(|p| Obligation {
- cause: ObligationCause::dummy(),
- param_env,
- recursion_depth: 0,
- predicate: p,
+
+ let obligation_guaranteed_to_fail = move |obligation: &PredicateObligation<'tcx>| {
+ if infcx.next_trait_solver() {
+ infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply())
+ } else {
+ // We use `evaluate_root_obligation` to correctly track
+ // intercrate ambiguity clauses. We do not need this in the
+ // new solver.
+ selcx.evaluate_root_obligation(obligation).map_or(
+ false, // Overflow has occurred, and treat the obligation as possibly holding.
+ |result| !result.may_apply(),
+ )
+ }
+ };
+
+ let opt_failing_obligation = [&impl1_header.predicates, &impl2_header.predicates]
+ .into_iter()
+ .flatten()
+ .map(|&predicate| {
+ Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate)
})
.chain(obligations)
- .find(|o| !selcx.predicate_may_hold_fatal(o));
+ .find(obligation_guaranteed_to_fail);
if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
@@ -294,9 +322,27 @@ fn implicit_negative<'cx, 'tcx>(
}
}
-/// Given impl1 and impl2 check if both impls are never satisfied by a common type (including
-/// where-clauses) If so, return true, they are disjoint and false otherwise.
-fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> bool {
+/// Check if both impls can be satisfied by a common type by considering whether
+/// any of first impl's obligations is known not to hold *via a negative predicate*.
+///
+/// For example, given these two impls:
+/// `struct MyCustomBox<T: ?Sized>(Box<T>);`
+/// `impl From<&str> for MyCustomBox<dyn Error>` (in my crate)
+/// `impl<E> From<E> for MyCustomBox<dyn Error> where E: Error` (in my crate)
+///
+/// After replacing the second impl's header with inference vars, we get:
+/// `MyCustomBox<dyn Error>: From<&str>`
+/// `MyCustomBox<dyn Error>: From<?E>`
+///
+/// This gives us `?E = &str`. We then try to prove the first impl's predicates
+/// after negating, giving us `&str: !Error`. This is a negative impl provided by
+/// libstd, and therefore we can guarantee for certain that libstd will never add
+/// a positive impl for `&str: Error` (without it being a breaking change).
+fn impl_intersection_has_negative_obligation(
+ tcx: TyCtxt<'_>,
+ impl1_def_id: DefId,
+ impl2_def_id: DefId,
+) -> bool {
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
// Create an infcx, taking the predicates of impl1 as assumptions:
@@ -322,57 +368,45 @@ fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> b
// Attempt to prove that impl2 applies, given all of the above.
let selcx = &mut SelectionContext::new(&infcx);
let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
- let (subject2, obligations) =
+ let (subject2, normalization_obligations) =
impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs, |_, _| {
ObligationCause::dummy()
});
- !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id)
-}
-
-fn equate<'tcx>(
- infcx: &InferCtxt<'tcx>,
- impl_env: ty::ParamEnv<'tcx>,
- subject1: ImplSubject<'tcx>,
- subject2: ImplSubject<'tcx>,
- obligations: impl Iterator<Item = PredicateObligation<'tcx>>,
- body_def_id: DefId,
-) -> bool {
- // do the impls unify? If not, not disjoint.
- let Ok(InferOk { obligations: more_obligations, .. }) =
+ // do the impls unify? If not, then it's not currently possible to prove any
+ // obligations about their intersection.
+ let Ok(InferOk { obligations: equate_obligations, .. }) =
infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No,subject1, subject2)
else {
debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2);
- return true;
+ return false;
};
- let opt_failing_obligation = obligations
- .into_iter()
- .chain(more_obligations)
- .find(|o| negative_impl_exists(infcx, o, body_def_id));
-
- if let Some(failing_obligation) = opt_failing_obligation {
- debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
- false
- } else {
- true
+ for obligation in normalization_obligations.into_iter().chain(equate_obligations) {
+ if negative_impl_exists(&infcx, &obligation, impl1_def_id) {
+ debug!("overlap: obligation unsatisfiable {:?}", obligation);
+ return true;
+ }
}
+
+ false
}
-/// Try to prove that a negative impl exist for the given obligation and its super predicates.
+/// Try to prove that a negative impl exist for the obligation or its supertraits.
+///
+/// If such a negative impl exists, then the obligation definitely must not hold
+/// due to coherence, even if it's not necessarily "knowable" in this crate. Any
+/// valid impl downstream would not be able to exist due to the overlapping
+/// negative impl.
#[instrument(level = "debug", skip(infcx))]
fn negative_impl_exists<'tcx>(
infcx: &InferCtxt<'tcx>,
o: &PredicateObligation<'tcx>,
body_def_id: DefId,
) -> bool {
- if resolve_negative_obligation(infcx.fork(), o, body_def_id) {
- return true;
- }
-
// Try to prove a negative obligation exists for super predicates
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) {
+ if prove_negated_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) {
return true;
}
}
@@ -381,7 +415,7 @@ fn negative_impl_exists<'tcx>(
}
#[instrument(level = "debug", skip(infcx))]
-fn resolve_negative_obligation<'tcx>(
+fn prove_negated_obligation<'tcx>(
infcx: InferCtxt<'tcx>,
o: &PredicateObligation<'tcx>,
body_def_id: DefId,
@@ -403,7 +437,11 @@ fn resolve_negative_obligation<'tcx>(
let body_def_id = body_def_id.as_local().unwrap_or(CRATE_DEF_ID);
let ocx = ObligationCtxt::new(&infcx);
- let wf_tys = ocx.assumed_wf_types(param_env, DUMMY_SP, body_def_id);
+ let Ok(wf_tys) = ocx.assumed_wf_types(param_env, body_def_id)
+ else {
+ return false;
+ };
+
let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
infcx.implied_bounds_tys(param_env, body_def_id, wf_tys),
@@ -676,7 +714,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> {
| ty::RawPtr(..)
| ty::Never
| ty::Tuple(..)
- | ty::Alias(ty::Projection | ty::Inherent, ..) => self.found_non_local_ty(ty),
+ | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => {
+ self.found_non_local_ty(ty)
+ }
ty::Param(..) => self.found_param_ty(ty),
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index bd1ea43a7..8dc13b827 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -176,7 +176,7 @@ fn satisfied_from_param_env<'tcx>(
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!("is_const_evaluatable: candidate={:?}", c);
if self.infcx.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
+ let ocx = ObligationCtxt::new(self.infcx);
ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()).is_ok()
&& ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok()
&& ocx.select_all_or_error().is_empty()
@@ -207,7 +207,7 @@ fn satisfied_from_param_env<'tcx>(
for pred in param_env.caller_bounds() {
match pred.kind().skip_binder() {
- ty::PredicateKind::ConstEvaluatable(ce) => {
+ ty::ClauseKind::ConstEvaluatable(ce) => {
let b_ct = tcx.expand_abstract_consts(ce);
let mut v = Visitor { ct, infcx, param_env, single_match };
let _ = b_ct.visit_with(&mut v);
@@ -219,7 +219,7 @@ fn satisfied_from_param_env<'tcx>(
}
if let Some(Ok(c)) = single_match {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok());
assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok());
assert!(ocx.select_all_or_error().is_empty());
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index 2c5ffd664..61f693e1b 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -1,9 +1,10 @@
use std::cell::RefCell;
use std::fmt::Debug;
+use super::FulfillmentContext;
use super::TraitEngine;
-use super::{ChalkFulfillmentContext, FulfillmentContext};
use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt;
+use crate::traits::error_reporting::TypeErrCtxtExt;
use crate::traits::NormalizeExt;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::ErrorGuaranteed;
@@ -24,27 +25,25 @@ use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::config::TraitSolver;
-use rustc_span::Span;
pub trait TraitEngineExt<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;
- fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self>;
+ fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>;
}
impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> Box<Self> {
- match tcx.sess.opts.unstable_opts.trait_solver {
- TraitSolver::Classic => Box::new(FulfillmentContext::new()),
- TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new()),
- TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()),
- }
- }
-
- fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> {
- match tcx.sess.opts.unstable_opts.trait_solver {
- TraitSolver::Classic => Box::new(FulfillmentContext::new_in_snapshot()),
- TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new_in_snapshot()),
- TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()),
+ fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> {
+ match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) {
+ (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => {
+ Box::new(FulfillmentContext::new(infcx))
+ }
+ (TraitSolver::Next | TraitSolver::NextCoherence, true) => {
+ Box::new(NextFulfillmentCtxt::new(infcx))
+ }
+ _ => bug!(
+ "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})",
+ infcx.tcx.sess.opts.unstable_opts.trait_solver,
+ infcx.next_trait_solver()
+ ),
}
}
}
@@ -58,11 +57,7 @@ pub struct ObligationCtxt<'a, 'tcx> {
impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
- Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) }
- }
-
- pub fn new_in_snapshot(infcx: &'a InferCtxt<'tcx>) -> Self {
- Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx.tcx)) }
+ Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) }
}
pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
@@ -202,17 +197,24 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
}
}
+ pub fn assumed_wf_types_and_report_errors(
+ &self,
+ param_env: ty::ParamEnv<'tcx>,
+ def_id: LocalDefId,
+ ) -> Result<FxIndexSet<Ty<'tcx>>, ErrorGuaranteed> {
+ self.assumed_wf_types(param_env, def_id)
+ .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(&errors))
+ }
+
pub fn assumed_wf_types(
&self,
param_env: ty::ParamEnv<'tcx>,
- span: Span,
def_id: LocalDefId,
- ) -> FxIndexSet<Ty<'tcx>> {
+ ) -> Result<FxIndexSet<Ty<'tcx>>, Vec<FulfillmentError<'tcx>>> {
let tcx = self.infcx.tcx;
- let assumed_wf_types = tcx.assumed_wf_types(def_id);
let mut implied_bounds = FxIndexSet::default();
- let cause = ObligationCause::misc(span, def_id);
- for ty in assumed_wf_types {
+ let mut errors = Vec::new();
+ for &(ty, span) in tcx.assumed_wf_types(def_id) {
// FIXME(@lcnr): rustc currently does not check wf for types
// pre-normalization, meaning that implied bounds are sometimes
// incorrect. See #100910 for more details.
@@ -225,10 +227,19 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
// sound and then uncomment this line again.
// implied_bounds.insert(ty);
- let normalized = self.normalize(&cause, param_env, ty);
- implied_bounds.insert(normalized);
+ let cause = ObligationCause::misc(span, def_id);
+ match self
+ .infcx
+ .at(&cause, param_env)
+ .deeply_normalize(ty, &mut **self.engine.borrow_mut())
+ {
+ // Insert well-formed types, ignoring duplicates.
+ Ok(normalized) => drop(implied_bounds.insert(normalized)),
+ Err(normalization_errors) => errors.extend(normalization_errors),
+ };
}
- implied_bounds
+
+ if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) }
}
pub fn make_canonicalized_query_response<T>(
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 7ab652761..f785c4eaf 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
@@ -1,7 +1,7 @@
use rustc_hir::def_id::DefId;
use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime};
use rustc_infer::traits::util::elaborate;
-use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation};
+use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation};
use rustc_middle::ty;
use rustc_span::{Span, DUMMY_SP};
@@ -14,13 +14,13 @@ pub enum Ambiguity {
pub fn recompute_applicable_impls<'tcx>(
infcx: &InferCtxt<'tcx>,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> Vec<Ambiguity> {
let tcx = infcx.tcx;
let param_env = obligation.param_env;
let impl_may_apply = |impl_def_id| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
let placeholder_obligation =
infcx.instantiate_binder_with_placeholders(obligation.predicate);
let obligation_trait_ref =
@@ -45,7 +45,7 @@ pub fn recompute_applicable_impls<'tcx>(
};
let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
let placeholder_obligation =
infcx.instantiate_binder_with_placeholders(obligation.predicate);
let obligation_trait_ref =
@@ -84,7 +84,7 @@ pub fn recompute_applicable_impls<'tcx>(
tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
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()
+ if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder()
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
{
if kind.rebind(trait_pred.trait_ref) == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) {
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 a10ececbb..f34218059 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -5,13 +5,13 @@ pub mod suggestions;
use super::{
FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, ObligationCause,
ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow,
- PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
+ PredicateObligation, SelectionError, TraitNotObjectSafe,
};
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{self, InferCtxt};
+use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::query::normalize::QueryNormalizeExt as _;
use crate::traits::specialize::to_pretty_impl_header;
use crate::traits::NormalizeExt;
use on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
@@ -28,21 +28,24 @@ 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::traits::SelectionOutputTypeParameterMismatch;
+use rustc_middle::traits::solve::Goal;
+use rustc_middle::traits::{DefiningAnchor, SelectionOutputTypeParameterMismatch};
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print};
use rustc_middle::ty::{
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
TypeVisitable, TypeVisitableExt,
};
-use rustc_session::config::TraitSolver;
+use rustc_session::config::{DumpSolverProofTree, TraitSolver};
use rustc_session::Limit;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::symbol::sym;
use rustc_span::{ExpnKind, Span, DUMMY_SP};
+use std::borrow::Cow;
use std::fmt;
+use std::io::Write;
use std::iter;
use std::ops::ControlFlow;
use suggestions::TypeErrCtxtExt as _;
@@ -59,12 +62,17 @@ pub enum CandidateSimilarity {
Fuzzy { ignoring_lifetimes: bool },
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ImplCandidate<'tcx> {
pub trait_ref: ty::TraitRef<'tcx>,
pub similarity: CandidateSimilarity,
}
+enum GetSafeTransmuteErrorAndReason {
+ Silent,
+ Error { err_msg: String, safe_transmute_explanation: String },
+}
+
pub trait InferCtxtExt<'tcx> {
/// Given some node representing a fn-like thing in the HIR map,
/// returns a span and `ArgKind` information that describes the
@@ -148,6 +156,12 @@ pub trait TypeErrCtxtExt<'tcx> {
root_obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>,
);
+
+ fn report_const_param_not_wf(
+ &self,
+ ty: Ty<'tcx>,
+ obligation: &PredicateObligation<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
}
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
@@ -365,7 +379,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
param_env,
ty.rebind(ty::TraitPredicate { trait_ref, constness, polarity }),
);
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
ocx.register_obligation(obligation);
if ocx.select_all_or_error().is_empty() {
return Ok((
@@ -618,6 +632,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
error: &SelectionError<'tcx>,
) {
let tcx = self.tcx;
+
+ if tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError {
+ dump_proof_tree(root_obligation, self.infcx);
+ }
+
let mut span = obligation.cause.span;
// FIXME: statically guarantee this by tainting after the diagnostic is emitted
self.set_tainted_by_errors(
@@ -640,6 +659,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
span = obligation.cause.span;
}
}
+
if let ObligationCauseCode::CompareImplItemObligation {
impl_item_def_id,
trait_item_def_id,
@@ -656,9 +676,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return;
}
+ // Report a const-param specific error
+ if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives()
+ {
+ self.report_const_param_not_wf(ty, &obligation).emit();
+ return;
+ }
+
let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => {
let trait_predicate = bound_predicate.rebind(trait_predicate);
let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate);
@@ -724,11 +751,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
== 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(
+ match self.get_safe_transmute_error_and_reason(
obligation.clone(),
trait_ref,
span,
- )
+ ) {
+ GetSafeTransmuteErrorAndReason::Silent => return,
+ GetSafeTransmuteErrorAndReason::Error {
+ err_msg,
+ safe_transmute_explanation,
+ } => (err_msg, Some(safe_transmute_explanation)),
+ }
} else {
(err_msg, None)
};
@@ -940,7 +973,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& self.fallback_has_occurred
{
let predicate = trait_predicate.map_bound(|trait_pred| {
- trait_pred.with_self_ty(self.tcx, self.tcx.mk_unit())
+ trait_pred.with_self_ty(self.tcx, Ty::new_unit(self.tcx))
});
let unit_obligation = obligation.with(tcx, predicate);
if self.predicate_may_hold(&unit_obligation) {
@@ -995,8 +1028,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate)
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => {
span_bug!(
span,
"outlives clauses should not error outside borrowck. obligation: `{:?}`",
@@ -1004,7 +1037,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
}
- ty::PredicateKind::Clause(ty::Clause::Projection(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => {
span_bug!(
span,
"projection clauses should be implied from elsewhere. obligation: `{:?}`",
@@ -1022,7 +1055,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.report_closure_error(&obligation, closure_def_id, found_kind, kind)
}
- ty::PredicateKind::WellFormed(ty) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => {
+ let ty = self.resolve_vars_if_possible(ty);
match self.tcx.sess.opts.unstable_opts.trait_solver {
TraitSolver::Classic => {
// WF predicates cannot themselves make
@@ -1032,7 +1066,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// (which may fail).
span_bug!(span, "WF predicate not satisfied for {:?}", ty);
}
- TraitSolver::Chalk | TraitSolver::Next => {
+ TraitSolver::Next | TraitSolver::NextCoherence => {
// FIXME: we'll need a better message which takes into account
// which bounds actually failed to hold.
self.tcx.sess.struct_span_err(
@@ -1043,7 +1077,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- ty::PredicateKind::ConstEvaluatable(..) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => {
// Errors for `ConstEvaluatable` predicates show up as
// `SelectionError::ConstEvalFailure`,
// not `Unimplemented`.
@@ -1067,17 +1101,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"),
- ty::PredicateKind::TypeWellFormedFromEnv(..) => span_bug!(
- span,
- "TypeWellFormedFromEnv predicate should only exist in the environment"
- ),
-
ty::PredicateKind::AliasRelate(..) => span_bug!(
span,
"AliasRelate predicate should never be the predicate cause of a SelectionError"
),
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
let mut diag = self.tcx.sess.struct_span_err(
span,
format!("the constant `{}` is not of type `{}`", ct, ty),
@@ -1122,6 +1151,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
+ SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage(
+ &obligation,
+ def_id,
+ ),
+
TraitNotObjectSafe(did) => {
let violations = self.tcx.object_safety_violations(did);
report_object_safety_error(self.tcx, span, did, violations)
@@ -1140,16 +1174,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
// Already reported in the query.
- SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) => {
- // FIXME(eddyb) remove this once `ErrorGuaranteed` becomes a proof token.
- self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error");
- return;
- }
+ SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) |
// Already reported.
- Overflow(OverflowError::Error(_)) => {
- self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported");
- return;
- }
+ Overflow(OverflowError::Error(_)) => return,
+
Overflow(_) => {
bug!("overflow should be handled before the `report_selection_error` path");
}
@@ -1162,6 +1190,102 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.point_at_returns_when_relevant(&mut err, &obligation);
err.emit();
}
+
+ fn report_const_param_not_wf(
+ &self,
+ ty: Ty<'tcx>,
+ obligation: &PredicateObligation<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let span = obligation.cause.span;
+
+ let mut diag = match ty.kind() {
+ _ if ty.has_param() => {
+ span_bug!(span, "const param tys cannot mention other generic parameters");
+ }
+ ty::Float(_) => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "`{ty}` is forbidden as the type of a const generic parameter",
+ )
+ }
+ ty::FnPtr(_) => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "using function pointers as const generic parameters is forbidden",
+ )
+ }
+ ty::RawPtr(_) => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "using raw pointers as const generic parameters is forbidden",
+ )
+ }
+ ty::Adt(def, _) => {
+ // We should probably see if we're *allowed* to derive `ConstParamTy` on the type...
+ let mut diag = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter",
+ );
+ // Only suggest derive if this isn't a derived obligation,
+ // and the struct is local.
+ if let Some(span) = self.tcx.hir().span_if_local(def.did())
+ && obligation.cause.code().parent().is_none()
+ {
+ if ty.is_structural_eq_shallow(self.tcx) {
+ diag.span_suggestion(
+ span,
+ "add `#[derive(ConstParamTy)]` to the struct",
+ "#[derive(ConstParamTy)]\n",
+ Applicability::MachineApplicable,
+ );
+ } else {
+ // FIXME(adt_const_params): We should check there's not already an
+ // overlapping `Eq`/`PartialEq` impl.
+ diag.span_suggestion(
+ span,
+ "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct",
+ "#[derive(ConstParamTy, PartialEq, Eq)]\n",
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ diag
+ }
+ _ => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "`{ty}` can't be used as a const parameter type",
+ )
+ }
+ };
+
+ let mut code = obligation.cause.code();
+ let mut pred = obligation.predicate.to_opt_poly_trait_pred();
+ while let Some((next_code, next_pred)) = code.parent() {
+ if let Some(pred) = pred {
+ let pred = self.instantiate_binder_with_placeholders(pred);
+ diag.note(format!(
+ "`{}` must implement `{}`, but it does not",
+ pred.self_ty(),
+ pred.print_modifiers_and_trait_path()
+ ));
+ }
+ code = next_code;
+ pred = next_pred;
+ }
+
+ diag
+ }
}
trait InferCtxtPrivExt<'tcx> {
@@ -1292,7 +1416,7 @@ trait InferCtxtPrivExt<'tcx> {
obligation: PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
- ) -> (String, Option<String>);
+ ) -> GetSafeTransmuteErrorAndReason;
fn add_tuple_trait_message(
&self,
@@ -1345,6 +1469,12 @@ trait InferCtxtPrivExt<'tcx> {
terr: TypeError<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+ fn report_opaque_type_auto_trait_leakage(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ def_id: DefId,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+
fn report_type_parameter_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
@@ -1372,8 +1502,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let bound_error = error.kind();
let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) {
(
- ty::PredicateKind::Clause(ty::Clause::Trait(..)),
- ty::PredicateKind::Clause(ty::Clause::Trait(error)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(error)),
) => (cond, bound_error.rebind(error)),
_ => {
// FIXME: make this work in other cases too.
@@ -1383,7 +1513,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
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)) =
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(implication)) =
bound_predicate.skip_binder()
{
let error = error.to_poly_trait_ref();
@@ -1404,6 +1534,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) {
+ if self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError {
+ dump_proof_tree(&error.root_obligation, self.infcx);
+ }
+
match error.code {
FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
self.report_selection_error(
@@ -1474,14 +1608,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
self.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
// try to find the mismatched types to report the error with.
//
// this can fail if the problem was higher-ranked, in which
// cause I have no idea for a good error message.
let bound_predicate = predicate.kind();
- let (values, err) = if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) =
+ let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) =
bound_predicate.skip_binder()
{
let data = self.instantiate_binder_with_fresh_vars(
@@ -1490,20 +1624,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
bound_predicate.rebind(data),
);
let unnormalized_term = match data.term.unpack() {
- ty::TermKind::Ty(_) => self
- .tcx
- .mk_projection(data.projection_ty.def_id, data.projection_ty.substs)
- .into(),
- ty::TermKind::Const(ct) => self
- .tcx
- .mk_const(
- ty::UnevaluatedConst {
- def: data.projection_ty.def_id,
- substs: data.projection_ty.substs,
- },
- ct.ty(),
- )
- .into(),
+ ty::TermKind::Ty(_) => Ty::new_projection(
+ self.tcx,
+ data.projection_ty.def_id,
+ data.projection_ty.substs,
+ )
+ .into(),
+ ty::TermKind::Const(ct) => ty::Const::new_unevaluated(
+ self.tcx,
+ ty::UnevaluatedConst {
+ def: data.projection_ty.def_id,
+ substs: data.projection_ty.substs,
+ },
+ ct.ty(),
+ )
+ .into(),
};
let normalized_term =
ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
@@ -1564,7 +1699,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}");
let secondary_span = (|| {
- let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) =
+ let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) =
predicate.kind().skip_binder()
else {
return None;
@@ -1602,7 +1737,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}),
) => Some((
ty.span,
- with_forced_trimmed_paths!(format!(
+ with_forced_trimmed_paths!(Cow::from(format!(
"type mismatch resolving `{}`",
self.resolve_vars_if_possible(predicate)
.print(FmtPrinter::new_with_limit(
@@ -1612,7 +1747,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
))
.unwrap()
.into_buffer()
- )),
+ ))),
)),
_ => None,
}
@@ -1702,12 +1837,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::Alias(ty::Projection, ..) => Some(12),
ty::Alias(ty::Inherent, ..) => Some(13),
ty::Alias(ty::Opaque, ..) => Some(14),
- ty::Never => Some(15),
- ty::Adt(..) => Some(16),
- ty::Generator(..) => Some(17),
- ty::Foreign(..) => Some(18),
- ty::GeneratorWitness(..) => Some(19),
- ty::GeneratorWitnessMIR(..) => Some(20),
+ ty::Alias(ty::Weak, ..) => Some(15),
+ ty::Never => Some(16),
+ ty::Adt(..) => Some(17),
+ ty::Generator(..) => Some(18),
+ ty::Foreign(..) => Some(19),
+ ty::GeneratorWitness(..) => Some(20),
+ ty::GeneratorWitnessMIR(..) => Some(21),
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None,
}
}
@@ -1775,6 +1911,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|| !trait_pred
.skip_binder()
.is_constness_satisfied_by(self.tcx.constness(def_id))
+ || !self.tcx.is_user_visible_dep(def_id.krate)
{
return None;
}
@@ -1803,10 +1940,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
other: bool,
) -> bool {
let other = if other { "other " } else { "" };
- let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
- candidates.sort();
- candidates.dedup();
- let len = candidates.len();
+ let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
if candidates.is_empty() {
return false;
}
@@ -1835,11 +1969,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect();
traits.sort();
traits.dedup();
+ // FIXME: this could use a better heuristic, like just checking
+ // that substs[1..] is the same.
+ let all_traits_equal = traits.len() == 1;
- let mut candidates: Vec<String> = candidates
+ let candidates: Vec<String> = candidates
.into_iter()
.map(|c| {
- if traits.len() == 1 {
+ if all_traits_equal {
format!("\n {}", c.self_ty())
} else {
format!("\n {}", c)
@@ -1847,14 +1984,16 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
})
.collect();
- candidates.sort();
- candidates.dedup();
let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
err.help(format!(
"the following {other}types implement trait `{}`:{}{}",
trait_ref.print_only_trait_path(),
candidates[..end].join(""),
- if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
+ if candidates.len() > 9 {
+ format!("\nand {} others", candidates.len() - 8)
+ } else {
+ String::new()
+ }
));
true
};
@@ -1868,7 +2007,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Mentioning implementers of `Copy`, `Debug` and friends is not useful.
return false;
}
- let normalized_impl_candidates: Vec<_> = self
+ let mut impl_candidates: Vec<_> = self
.tcx
.all_impls(def_id)
// Ignore automatically derived impls and `!Trait` impls.
@@ -1895,7 +2034,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
})
.collect();
- return report(normalized_impl_candidates, err);
+
+ impl_candidates.sort();
+ impl_candidates.dedup();
+ return report(impl_candidates, err);
}
// Sort impl candidates so that ordering is consistent for UI tests.
@@ -1904,27 +2046,25 @@ 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
+ let mut impl_candidates: Vec<_> = impl_candidates
.iter()
- .copied()
- .map(|ImplCandidate { trait_ref, similarity }| {
- // FIXME(compiler-errors): This should be using `NormalizeExt::normalize`
- let normalized = self
- .at(&ObligationCause::dummy(), ty::ParamEnv::empty())
- .query_normalize(trait_ref)
- .map_or(trait_ref, |normalized| normalized.value);
- (similarity, normalized)
+ .cloned()
+ .map(|mut cand| {
+ // Fold the consts so that they shows up as, e.g., `10`
+ // instead of `core::::array::{impl#30}::{constant#0}`.
+ cand.trait_ref = cand.trait_ref.fold_with(&mut BottomUpFolder {
+ tcx: self.tcx,
+ ty_op: |ty| ty,
+ lt_op: |lt| lt,
+ ct_op: |ct| ct.eval(self.tcx, ty::ParamEnv::empty()),
+ });
+ cand
})
- .collect::<Vec<_>>();
- normalized_impl_candidates_and_similarities.sort();
- normalized_impl_candidates_and_similarities.dedup();
-
- let normalized_impl_candidates = normalized_impl_candidates_and_similarities
- .into_iter()
- .map(|(_, normalized)| normalized)
- .collect::<Vec<_>>();
+ .collect();
+ impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref));
+ impl_candidates.dedup();
- report(normalized_impl_candidates, err)
+ report(impl_candidates.into_iter().map(|cand| cand.trait_ref).collect(), err)
}
fn report_similar_impl_candidates_for_root_obligation(
@@ -2075,7 +2215,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let bound_predicate = predicate.kind();
let mut err = match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
let trait_ref = bound_predicate.rebind(data.trait_ref);
debug!(?trait_ref);
@@ -2145,55 +2285,40 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
};
- let obligation = obligation.with(self.tcx, trait_ref);
- let mut selcx = SelectionContext::new(&self);
- match selcx.select_from_obligation(&obligation) {
- Ok(None) => {
- let ambiguities =
- ambiguity::recompute_applicable_impls(self.infcx, &obligation);
- let has_non_region_infer = trait_ref
- .skip_binder()
- .substs
- .types()
- .any(|t| !t.is_ty_or_numeric_infer());
- // It doesn't make sense to talk about applicable impls if there are more
- // than a handful of them.
- if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
- if self.tainted_by_errors().is_some() && subst.is_none() {
- // If `subst.is_none()`, then this is probably two param-env
- // candidates or impl candidates that are equal modulo lifetimes.
- // Therefore, if we've already emitted an error, just skip this
- // one, since it's not particularly actionable.
- err.cancel();
- return;
- }
- self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
- } else {
- if self.tainted_by_errors().is_some() {
- err.cancel();
- return;
- }
- err.note(format!("cannot satisfy `{}`", predicate));
- let impl_candidates = self.find_similar_impl_candidates(
- predicate.to_opt_poly_trait_pred().unwrap(),
- );
- if impl_candidates.len() < 10 {
- self.report_similar_impl_candidates(
- impl_candidates.as_slice(),
- trait_ref,
- obligation.cause.body_id,
- &mut err,
- false,
- );
- }
- }
+ let ambiguities = ambiguity::recompute_applicable_impls(
+ self.infcx,
+ &obligation.with(self.tcx, trait_ref),
+ );
+ let has_non_region_infer =
+ trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_or_numeric_infer());
+ // It doesn't make sense to talk about applicable impls if there are more
+ // than a handful of them.
+ if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
+ if self.tainted_by_errors().is_some() && subst.is_none() {
+ // If `subst.is_none()`, then this is probably two param-env
+ // candidates or impl candidates that are equal modulo lifetimes.
+ // Therefore, if we've already emitted an error, just skip this
+ // one, since it's not particularly actionable.
+ err.cancel();
+ return;
}
- _ => {
- if self.tainted_by_errors().is_some() {
- err.cancel();
- return;
- }
- err.note(format!("cannot satisfy `{}`", predicate));
+ self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
+ } else {
+ if self.tainted_by_errors().is_some() {
+ err.cancel();
+ return;
+ }
+ err.note(format!("cannot satisfy `{}`", predicate));
+ let impl_candidates = self
+ .find_similar_impl_candidates(predicate.to_opt_poly_trait_pred().unwrap());
+ if impl_candidates.len() < 10 {
+ self.report_similar_impl_candidates(
+ impl_candidates.as_slice(),
+ trait_ref,
+ obligation.cause.body_id,
+ &mut err,
+ false,
+ );
}
}
@@ -2258,17 +2383,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& let Some(impl_def_id) = trait_impls.non_blanket_impls().values().flatten().next()
{
let non_blanket_impl_count = trait_impls.non_blanket_impls().values().flatten().count();
- let message = if non_blanket_impl_count == 1 {
- "use the fully-qualified path to the only available implementation".to_string()
- } else {
+ // If there is only one implementation of the trait, suggest using it.
+ // Otherwise, use a placeholder comment for the implementation.
+ let (message, impl_suggestion) = if non_blanket_impl_count == 1 {(
+ "use the fully-qualified path to the only available implementation".to_string(),
+ format!("<{} as ", self.tcx.type_of(impl_def_id).subst_identity())
+ )} else {(
format!(
"use a fully-qualified path to a specific available implementation ({} found)",
non_blanket_impl_count
- )
- };
+ ),
+ "</* self type */ as ".to_string()
+ )};
let mut suggestions = vec![(
path.span.shrink_to_lo(),
- format!("<{} as ", self.tcx.type_of(impl_def_id).subst_identity())
+ impl_suggestion
)];
if let Some(generic_arg) = trait_path_segment.args {
let between_span = trait_path_segment.ident.span.between(generic_arg.span_ext);
@@ -2291,7 +2420,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
// Same hacky approach as above to avoid deluging user
// with error messages.
if arg.references_error()
@@ -2329,7 +2458,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
true,
)
}
- ty::PredicateKind::Clause(ty::Clause::Projection(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
if predicate.references_error() || self.tainted_by_errors().is_some() {
return;
}
@@ -2363,7 +2492,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- ty::PredicateKind::ConstEvaluatable(data) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
if predicate.references_error() || self.tainted_by_errors().is_some() {
return;
}
@@ -2526,11 +2655,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
- if let ty::Param(ty::ParamTy { name, .. }) = *ty.kind() {
+ if let ty::Param(_) = *ty.kind() {
let infcx = self.infcx;
*self.var_map.entry(ty).or_insert_with(|| {
infcx.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeParameterDefinition(name, None),
+ kind: TypeVariableOriginKind::MiscVariable,
span: DUMMY_SP,
})
})
@@ -2577,7 +2706,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
) {
- let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = obligation.predicate.kind().skip_binder() else { return; };
+ let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = obligation.predicate.kind().skip_binder() else { return; };
let (ObligationCauseCode::BindingObligation(item_def_id, span)
| ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..))
= *obligation.cause.code().peel_derives() else { return; };
@@ -2738,7 +2867,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
obligation: PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
- ) -> (String, Option<String>) {
+ ) -> GetSafeTransmuteErrorAndReason {
+ use rustc_transmute::Answer;
+
// Erase regions because layout code doesn't particularly care about regions.
let trait_ref = self.tcx.erase_regions(self.tcx.erase_late_bound_regions(trait_ref));
@@ -2751,19 +2882,20 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, trait_ref.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) => {
+ Answer::No(reason) => {
let dst = trait_ref.substs.type_at(0);
let src = trait_ref.substs.type_at(1);
- let custom_err_msg = format!(
+ let err_msg = format!(
"`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`"
);
- let reason_msg = match reason {
+ let safe_transmute_explanation = match reason {
rustc_transmute::Reason::SrcIsUnspecified => {
format!("`{src}` does not have a well-specified layout")
}
@@ -2779,19 +2911,39 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
rustc_transmute::Reason::DstIsPrivate => format!(
"`{dst}` is or contains a type or field that is not visible in that scope"
),
- // 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}`")
}
+ rustc_transmute::Reason::DstHasStricterAlignment {
+ src_min_align,
+ dst_min_align,
+ } => {
+ format!(
+ "The minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})"
+ )
+ }
+ rustc_transmute::Reason::DstIsMoreUnique => {
+ format!("`{src}` is a shared reference, but `{dst}` is a unique reference")
+ }
+ // Already reported by rustc
+ rustc_transmute::Reason::TypeError => {
+ return GetSafeTransmuteErrorAndReason::Silent;
+ }
+ rustc_transmute::Reason::SrcLayoutUnknown => {
+ format!("`{src}` has an unknown layout")
+ }
+ rustc_transmute::Reason::DstLayoutUnknown => {
+ format!("`{dst}` has an unknown layout")
+ }
};
- (custom_err_msg, Some(reason_msg))
+ GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation }
}
// 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!(
+ Answer::Yes => span_bug!(
span,
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
),
- _ => span_bug!(span, "Unsupported rustc_transmute::Reason variant"),
+ other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
}
}
@@ -3049,6 +3201,39 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
}
+ fn report_opaque_type_auto_trait_leakage(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ def_id: DefId,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
+ hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
+ format!("opaque type")
+ }
+ hir::OpaqueTyOrigin::TyAlias { .. } => {
+ format!("`{}`", self.tcx.def_path_debug_str(def_id))
+ }
+ };
+ let mut err = self.tcx.sess.struct_span_err(
+ obligation.cause.span,
+ format!("cannot check whether the hidden type of {name} satisfies auto traits"),
+ );
+ err.span_note(self.tcx.def_span(def_id), "opaque type is declared here");
+ match self.defining_use_anchor {
+ DefiningAnchor::Bubble | DefiningAnchor::Error => {}
+ DefiningAnchor::Bind(bind) => {
+ err.span_note(
+ self.tcx.def_ident_span(bind).unwrap_or_else(|| self.tcx.def_span(bind)),
+ "this item depends on auto traits of the hidden type, \
+ but may also be registering the hidden type. \
+ This is not supported right now. \
+ You can try moving the opaque type and the item that actually registers a hidden type into a new submodule".to_string(),
+ );
+ }
+ };
+ err
+ }
+
fn report_type_parameter_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
@@ -3178,7 +3363,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::ConstEvaluatable(ct) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
let ty::ConstKind::Unevaluated(uv) = ct.kind() else {
bug!("const evaluatable failed for non-unevaluated const `{ct:?}`");
};
@@ -3360,3 +3545,16 @@ pub enum DefIdOrName {
DefId(DefId),
Name(&'static str),
}
+
+pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &InferCtxt<'tcx>) {
+ infcx.probe(|_| {
+ let goal = Goal { predicate: o.predicate, param_env: o.param_env };
+ let tree = infcx
+ .evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No))
+ .1
+ .expect("proof tree should have been generated");
+ let mut lock = std::io::stdout().lock();
+ let _ = lock.write_fmt(format_args!("{tree:?}"));
+ let _ = lock.flush();
+ });
+}
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 10bd027b6..b16d2eb5f 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
@@ -41,7 +41,6 @@ pub trait TypeErrCtxtExt<'tcx> {
static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
kw::SelfUpper,
sym::ItemContext,
- sym::from_method,
sym::from_desugaring,
sym::direct,
sym::cause,
@@ -172,23 +171,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- if let ObligationCauseCode::ItemObligation(item)
- | ObligationCauseCode::BindingObligation(item, _)
- | ObligationCauseCode::ExprItemObligation(item, ..)
- | ObligationCauseCode::ExprBindingObligation(item, ..) = *obligation.cause.code()
- {
- // FIXME: maybe also have some way of handling methods
- // from other traits? That would require name resolution,
- // which we might want to be some sort of hygienic.
- //
- // Currently I'm leaving it for what I need for `try`.
- if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
- let method = self.tcx.item_name(item);
- flags.push((sym::from_method, None));
- flags.push((sym::from_method, Some(method.to_string())));
- }
- }
-
if let Some(k) = obligation.cause.span.desugaring_kind() {
flags.push((sym::from_desugaring, None));
flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
@@ -278,7 +260,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
if let ty::Array(aty, len) = self_ty.kind() {
flags.push((sym::_Self, Some("[]".to_string())));
- let len = len.kind().try_to_value().and_then(|v| v.try_to_target_usize(self.tcx));
+ let len = len.try_to_value().and_then(|v| v.try_to_target_usize(self.tcx));
flags.push((sym::_Self, Some(format!("[{}; _]", aty))));
if let Some(n) = len {
flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n))));
@@ -672,7 +654,7 @@ impl<'tcx> OnUnimplementedFormatString {
None => {
if let Some(val) = options.get(&s) {
val
- } else if s == sym::from_desugaring || s == sym::from_method {
+ } else if s == sym::from_desugaring {
// don't break messages using these two arguments incorrectly
&empty_string
} else if s == sym::ItemContext {
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 82bad96ea..9ac1ba027 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
- ErrorGuaranteed, MultiSpan, Style,
+ ErrorGuaranteed, MultiSpan, Style, SuggestionStyle,
};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
@@ -38,6 +38,7 @@ use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
use rustc_target::spec::abi;
+use std::borrow::Cow;
use std::iter;
use std::ops::Deref;
@@ -186,7 +187,12 @@ pub trait TypeErrCtxtExt<'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
- fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol>;
+ fn get_closure_name(
+ &self,
+ def_id: DefId,
+ err: &mut Diagnostic,
+ msg: Cow<'static, str>,
+ ) -> Option<Symbol>;
fn suggest_fn_call(
&self,
@@ -356,6 +362,15 @@ pub trait TypeErrCtxtExt<'tcx> {
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
+
+ fn suggest_option_method_if_applicable(
+ &self,
+ failed_pred: ty::Predicate<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ );
+
fn note_function_argument_obligation(
&self,
body_id: LocalDefId,
@@ -771,7 +786,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let Some(steps) =
autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| {
// Re-add the `&`
- let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
+ let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl });
// Remapping bound vars here
let real_trait_pred_and_ty =
@@ -857,7 +872,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
/// Given a closure's `DefId`, return the given name of the closure.
///
/// This doesn't account for reassignments, but it's only used for suggestions.
- fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol> {
+ fn get_closure_name(
+ &self,
+ def_id: DefId,
+ err: &mut Diagnostic,
+ msg: Cow<'static, str>,
+ ) -> Option<Symbol> {
let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> {
// Get the local name of this closure. This can be inaccurate because
// of the possibility of reassignment, but this should be good enough.
@@ -900,7 +920,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return false;
}
- if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder()
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = obligation.predicate.kind().skip_binder()
&& Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
{
// Don't suggest calling to turn an unsized type into a sized type
@@ -934,17 +954,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let msg = match def_id_or_name {
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
DefKind::Ctor(CtorOf::Struct, _) => {
- "use parentheses to construct this tuple struct".to_string()
+ Cow::from("use parentheses to construct this tuple struct")
}
DefKind::Ctor(CtorOf::Variant, _) => {
- "use parentheses to construct this tuple variant".to_string()
+ Cow::from("use parentheses to construct this tuple variant")
}
- kind => format!(
+ kind => Cow::from(format!(
"use parentheses to call this {}",
self.tcx.def_kind_descr(kind, def_id)
- ),
+ )),
},
- DefIdOrName::Name(name) => format!("use parentheses to call this {name}"),
+ DefIdOrName::Name(name) => Cow::from(format!("use parentheses to call this {name}")),
};
let args = inputs
@@ -979,7 +999,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
..
})) => {
err.span_label(*fn_decl_span, "consider calling this closure");
- let Some(name) = self.get_closure_name(def_id, err, &msg) else {
+ let Some(name) = self.get_closure_name(def_id, err, msg.clone()) else {
return false;
};
name.to_string()
@@ -1137,7 +1157,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
self.tcx.item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
// args tuple will always be substs[1]
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
@@ -1181,7 +1201,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
DefIdOrName::Name("type parameter")
};
param_env.caller_bounds().iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
&& proj.projection_ty.self_ty() == found
// args tuple will always be substs[1]
@@ -1278,13 +1298,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
(
trait_pred,
- self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+ Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),
)
});
let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
(
trait_pred,
- self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+ Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),
)
});
@@ -1341,7 +1361,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.note(msg);
} else {
err.message =
- vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
+ vec![(rustc_errors::DiagnosticMessage::from(msg), Style::NoStyle)];
}
err.span_label(
span,
@@ -1445,7 +1465,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
) {
let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else { return; };
let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { return; };
- let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty);
+ let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty);
for predicate in predicates.iter() {
if !self.predicate_must_hold_modulo_regions(
@@ -1619,7 +1639,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
// FIXME: account for associated `async fn`s.
if let hir::Expr { span, kind: hir::ExprKind::Call(base, _), .. } = expr {
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) =
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
obligation.predicate.kind().skip_binder()
{
err.span_label(*span, format!("this call returns `{}`", pred.self_ty()));
@@ -1686,8 +1706,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind()
{
let suggested_ty = match mutability {
- hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type),
- hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type),
+ hir::Mutability::Mut => Ty::new_imm_ref(self.tcx, region, t_type),
+ hir::Mutability::Not => Ty::new_mut_ref(self.tcx, region, t_type),
};
// Remapping bound vars here
@@ -1931,7 +1951,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
),
};
- infcx.tcx.mk_fn_ptr(trait_ref.rebind(sig))
+ Ty::new_fn_ptr(infcx.tcx, trait_ref.rebind(sig))
}
let argument_kind = match expected.skip_binder().self_ty().kind() {
@@ -1981,7 +2001,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = cause
&& let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
&& let Some(pred) = predicates.predicates.get(*idx)
- && let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder()
+ && let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder()
&& self.tcx.is_fn_trait(trait_pred.def_id())
{
let expected_self =
@@ -1995,7 +2015,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let other_pred = predicates.into_iter()
.enumerate()
.find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
+ ty::ClauseKind::Trait(trait_pred)
if self.tcx.is_fn_trait(trait_pred.def_id())
&& other_idx != idx
// Make sure that the self type matches
@@ -2121,7 +2141,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// bound was introduced. At least one generator should be present for this diagnostic to be
// modified.
let (mut trait_ref, mut target_ty) = match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(p)) => (Some(p), Some(p.self_ty())),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => (Some(p), Some(p.self_ty())),
_ => (None, None),
};
let mut generator = None;
@@ -2643,8 +2663,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::LetElse
| ObligationCauseCode::BinOp { .. }
| ObligationCauseCode::AscribeUserTypeProvePredicate(..)
- | ObligationCauseCode::RustCall
- | ObligationCauseCode::DropImpl => {}
+ | ObligationCauseCode::DropImpl
+ | ObligationCauseCode::ConstParam(_) => {}
+ ObligationCauseCode::RustCall => {
+ if let Some(pred) = predicate.to_opt_poly_trait_pred()
+ && Some(pred.def_id()) == self.tcx.lang_items().sized_trait()
+ {
+ err.note("argument required to be sized due to `extern \"rust-call\"` ABI");
+ }
+ }
ObligationCauseCode::SliceOrArrayElem => {
err.note("slice and array elements must have `Sized` type");
}
@@ -2697,6 +2724,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let msg = format!("required by this bound in `{short_item_name}`");
multispan.push_span_label(span, msg);
err.span_note(multispan, descr);
+ if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
+ && let ty::ClauseKind::Trait(trait_pred) = clause
+ {
+ let def_id = trait_pred.def_id();
+ let visible_item = if let Some(local) = def_id.as_local() {
+ // Check for local traits being reachable.
+ let vis = &self.tcx.resolutions(()).effective_visibilities;
+ // Account for non-`pub` traits in the root of the local crate.
+ let is_locally_reachable = self.tcx.parent(def_id).is_crate_root();
+ vis.is_reachable(local) || is_locally_reachable
+ } else {
+ // Check for foreign traits being reachable.
+ self.tcx.visible_parent_map(()).get(&def_id).is_some()
+ };
+ if let DefKind::Trait = tcx.def_kind(item_def_id) && !visible_item {
+ // FIXME(estebank): extend this to search for all the types that do
+ // implement this trait and list them.
+ err.note(format!(
+ "`{short_item_name}` is a \"sealed trait\", because to implement \
+ it you also need to implelement `{}`, which is not accessible; \
+ this is usually done to force you to use one of the provided \
+ types that already implement it",
+ with_no_trimmed_paths!(tcx.def_path_str(def_id)),
+ ));
+ }
+ }
} else {
err.span_note(tcx.def_span(item_def_id), descr);
}
@@ -2786,10 +2839,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.help("unsized locals are gated as an unstable feature");
}
}
- ObligationCauseCode::SizedArgumentType(sp) => {
- if let Some(span) = sp {
+ ObligationCauseCode::SizedArgumentType(ty_span) => {
+ if let Some(span) = ty_span {
if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
- && let ty::Clause::Trait(trait_pred) = clause
+ && let ty::ClauseKind::Trait(trait_pred) = clause
&& let ty::Dynamic(..) = trait_pred.self_ty().kind()
{
let span = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
@@ -2958,7 +3011,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
for ty in bound_tys.skip_binder() {
with_forced_trimmed_paths!(write!(msg, "`{}`, ", ty).unwrap());
}
- err.note(msg.trim_end_matches(", "))
+ err.note(msg.trim_end_matches(", ").to_string())
}
ty::GeneratorWitnessMIR(def_id, substs) => {
use std::fmt::Write;
@@ -2972,7 +3025,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let ty = bty.subst(tcx, substs);
write!(msg, "`{}`, ", ty).unwrap();
}
- err.note(msg.trim_end_matches(", "))
+ err.note(msg.trim_end_matches(", ").to_string())
}
ty::Generator(def_id, _, _) => {
let sp = self.tcx.def_span(def_id);
@@ -3171,6 +3224,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
});
}
+ ObligationCauseCode::TypeAlias(ref nested, span, def_id) => {
+ // #74711: avoid a stack overflow
+ ensure_sufficient_stack(|| {
+ self.note_obligation_cause_code(
+ body_id,
+ err,
+ predicate,
+ param_env,
+ nested,
+ obligated_types,
+ seen_requirements,
+ )
+ });
+ let mut multispan = MultiSpan::from(span);
+ multispan.push_span_label(span, "required by this bound");
+ err.span_note(
+ multispan,
+ format!(
+ "required by a bound on the type alias `{}`",
+ self.infcx.tcx.item_name(def_id)
+ ),
+ );
+ }
ObligationCauseCode::FunctionArgumentObligation {
arg_hir_id,
call_hir_id,
@@ -3271,7 +3347,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
// `<T as Future>::Output`
let projection_ty = trait_pred.map_bound(|trait_pred| {
- self.tcx.mk_projection(
+ Ty::new_projection(
+ self.tcx,
item_def_id,
// Future::Output has no substs
[trait_pred.self_ty()],
@@ -3425,7 +3502,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
let expr = expr.peel_blocks();
- let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc());
+ let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx,));
let span = expr.span;
if Some(span) != err.span.primary_span() {
err.span_label(
@@ -3450,7 +3527,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_substs)
&& let Some(where_pred) = where_clauses.predicates.get(*idx)
{
- if let Some(where_pred) = where_pred.to_opt_poly_trait_pred()
+ if let Some(where_pred) = where_pred.as_trait_clause()
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
{
let where_pred = self.instantiate_binder_with_placeholders(where_pred);
@@ -3473,13 +3550,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
})
};
- } else if let Some(where_pred) = where_pred.to_opt_poly_projection_pred()
+ } else if let Some(where_pred) = where_pred.as_projection_clause()
&& let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
&& let Some(found) = failed_pred.skip_binder().term.ty()
{
type_diffs = vec![
Sorts(ty::error::ExpectedFound {
- expected: self.tcx.mk_alias(ty::Projection, where_pred.skip_binder().projection_ty),
+ expected: Ty::new_alias(self.tcx,ty::Projection, where_pred.skip_binder().projection_ty),
found,
}),
];
@@ -3509,15 +3586,93 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.replace_span_with(path.ident.span, true);
}
}
- if let Some(Node::Expr(hir::Expr {
- kind:
- hir::ExprKind::Call(hir::Expr { span, .. }, _)
- | hir::ExprKind::MethodCall(hir::PathSegment { ident: Ident { span, .. }, .. }, ..),
- ..
- })) = hir.find(call_hir_id)
+
+ if let Some(Node::Expr(expr)) = hir.find(call_hir_id) {
+ if let hir::ExprKind::Call(hir::Expr { span, .. }, _)
+ | hir::ExprKind::MethodCall(
+ hir::PathSegment { ident: Ident { span, .. }, .. },
+ ..,
+ ) = expr.kind
+ {
+ if Some(*span) != err.span.primary_span() {
+ err.span_label(*span, "required by a bound introduced by this call");
+ }
+ }
+
+ if let hir::ExprKind::MethodCall(_, expr, ..) = expr.kind {
+ self.suggest_option_method_if_applicable(failed_pred, param_env, err, expr);
+ }
+ }
+ }
+
+ fn suggest_option_method_if_applicable(
+ &self,
+ failed_pred: ty::Predicate<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ ) {
+ let tcx = self.tcx;
+ let infcx = self.infcx;
+ let Some(typeck_results) = self.typeck_results.as_ref() else { return };
+
+ // Make sure we're dealing with the `Option` type.
+ let Some(option_ty_adt) = typeck_results.expr_ty_adjusted(expr).ty_adt_def() else { return };
+ if !tcx.is_diagnostic_item(sym::Option, option_ty_adt.did()) {
+ return;
+ }
+
+ // Given the predicate `fn(&T): FnOnce<(U,)>`, extract `fn(&T)` and `(U,)`,
+ // then suggest `Option::as_deref(_mut)` if `U` can deref to `T`
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, .. }))
+ = failed_pred.kind().skip_binder()
+ && tcx.is_fn_trait(trait_ref.def_id)
+ && let [self_ty, found_ty] = trait_ref.substs.as_slice()
+ && let Some(fn_ty) = self_ty.as_type().filter(|ty| ty.is_fn())
+ && let fn_sig @ ty::FnSig {
+ abi: abi::Abi::Rust,
+ c_variadic: false,
+ unsafety: hir::Unsafety::Normal,
+ ..
+ } = fn_ty.fn_sig(tcx).skip_binder()
+
+ // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T`
+ && let Some(&ty::Ref(_, target_ty, needs_mut)) = fn_sig.inputs().first().map(|t| t.kind())
+ && !target_ty.has_escaping_bound_vars()
+
+ // Extract first tuple element out of fn trait, e.g. `FnOnce<(U,)>` -> `U`
+ && let Some(ty::Tuple(tys)) = found_ty.as_type().map(Ty::kind)
+ && let &[found_ty] = tys.as_slice()
+ && !found_ty.has_escaping_bound_vars()
+
+ // Extract `<U as Deref>::Target` assoc type and check that it is `T`
+ && let Some(deref_target_did) = tcx.lang_items().deref_target()
+ && let projection = Ty::new_projection(tcx,deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)]))
+ && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection)
+ && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation))
+ && infcx.can_eq(param_env, deref_target, target_ty)
{
- if Some(*span) != err.span.primary_span() {
- err.span_label(*span, "required by a bound introduced by this call");
+ let help = if let hir::Mutability::Mut = needs_mut
+ && let Some(deref_mut_did) = tcx.lang_items().deref_mut_trait()
+ && infcx
+ .type_implements_trait(deref_mut_did, iter::once(found_ty), param_env)
+ .must_apply_modulo_regions()
+ {
+ Some(("call `Option::as_deref_mut()` first", ".as_deref_mut()"))
+ } else if let hir::Mutability::Not = needs_mut {
+ Some(("call `Option::as_deref()` first", ".as_deref()"))
+ } else {
+ None
+ };
+
+ if let Some((msg, sugg)) = help {
+ err.span_suggestion_with_style(
+ expr.span.shrink_to_hi(),
+ msg,
+ sugg,
+ Applicability::MaybeIncorrect,
+ SuggestionStyle::ShowAlways
+ );
}
}
}
@@ -3539,7 +3694,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut assocs = vec![];
let mut expr = expr;
let mut prev_ty = self.resolve_vars_if_possible(
- typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc()),
+ typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
);
while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind {
// Point at every method call in the chain with the resulting type.
@@ -3550,7 +3705,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
assocs.push(assocs_in_this_method);
prev_ty = self.resolve_vars_if_possible(
- typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc()),
+ typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
);
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
@@ -3568,7 +3723,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let hir::Node::Param(param) = parent {
// ...and it is a an fn argument.
let prev_ty = self.resolve_vars_if_possible(
- typeck_results.node_type_opt(param.hir_id).unwrap_or(tcx.ty_error_misc()),
+ typeck_results.node_type_opt(param.hir_id).unwrap_or(Ty::new_misc_error(tcx,)),
);
let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env);
if assocs_in_this_method.iter().any(|a| a.is_some()) {
@@ -3671,7 +3826,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> {
- let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
+ let ocx = ObligationCtxt::new(self.infcx);
let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
for diff in type_diffs {
let Sorts(expected_found) = diff else { continue; };
@@ -3698,12 +3853,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// in. For example, this would be what `Iterator::Item` is here.
let ty_var = self.infcx.next_ty_var(origin);
// This corresponds to `<ExprTy as Iterator>::Item = _`.
- let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
- ty::ProjectionPredicate {
+ let projection = ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_ty: self.tcx.mk_alias_ty(proj.def_id, substs),
term: ty_var.into(),
- },
- )));
+ }),
+ ));
let body_def_id = self.tcx.hir().enclosing_body_owner(body_id);
// Add `<ExprTy as Iterator>::Item = _` obligation.
ocx.register_obligation(Obligation::misc(
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 2f85c32b5..cf9d9315f 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -4,7 +4,7 @@ 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_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
@@ -50,20 +50,15 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
/// method `select_all_or_error` can be used to report any remaining
/// ambiguous cases as errors.
pub struct FulfillmentContext<'tcx> {
- // A list of all obligations that have been registered with this
- // fulfillment context.
+ /// A list of all obligations that have been registered with this
+ /// fulfillment context.
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
- // Is it OK to register obligations into this infcx inside
- // an infcx snapshot?
- //
- // The "primary fulfillment" in many cases in typeck lives
- // outside of any snapshot, so any use of it inside a snapshot
- // will lead to trouble and therefore is checked against, but
- // other fulfillment contexts sometimes do live inside of
- // a snapshot (they don't *straddle* a snapshot, so there
- // is no trouble there).
- usable_in_snapshot: bool,
+ /// The snapshot in which this context was created. Using the context
+ /// outside of this snapshot leads to subtle bugs if the snapshot
+ /// gets rolled back. Because of this we explicitly check that we only
+ /// use the context in exactly this snapshot.
+ usable_in_snapshot: usize,
}
#[derive(Clone, Debug)]
@@ -80,18 +75,17 @@ pub struct PendingPredicateObligation<'tcx> {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(PendingPredicateObligation<'_>, 72);
-impl<'a, 'tcx> FulfillmentContext<'tcx> {
+impl<'tcx> FulfillmentContext<'tcx> {
/// Creates a new fulfillment context.
- pub(super) fn new() -> FulfillmentContext<'tcx> {
- FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: false }
- }
-
- pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> {
- FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: true }
+ pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx> {
+ FulfillmentContext {
+ predicates: ObligationForest::new(),
+ usable_in_snapshot: infcx.num_open_snapshots(),
+ }
}
/// Attempts to select obligations using `selcx`.
- fn select(&mut self, selcx: SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> {
+ fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> {
let span = debug_span!("select", obligation_forest_size = ?self.predicates.len());
let _enter = span.enter();
@@ -116,19 +110,19 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
+ #[inline]
fn register_predicate_obligation(
&mut self,
infcx: &InferCtxt<'tcx>,
obligation: PredicateObligation<'tcx>,
) {
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
// this helps to reduce duplicate errors, as well as making
// debug output much nicer to read and so on.
let obligation = infcx.resolve_vars_if_possible(obligation);
debug!(?obligation, "register_predicate_obligation");
- assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
-
self.predicates
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
}
@@ -332,7 +326,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
// Evaluation will discard candidates using the leak check.
// This means we need to pass it the bound version of our
// predicate.
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_ref)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) => {
let trait_obligation = obligation.with(infcx.tcx, binder.rebind(trait_ref));
self.process_trait_obligation(
@@ -341,7 +335,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
&mut pending_obligation.stalled_on,
)
}
- ty::PredicateKind::Clause(ty::Clause::Projection(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
let project_obligation = obligation.with(infcx.tcx, binder.rebind(data));
self.process_projection_obligation(
@@ -350,30 +344,27 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
&mut pending_obligation.stalled_on,
)
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
- | ty::PredicateKind::WellFormed(_)
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(_))
+ | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_))
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..) => {
let pred =
ty::Binder::dummy(infcx.instantiate_binder_with_placeholders(binder));
ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)]))
}
ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("TypeWellFormedFromEnv is only used for Chalk")
- }
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used for new solver")
}
},
Some(pred) => match pred {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
let trait_obligation = obligation.with(infcx.tcx, Binder::dummy(data));
self.process_trait_obligation(
@@ -383,7 +374,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
)
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => {
if infcx.considering_regions {
infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data));
}
@@ -391,7 +382,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ProcessResult::Changed(vec![])
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
t_a,
r_b,
))) => {
@@ -401,7 +392,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ProcessResult::Changed(vec![])
}
- ty::PredicateKind::Clause(ty::Clause::Projection(ref data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(ref data)) => {
let project_obligation = obligation.with(infcx.tcx, Binder::dummy(*data));
self.process_projection_obligation(
@@ -432,7 +423,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
match wf::obligations(
self.selcx.infcx,
obligation.param_env,
@@ -497,7 +488,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
- ty::PredicateKind::ConstEvaluatable(uv) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => {
match const_evaluatable::is_const_evaluatable(
self.selcx.infcx,
uv,
@@ -537,7 +528,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2);
use rustc_hir::def::DefKind;
- use ty::ConstKind::Unevaluated;
+ use ty::Unevaluated;
match (c1.kind(), c2.kind()) {
(Unevaluated(a), Unevaluated(b))
if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
@@ -633,13 +624,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("TypeWellFormedFromEnv is only used for Chalk")
- }
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used for new solver")
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq(
DefineOpaqueTypes::No,
ct.ty(),
@@ -679,7 +667,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
fn process_trait_obligation(
&mut self,
obligation: &PredicateObligation<'tcx>,
- trait_obligation: TraitObligation<'tcx>,
+ trait_obligation: PolyTraitObligation<'tcx>,
stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>,
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
let infcx = self.selcx.infcx;
@@ -695,7 +683,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
}
}
- match self.selcx.select(&trait_obligation) {
+ match self.selcx.poly_select(&trait_obligation) {
Ok(Some(impl_source)) => {
debug!("selecting trait at depth {} yielded Ok(Some)", obligation.recursion_depth);
ProcessResult::Changed(mk_pending(impl_source.nested_obligations()))
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index 2210ef975..e9cfd63e2 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -100,7 +100,8 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
| ty::Str
| ty::Array(..)
| ty::Slice(_)
- | ty::Ref(.., hir::Mutability::Not) => return Ok(()),
+ | ty::Ref(.., hir::Mutability::Not)
+ | ty::Tuple(_) => return Ok(()),
&ty::Adt(adt, substs) => (adt, substs),
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index f265230ff..1af8323b6 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -3,7 +3,6 @@
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
pub mod auto_trait;
-mod chalk_fulfill;
pub(crate) mod coherence;
pub mod const_evaluatable;
mod engine;
@@ -12,14 +11,16 @@ mod fulfill;
pub mod misc;
mod object_safety;
pub mod outlives_bounds;
-mod project;
+pub mod project;
pub mod query;
+#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))]
mod select;
mod specialize;
mod structural_match;
mod structural_normalize;
+#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))]
mod util;
-mod vtable;
+pub mod vtable;
pub mod wf;
use crate::infer::outlives::env::OutlivesEnvironment;
@@ -30,7 +31,7 @@ use rustc_errors::ErrorGuaranteed;
use rustc_middle::query::Providers;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
-use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeSuperVisitable};
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFolder, TypeSuperVisitable};
use rustc_middle::ty::{InternalSubsts, SubstsRef};
use rustc_span::def_id::DefId;
use rustc_span::Span;
@@ -38,6 +39,8 @@ use rustc_span::Span;
use std::fmt::Debug;
use std::ops::ControlFlow;
+pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+
pub use self::FulfillmentErrorCode::*;
pub use self::ImplSource::*;
pub use self::ObligationCauseCode::*;
@@ -60,19 +63,15 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
pub use self::specialize::{
specialization_graph, translate_substs, translate_substs_with_cause, OverlapError,
};
-pub use self::structural_match::{
- search_for_adt_const_param_violation, search_for_structural_match_violation,
-};
+pub use self::structural_match::search_for_structural_match_violation;
pub use self::structural_normalize::StructurallyNormalizeExt;
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, upcast_choices};
pub use self::util::{
- supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item,
- SupertraitDefIds,
+ check_substs_compatible, supertrait_def_ids, supertraits, transitive_bounds,
+ transitive_bounds_that_define_assoc_item, SupertraitDefIds,
};
-
-pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext;
+pub use self::util::{expand_trait_aliases, TraitAliasExpander};
+pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
pub use rustc_infer::traits::*;
@@ -114,11 +113,11 @@ pub fn predicates_for_generics<'tcx>(
param_env: ty::ParamEnv<'tcx>,
generic_bounds: ty::InstantiatedPredicates<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
- generic_bounds.into_iter().enumerate().map(move |(idx, (predicate, span))| Obligation {
+ generic_bounds.into_iter().enumerate().map(move |(idx, (clause, span))| Obligation {
cause: cause(idx, span),
recursion_depth: 0,
param_env,
- predicate,
+ predicate: clause.as_predicate(),
})
}
@@ -161,7 +160,7 @@ fn pred_known_to_hold_modulo_regions<'tcx>(
// the we do no inference in the process of checking this obligation.
let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
infcx.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error();
@@ -185,8 +184,8 @@ fn do_normalize_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
cause: ObligationCause<'tcx>,
elaborated_env: ty::ParamEnv<'tcx>,
- predicates: Vec<ty::Predicate<'tcx>>,
-) -> Result<Vec<ty::Predicate<'tcx>>, ErrorGuaranteed> {
+ predicates: Vec<ty::Clause<'tcx>>,
+) -> Result<Vec<ty::Clause<'tcx>>, ErrorGuaranteed> {
let span = cause.span;
// FIXME. We should really... do something with these region
// obligations. But this call just continues the older
@@ -270,13 +269,67 @@ pub fn normalize_param_env_or_error<'tcx>(
// parameter environments once for every fn as it goes,
// 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(tcx, unnormalized_env.caller_bounds().into_iter()).collect();
+ let mut predicates: Vec<_> = util::elaborate(
+ tcx,
+ unnormalized_env.caller_bounds().into_iter().map(|predicate| {
+ if tcx.features().generic_const_exprs {
+ return predicate;
+ }
+
+ struct ConstNormalizer<'tcx>(TyCtxt<'tcx>);
+
+ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ConstNormalizer<'tcx> {
+ fn interner(&self) -> TyCtxt<'tcx> {
+ self.0
+ }
+
+ fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ // While it is pretty sus to be evaluating things with an empty param env, it
+ // should actually be okay since without `feature(generic_const_exprs)` the only
+ // const arguments that have a non-empty param env are array repeat counts. These
+ // do not appear in the type system though.
+ c.eval(self.0, ty::ParamEnv::empty())
+ }
+ }
+
+ // This whole normalization step is a hack to work around the fact that
+ // `normalize_param_env_or_error` is fundamentally broken from using an
+ // unnormalized param env with a trait solver that expects the param env
+ // to be normalized.
+ //
+ // When normalizing the param env we can end up evaluating obligations
+ // that have been normalized but can only be proven via a where clause
+ // which is still in its unnormalized form. example:
+ //
+ // Attempting to prove `T: Trait<<u8 as Identity>::Assoc>` in a param env
+ // with a `T: Trait<<u8 as Identity>::Assoc>` where clause will fail because
+ // we first normalize obligations before proving them so we end up proving
+ // `T: Trait<u8>`. Since lazy normalization is not implemented equating `u8`
+ // with `<u8 as Identity>::Assoc` fails outright so we incorrectly believe that
+ // we cannot prove `T: Trait<u8>`.
+ //
+ // The same thing is true for const generics- attempting to prove
+ // `T: Trait<ConstKind::Unevaluated(...)>` with the same thing as a where clauses
+ // will fail. After normalization we may be attempting to prove `T: Trait<4>` with
+ // the unnormalized where clause `T: Trait<ConstKind::Unevaluated(...)>`. In order
+ // for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)`
+ // but as we do not have lazy norm implemented, equating the two consts fails outright.
+ //
+ // Ideally we would not normalize consts here at all but it is required for backwards
+ // compatibility. Eventually when lazy norm is implemented this can just be removed.
+ // We do not normalize types here as there is no backwards compatibility requirement
+ // for us to do so.
+ //
+ // FIXME(-Ztrait-solver=next): remove this hack since we have deferred projection equality
+ predicate.fold_with(&mut ConstNormalizer(tcx))
+ }),
+ )
+ .collect();
debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
let elaborated_env = ty::ParamEnv::new(
- tcx.mk_predicates(&predicates),
+ tcx.mk_clauses(&predicates),
unnormalized_env.reveal(),
unnormalized_env.constness(),
);
@@ -300,11 +353,8 @@ pub fn normalize_param_env_or_error<'tcx>(
// This works fairly well because trait matching does not actually care about param-env
// TypeOutlives predicates - these are normally used by regionck.
let outlives_predicates: Vec<_> = predicates
- .drain_filter(|predicate| {
- matches!(
- predicate.kind().skip_binder(),
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
- )
+ .extract_if(|predicate| {
+ matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..))
})
.collect();
@@ -330,7 +380,7 @@ pub fn normalize_param_env_or_error<'tcx>(
// predicates here anyway. Keeping them here anyway because it seems safer.
let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned();
let outlives_env = ty::ParamEnv::new(
- tcx.mk_predicates_from_iter(outlives_env),
+ tcx.mk_clauses_from_iter(outlives_env),
unnormalized_env.reveal(),
unnormalized_env.constness(),
);
@@ -350,13 +400,18 @@ pub fn normalize_param_env_or_error<'tcx>(
predicates.extend(outlives_predicates);
debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
ty::ParamEnv::new(
- tcx.mk_predicates(&predicates),
+ tcx.mk_clauses(&predicates),
unnormalized_env.reveal(),
unnormalized_env.constness(),
)
}
-/// Normalize a type and process all resulting obligations, returning any errors
+/// Normalize a type and process all resulting obligations, returning any errors.
+///
+/// FIXME(-Ztrait-solver=next): This should be replaced by `At::deeply_normalize`
+/// which has the same behavior with the new solver. Because using a separate
+/// fulfillment context worsens caching in the old solver, `At::deeply_normalize`
+/// is still lazy with the old solver as it otherwise negatively impacts perf.
#[instrument(skip_all)]
pub fn fully_normalize<'tcx, T>(
infcx: &InferCtxt<'tcx>,
@@ -385,10 +440,7 @@ where
/// 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.
-pub fn impossible_predicates<'tcx>(
- tcx: TyCtxt<'tcx>,
- predicates: Vec<ty::Predicate<'tcx>>,
-) -> bool {
+pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec<ty::Clause<'tcx>>) -> bool {
debug!("impossible_predicates(predicates={:?})", predicates);
let infcx = tcx.infer_ctxt().build();
@@ -485,7 +537,7 @@ fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefI
tcx,
ObligationCause::dummy_with_span(*span),
param_env,
- ty::EarlyBinder(*pred).subst(tcx, impl_trait_ref.substs),
+ ty::EarlyBinder::bind(*pred).subst(tcx, impl_trait_ref.substs),
)
})
});
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index c81bf6ebc..c31944c16 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -101,7 +101,7 @@ pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::A
debug_assert!(tcx.generics_of(trait_def_id).has_self);
debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method);
// Any method that has a `Self: Sized` bound cannot be called.
- if generics_require_sized_self(tcx, method.def_id) {
+ if tcx.generics_require_sized_self(method.def_id) {
return false;
}
@@ -115,15 +115,11 @@ fn object_safety_violations_for_trait(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
) -> Vec<ObjectSafetyViolation> {
- // Check methods for violations.
+ // Check assoc items for violations.
let mut violations: Vec<_> = tcx
.associated_items(trait_def_id)
.in_definition_order()
- .filter(|item| item.kind == ty::AssocKind::Fn)
- .filter_map(|&item| {
- object_safety_violation_for_method(tcx, trait_def_id, item)
- .map(|(code, span)| ObjectSafetyViolation::Method(item.name, code, span))
- })
+ .filter_map(|&item| object_safety_violation_for_assoc_item(tcx, trait_def_id, item))
.collect();
// Check the trait itself.
@@ -145,30 +141,6 @@ fn object_safety_violations_for_trait(
violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans));
}
- violations.extend(
- tcx.associated_items(trait_def_id)
- .in_definition_order()
- .filter(|item| item.kind == ty::AssocKind::Const)
- .map(|item| {
- let ident = item.ident(tcx);
- ObjectSafetyViolation::AssocConst(ident.name, ident.span)
- }),
- );
-
- if !tcx.features().generic_associated_types_extended {
- violations.extend(
- tcx.associated_items(trait_def_id)
- .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)
- }),
- );
- }
-
debug!(
"object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
trait_def_id, violations
@@ -299,22 +271,22 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Type)
.flat_map(|item| tcx.explicit_item_bounds(item.def_id).subst_identity_iter_copied())
- .filter_map(|pred_span| predicate_references_self(tcx, pred_span))
+ .filter_map(|c| predicate_references_self(tcx, c))
.collect()
}
fn predicate_references_self<'tcx>(
tcx: TyCtxt<'tcx>,
- (predicate, sp): (ty::Predicate<'tcx>, Span),
+ (predicate, sp): (ty::Clause<'tcx>, Span),
) -> Option<Span> {
let self_ty = tcx.types.self_param;
let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into());
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(ref data)) => {
+ ty::ClauseKind::Trait(ref data) => {
// In the case of a trait predicate, we can skip the "self" type.
data.trait_ref.substs[1..].iter().any(has_self_ty).then_some(sp)
}
- ty::PredicateKind::Clause(ty::Clause::Projection(ref data)) => {
+ ty::ClauseKind::Projection(ref data) => {
// And similarly for projections. This should be redundant with
// the previous check because any projection should have a
// matching `Trait` predicate with the same inputs, but we do
@@ -332,24 +304,14 @@ fn predicate_references_self<'tcx>(
// possible alternatives.
data.projection_ty.substs[1..].iter().any(has_self_ty).then_some(sp)
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(_ct, ty)) => {
- has_self_ty(&ty.into()).then_some(sp)
- }
-
- ty::PredicateKind::AliasRelate(..) => bug!("`AliasRelate` not allowed as assumption"),
+ ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp),
- ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
- | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
+ ty::ClauseKind::WellFormed(..)
+ | ty::ClauseKind::TypeOutlives(..)
+ | ty::ClauseKind::RegionOutlives(..)
// FIXME(generic_const_exprs): this can mention `Self`
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
+ | ty::ClauseKind::ConstEvaluatable(..)
+ => None,
}
}
@@ -369,7 +331,7 @@ fn super_predicates_have_non_lifetime_binders(
}
fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
- generics_require_sized_self(tcx, trait_def_id)
+ tcx.generics_require_sized_self(trait_def_id)
}
fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
@@ -381,54 +343,66 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let predicates = tcx.predicates_of(def_id);
let predicates = predicates.instantiate_identity(tcx).predicates;
elaborate(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(ref trait_pred)) => {
+ ty::ClauseKind::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,
+ ty::ClauseKind::RegionOutlives(_)
+ | ty::ClauseKind::TypeOutlives(_)
+ | ty::ClauseKind::Projection(_)
+ | ty::ClauseKind::ConstArgHasType(_, _)
+ | ty::ClauseKind::WellFormed(_)
+ | ty::ClauseKind::ConstEvaluatable(_) => false,
})
}
-/// Returns `Some(_)` if this method makes the containing trait not object safe.
-fn object_safety_violation_for_method(
+/// Returns `Some(_)` if this item makes the containing trait not object safe.
+#[instrument(level = "debug", skip(tcx), ret)]
+fn object_safety_violation_for_assoc_item(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
- method: ty::AssocItem,
-) -> Option<(MethodViolationCode, Span)> {
- debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method);
- // Any method that has a `Self : Sized` requisite is otherwise
+ item: ty::AssocItem,
+) -> Option<ObjectSafetyViolation> {
+ // Any item that has a `Self : Sized` requisite is otherwise
// exempt from the regulations.
- if generics_require_sized_self(tcx, method.def_id) {
+ if tcx.generics_require_sized_self(item.def_id) {
return None;
}
- let violation = virtual_call_violation_for_method(tcx, trait_def_id, method);
- // Get an accurate span depending on the violation.
- violation.map(|v| {
- let node = tcx.hir().get_if_local(method.def_id);
- let span = match (&v, node) {
- (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span,
- (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span,
- (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span,
- (MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
- node.fn_decl().map_or(method.ident(tcx).span, |decl| decl.output.span())
+ match item.kind {
+ // Associated consts are never object safe, as they can't have `where` bounds yet at all,
+ // and associated const bounds in trait objects aren't a thing yet either.
+ ty::AssocKind::Const => {
+ Some(ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span))
+ }
+ ty::AssocKind::Fn => virtual_call_violation_for_method(tcx, trait_def_id, item).map(|v| {
+ let node = tcx.hir().get_if_local(item.def_id);
+ // Get an accurate span depending on the violation.
+ let span = match (&v, node) {
+ (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span,
+ (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span,
+ (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span,
+ (MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
+ node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span())
+ }
+ _ => item.ident(tcx).span,
+ };
+
+ ObjectSafetyViolation::Method(item.name, v, span)
+ }),
+ // Associated types can only be object safe if they have `Self: Sized` bounds.
+ ty::AssocKind::Type => {
+ if !tcx.features().generic_associated_types_extended
+ && !tcx.generics_of(item.def_id).params.is_empty()
+ && item.opt_rpitit_info.is_none()
+ {
+ Some(ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span))
+ } else {
+ // We will permit associated types if they are explicitly mentioned in the trait object.
+ // We can't check this here, as here we only check if it is guaranteed to not be possible.
+ None
}
- _ => method.ident(tcx).span,
- };
- (v, span)
- })
+ }
+ }
}
/// Returns `Some(_)` if this method cannot be called on a trait
@@ -526,7 +500,7 @@ fn virtual_call_violation_for_method<'tcx>(
// #78372
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
- format!("error: {}\n while computing layout for type {:?}", err, ty),
+ format!("error: {err}\n while computing layout for type {ty:?}"),
);
None
}
@@ -535,7 +509,7 @@ fn virtual_call_violation_for_method<'tcx>(
// e.g., `Rc<()>`
let unit_receiver_ty =
- receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id);
+ receiver_for_self_ty(tcx, receiver_ty, Ty::new_unit(tcx), method.def_id);
match abi_of_ty(unit_receiver_ty) {
Some(Abi::Scalar(..)) => (),
@@ -583,7 +557,7 @@ fn virtual_call_violation_for_method<'tcx>(
// because a trait object can't claim to live longer than the concrete
// type. If the lifetime bound holds on dyn Trait then it's guaranteed
// to hold as well on the concrete type.
- if pred.to_opt_type_outlives().is_some() {
+ if pred.as_type_outlives_clause().is_some() {
return false;
}
@@ -600,11 +574,11 @@ fn virtual_call_violation_for_method<'tcx>(
// only if the autotrait is one of the trait object's trait bounds, like
// in `dyn Trait + AutoTrait`. This guarantees that trait objects only
// implement auto traits if the underlying type does as well.
- if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
+ if let ty::ClauseKind::Trait(ty::TraitPredicate {
trait_ref: pred_trait_ref,
constness: ty::BoundConstness::NotConst,
polarity: ty::ImplPolarity::Positive,
- })) = pred.kind().skip_binder()
+ }) = pred.kind().skip_binder()
&& pred_trait_ref.self_ty() == tcx.types.self_param
&& tcx.trait_is_auto(pred_trait_ref.def_id)
{
@@ -642,7 +616,7 @@ fn receiver_for_self_ty<'tcx>(
if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) }
});
- let result = EarlyBinder(receiver_ty).subst(tcx, substs);
+ let result = EarlyBinder::bind(receiver_ty).subst(tcx, substs);
debug!(
"receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}",
receiver_ty, self_ty, method_def_id, result
@@ -690,7 +664,7 @@ fn object_ty_for_trait<'tcx>(
);
debug!(?existential_predicates);
- tcx.mk_dynamic(existential_predicates, lifetime, ty::Dyn)
+ Ty::new_dynamic(tcx, existential_predicates, lifetime, ty::Dyn)
}
/// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a
@@ -758,7 +732,7 @@ fn receiver_is_dispatchable<'tcx>(
// FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can
// replace this with `dyn Trait`
let unsized_self_ty: Ty<'tcx> =
- tcx.mk_ty_param(u32::MAX, Symbol::intern("RustaceansAreAwesome"));
+ Ty::new_param(tcx, u32::MAX, Symbol::intern("RustaceansAreAwesome"));
// `Receiver[Self => U]`
let unsized_receiver_ty =
@@ -772,7 +746,6 @@ fn receiver_is_dispatchable<'tcx>(
// Self: Unsize<U>
let unsize_predicate =
ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty])
- .without_const()
.to_predicate(tcx);
// U: Trait<Arg1, ..., ArgN>
@@ -789,7 +762,7 @@ fn receiver_is_dispatchable<'tcx>(
param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]);
ty::ParamEnv::new(
- tcx.mk_predicates_from_iter(caller_bounds),
+ tcx.mk_clauses_from_iter(caller_bounds),
param_env.reveal(),
param_env.constness(),
)
@@ -949,5 +922,10 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>(
}
pub fn provide(providers: &mut Providers) {
- *providers = Providers { object_safety_violations, check_is_object_safe, ..*providers };
+ *providers = Providers {
+ object_safety_violations,
+ check_is_object_safe,
+ generics_require_sized_self,
+ ..*providers
+ };
}
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index f8d056e32..ae6fc7cf8 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -9,7 +9,7 @@ use rustc_span::def_id::LocalDefId;
pub use rustc_middle::traits::query::OutlivesBound;
-type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
+pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
pub trait InferCtxtExt<'a, 'tcx> {
fn implied_outlives_bounds(
&self,
@@ -57,16 +57,12 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
// We do not expect existential variables in implied bounds.
- // We may however encounter unconstrained lifetime variables in invalid
- // code. See #110161 for context.
+ // We may however encounter unconstrained lifetime variables
+ // in very rare cases.
+ //
+ // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for
+ // an example.
assert!(!ty.has_non_region_infer());
- if ty.has_infer() {
- self.tcx.sess.delay_span_bug(
- self.tcx.def_span(body_id),
- "skipped implied_outlives_bounds due to unconstrained lifetimes",
- );
- return vec![];
- }
let mut canonical_var_values = OriginalQueryValues::default();
let canonical_ty =
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 510698971..a10bca31f 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1,8 +1,10 @@
//! Code for projecting associated types out of trait references.
+use super::check_substs_compatible;
use super::specialization_graph;
use super::translate_substs;
use super::util;
+use super::ImplSourceUserDefinedData;
use super::MismatchedProjectionTypes;
use super::Obligation;
use super::ObligationCause;
@@ -10,10 +12,6 @@ use super::PredicateObligation;
use super::Selection;
use super::SelectionContext;
use super::SelectionError;
-use super::{
- ImplSourceClosureData, ImplSourceFnPointerData, ImplSourceFutureData, ImplSourceGeneratorData,
- ImplSourceUserDefinedData,
-};
use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
use crate::errors::InherentProjectionNormalizationOverflow;
@@ -30,7 +28,9 @@ 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_infer::traits::FulfillmentError;
+use rustc_infer::traits::ObligationCauseCode;
+use rustc_infer::traits::TraitEngine;
use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt};
@@ -55,14 +55,55 @@ pub trait NormalizeExt<'tcx> {
/// This normalization should be used when the type contains inference variables or the
/// projection may be fallible.
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> InferOk<'tcx, T>;
+
+ /// Deeply normalizes `value`, replacing all aliases which can by normalized in
+ /// the current environment. In the new solver this errors in case normalization
+ /// fails or is ambiguous. This only normalizes opaque types with `Reveal::All`.
+ ///
+ /// In the old solver this simply uses `normalizes` and adds the nested obligations
+ /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the
+ /// same goals in both a temporary and the shared context which negatively impacts
+ /// performance as these don't share caching.
+ ///
+ /// FIXME(-Ztrait-solver=next): This has the same behavior as `traits::fully_normalize`
+ /// in the new solver, but because of performance reasons, we currently reuse an
+ /// existing fulfillment context in the old solver. Once we also eagerly prove goals with
+ /// the old solver or have removed the old solver, remove `traits::fully_normalize` and
+ /// rename this function to `At::fully_normalize`.
+ fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+ self,
+ value: T,
+ fulfill_cx: &mut dyn TraitEngine<'tcx>,
+ ) -> Result<T, Vec<FulfillmentError<'tcx>>>;
}
impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> {
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
- let mut selcx = SelectionContext::new(self.infcx);
- let Normalized { value, obligations } =
- normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
- InferOk { value, obligations }
+ if self.infcx.next_trait_solver() {
+ InferOk { value, obligations: Vec::new() }
+ } else {
+ let mut selcx = SelectionContext::new(self.infcx);
+ let Normalized { value, obligations } =
+ normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
+ InferOk { value, obligations }
+ }
+ }
+
+ fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+ self,
+ value: T,
+ fulfill_cx: &mut dyn TraitEngine<'tcx>,
+ ) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+ if self.infcx.next_trait_solver() {
+ crate::solve::deeply_normalize(self, value)
+ } else {
+ let value = self
+ .normalize(value)
+ .into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
+ let errors = fulfill_cx.select_where_possible(self.infcx);
+ let value = self.infcx.resolve_vars_if_possible(value);
+ if errors.is_empty() { Ok(value) } else { Err(errors) }
+ }
}
}
@@ -406,6 +447,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
depth: usize,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
+ debug_assert!(!selcx.infcx.next_trait_solver());
AssocTypeNormalizer {
selcx,
param_env,
@@ -502,10 +544,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
// to make sure we don't forget to fold the substs regardless.
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::Opaque if !data.substs.has_escaping_bound_vars() => {
+ ty::Opaque => {
// Only normalize `impl Trait` outside of type inference, usually in codegen.
match self.param_env.reveal() {
Reveal::UserFacing => ty.super_fold_with(self),
@@ -531,7 +570,6 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
}
}
}
- ty::Opaque => ty.super_fold_with(self),
ty::Projection if !data.has_escaping_bound_vars() => {
// This branch is *mostly* just an optimization: when we don't
@@ -621,6 +659,30 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
);
normalized_ty
}
+ ty::Weak => {
+ let infcx = self.selcx.infcx;
+ self.obligations.extend(
+ infcx
+ .tcx
+ .predicates_of(data.def_id)
+ .instantiate_own(infcx.tcx, data.substs)
+ .map(|(mut predicate, span)| {
+ if data.has_escaping_bound_vars() {
+ (predicate, ..) = BoundVarReplacer::replace_bound_vars(
+ infcx,
+ &mut self.universes,
+ predicate,
+ );
+ }
+ let mut cause = self.cause.clone();
+ cause.map_code(|code| {
+ ObligationCauseCode::TypeAlias(code, span, data.def_id)
+ });
+ Obligation::new(infcx.tcx, cause, self.param_env, predicate)
+ }),
+ );
+ infcx.tcx.type_of(data.def_id).subst(infcx.tcx, data.substs).fold_with(self)
+ }
ty::Inherent if !data.has_escaping_bound_vars() => {
// This branch is *mostly* just an optimization: when we don't
@@ -672,7 +734,9 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
#[instrument(skip(self), level = "debug")]
fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
let tcx = self.selcx.tcx();
- if tcx.lazy_normalization() || !needs_normalization(&constant, self.param_env.reveal()) {
+ if tcx.features().generic_const_exprs
+ || !needs_normalization(&constant, self.param_env.reveal())
+ {
constant
} else {
let constant = constant.super_fold_with(self);
@@ -824,7 +888,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderRegion { universe, bound: br };
self.mapped_regions.insert(p, br);
- self.infcx.tcx.mk_re_placeholder(p)
+ ty::Region::new_placeholder(self.infcx.tcx, p)
}
_ => r,
}
@@ -842,7 +906,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderType { universe, bound: bound_ty };
self.mapped_types.insert(p, bound_ty);
- self.infcx.tcx.mk_placeholder(p)
+ Ty::new_placeholder(self.infcx.tcx, p)
}
_ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
_ => t,
@@ -861,7 +925,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderConst { universe, bound: bound_const };
self.mapped_consts.insert(p, bound_const);
- self.infcx.tcx.mk_const(p, ct.ty())
+ ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty())
}
_ => ct.super_fold_with(self),
}
@@ -945,7 +1009,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
let db = ty::DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
- self.interner().mk_re_late_bound(db, *replace_var)
+ ty::Region::new_late_bound(self.interner(), db, *replace_var)
}
None => r1,
}
@@ -972,7 +1036,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
let db = ty::DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
- self.interner().mk_bound(db, *replace_var)
+ Ty::new_bound(self.infcx.tcx, db, *replace_var)
}
None => ty,
}
@@ -996,7 +1060,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
let db = ty::DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
- self.interner().mk_const(ty::ConstKind::Bound(db, *replace_var), ct.ty())
+ ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty())
}
None => ct,
}
@@ -1059,6 +1123,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> Result<Option<Term<'tcx>>, InProgress> {
let infcx = selcx.infcx;
+ debug_assert!(!selcx.infcx.next_trait_solver());
// Don't use the projection cache in intercrate mode -
// the `infcx` may be re-used between intercrate in non-intercrate
// mode, which could lead to using incorrect cache results.
@@ -1168,11 +1233,11 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
};
let mut deduped: SsoHashSet<_> = Default::default();
- result.obligations.drain_filter(|projected_obligation| {
+ result.obligations.retain(|projected_obligation| {
if !deduped.insert(projected_obligation.clone()) {
- return true;
+ return false;
}
- false
+ true
});
if use_cache {
@@ -1375,7 +1440,7 @@ struct Progress<'tcx> {
impl<'tcx> Progress<'tcx> {
fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
- Progress { term: tcx.ty_error(guar).into(), obligations: vec![] }
+ Progress { term: Ty::new_error(tcx, guar).into(), obligations: vec![] }
}
fn with_addl_obligations(mut self, mut obligations: Vec<PredicateObligation<'tcx>>) -> Self {
@@ -1434,19 +1499,22 @@ fn project<'cx, 'tcx>(
ProjectionCandidateSet::None => {
let tcx = selcx.tcx();
let term = match tcx.def_kind(obligation.predicate.def_id) {
- DefKind::AssocTy | DefKind::ImplTraitPlaceholder => tcx
- .mk_projection(obligation.predicate.def_id, obligation.predicate.substs)
- .into(),
- DefKind::AssocConst => tcx
- .mk_const(
- ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(
- obligation.predicate.def_id,
- obligation.predicate.substs,
- )),
- tcx.type_of(obligation.predicate.def_id)
- .subst(tcx, obligation.predicate.substs),
- )
- .into(),
+ DefKind::AssocTy | DefKind::ImplTraitPlaceholder => Ty::new_projection(
+ tcx,
+ obligation.predicate.def_id,
+ obligation.predicate.substs,
+ )
+ .into(),
+ DefKind::AssocConst => ty::Const::new_unevaluated(
+ tcx,
+ ty::UnevaluatedConst::new(
+ obligation.predicate.def_id,
+ obligation.predicate.substs,
+ ),
+ tcx.type_of(obligation.predicate.def_id)
+ .subst(tcx, obligation.predicate.substs),
+ )
+ .into(),
kind => {
bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id))
}
@@ -1477,7 +1545,6 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
let trait_def_id = tcx.parent(trait_fn_def_id);
let trait_substs =
obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id));
- // FIXME(named-returns): Binders
let trait_predicate = ty::TraitRef::new(tcx, trait_def_id, trait_substs);
let _ = selcx.infcx.commit_if_ok(|_| {
@@ -1543,7 +1610,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
// Check whether the self-type is itself a projection.
// If so, extract what we know from the trait and try to come up with a good answer.
let bounds = match *obligation.predicate.self_ty().kind() {
- // Excluding IATs here as they don't have meaningful item bounds.
+ // Excluding IATs and type aliases here as they don't have meaningful item bounds.
ty::Alias(ty::Projection | ty::Opaque, ref data) => {
tcx.item_bounds(data.def_id).subst(tcx, data.substs)
}
@@ -1584,6 +1651,10 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
let tcx = selcx.tcx();
+ if !tcx.trait_def(obligation.predicate.trait_def_id(tcx)).implement_via_object {
+ return;
+ }
+
let self_ty = obligation.predicate.self_ty();
let object_ty = selcx.infcx.shallow_resolve(self_ty);
let data = match object_ty.kind() {
@@ -1620,15 +1691,13 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionCandidateSet<'tcx>,
ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionCandidate<'tcx>,
- env_predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
+ env_predicates: impl Iterator<Item = ty::Clause<'tcx>>,
potentially_unnormalized_candidates: bool,
) {
let infcx = selcx.infcx;
for predicate in env_predicates {
let bound_predicate = predicate.kind();
- if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) =
- predicate.kind().skip_binder()
- {
+ if let ty::ClauseKind::Projection(data) = predicate.kind().skip_binder() {
let data = bound_predicate.rebind(data);
if data.projection_def_id() != obligation.predicate.def_id {
continue;
@@ -1677,8 +1746,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// If we are resolving `<T as TraitRef<...>>::Item == Type`,
// start out by selecting the predicate `T as TraitRef<...>`:
- let poly_trait_ref = ty::Binder::dummy(obligation.predicate.trait_ref(selcx.tcx()));
- let trait_obligation = obligation.with(selcx.tcx(), poly_trait_ref);
+ let trait_ref = obligation.predicate.trait_ref(selcx.tcx());
+ let trait_obligation = obligation.with(selcx.tcx(), trait_ref);
let _ = selcx.infcx.commit_if_ok(|_| {
let impl_source = match selcx.select(&trait_obligation) {
Ok(Some(impl_source)) => impl_source,
@@ -1694,11 +1763,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
};
let eligible = match &impl_source {
- super::ImplSource::Closure(_)
- | super::ImplSource::Generator(_)
- | super::ImplSource::Future(_)
- | super::ImplSource::FnPointer(_)
- | super::ImplSource::TraitAlias(_) => true,
super::ImplSource::UserDefined(impl_data) => {
// We have to be careful when projecting out of an
// impl because of specialization. If we are not in
@@ -1737,7 +1801,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
if obligation.param_env.reveal() == Reveal::All {
// NOTE(eddyb) inference variables can resolve to parameters, so
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
- let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(poly_trait_ref);
+ let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(trait_ref);
!poly_trait_ref.still_further_specializable()
} else {
debug!(
@@ -1756,7 +1820,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let lang_items = selcx.tcx().lang_items();
- if lang_items.discriminant_kind_trait() == Some(poly_trait_ref.def_id()) {
+ if [lang_items.gen_trait(), lang_items.future_trait()].contains(&Some(trait_ref.def_id))
+ || selcx.tcx().fn_trait_kind_from_def_id(trait_ref.def_id).is_some()
+ {
+ true
+ } else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) {
match self_ty.kind() {
ty::Bool
| ty::Char
@@ -1791,7 +1859,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
| ty::Infer(..)
| ty::Error(_) => false,
}
- } else if lang_items.pointee_trait() == Some(poly_trait_ref.def_id()) {
+ } else if lang_items.pointee_trait() == Some(trait_ref.def_id) {
let tail = selcx.tcx().struct_tail_with_normalize(
self_ty,
|ty| {
@@ -1866,7 +1934,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
}
}
} else {
- bug!("unexpected builtin trait with associated type: {poly_trait_ref:?}")
+ bug!("unexpected builtin trait with associated type: {trait_ref:?}")
}
}
super::ImplSource::Param(..) => {
@@ -1903,9 +1971,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// why we special case object types.
false
}
- super::ImplSource::AutoImpl(..)
- | super::ImplSource::TraitUpcasting(_)
- | super::ImplSource::ConstDestruct(_) => {
+ | super::ImplSource::TraitUpcasting(_) => {
// These traits have no associated types.
selcx.tcx().sess.delay_span_bug(
obligation.cause.span,
@@ -1969,17 +2035,26 @@ fn confirm_select_candidate<'cx, 'tcx>(
) -> Progress<'tcx> {
match impl_source {
super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
- super::ImplSource::Generator(data) => confirm_generator_candidate(selcx, obligation, data),
- super::ImplSource::Future(data) => confirm_future_candidate(selcx, obligation, data),
- super::ImplSource::Closure(data) => confirm_closure_candidate(selcx, obligation, data),
- super::ImplSource::FnPointer(data) => confirm_fn_pointer_candidate(selcx, obligation, data),
- super::ImplSource::Builtin(data) => confirm_builtin_candidate(selcx, obligation, data),
+ super::ImplSource::Builtin(data) => {
+ let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx());
+ let lang_items = selcx.tcx().lang_items();
+ if lang_items.gen_trait() == Some(trait_def_id) {
+ confirm_generator_candidate(selcx, obligation, data)
+ } else if lang_items.future_trait() == Some(trait_def_id) {
+ confirm_future_candidate(selcx, obligation, data)
+ } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() {
+ if obligation.predicate.self_ty().is_closure() {
+ confirm_closure_candidate(selcx, obligation, data)
+ } else {
+ confirm_fn_pointer_candidate(selcx, obligation, data)
+ }
+ } else {
+ confirm_builtin_candidate(selcx, obligation, data)
+ }
+ }
super::ImplSource::Object(_)
- | super::ImplSource::AutoImpl(..)
| super::ImplSource::Param(..)
- | super::ImplSource::TraitUpcasting(_)
- | super::ImplSource::TraitAlias(..)
- | super::ImplSource::ConstDestruct(_) => {
+ | super::ImplSource::TraitUpcasting(_) => {
// we don't create Select candidates with this kind of resolution
span_bug!(
obligation.cause.span,
@@ -1993,9 +2068,14 @@ fn confirm_select_candidate<'cx, 'tcx>(
fn confirm_generator_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- impl_source: ImplSourceGeneratorData<'tcx, PredicateObligation<'tcx>>,
+ nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let gen_sig = impl_source.substs.as_generator().poly_sig();
+ let ty::Generator(_, substs, _) =
+ selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
+ else {
+ unreachable!()
+ };
+ let gen_sig = substs.as_generator().poly_sig();
let Normalized { value: gen_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@@ -2033,16 +2113,21 @@ fn confirm_generator_candidate<'cx, 'tcx>(
});
confirm_param_env_candidate(selcx, obligation, predicate, false)
- .with_addl_obligations(impl_source.nested)
+ .with_addl_obligations(nested)
.with_addl_obligations(obligations)
}
fn confirm_future_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- impl_source: ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>,
+ nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let gen_sig = impl_source.substs.as_generator().poly_sig();
+ let ty::Generator(_, substs, _) =
+ selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
+ else {
+ unreachable!()
+ };
+ let gen_sig = substs.as_generator().poly_sig();
let Normalized { value: gen_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@@ -2072,14 +2157,14 @@ fn confirm_future_candidate<'cx, 'tcx>(
});
confirm_param_env_candidate(selcx, obligation, predicate, false)
- .with_addl_obligations(impl_source.nested)
+ .with_addl_obligations(nested)
.with_addl_obligations(obligations)
}
fn confirm_builtin_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- data: ImplSourceBuiltinData<PredicateObligation<'tcx>>,
+ data: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
let tcx = selcx.tcx();
let self_ty = obligation.predicate.self_ty();
@@ -2127,15 +2212,15 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
.with_addl_obligations(obligations)
- .with_addl_obligations(data.nested)
+ .with_addl_obligations(data)
}
fn confirm_fn_pointer_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- fn_pointer_impl_source: ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>,
+ nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let fn_type = selcx.infcx.shallow_resolve(fn_pointer_impl_source.fn_ty);
+ let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let sig = fn_type.fn_sig(selcx.tcx());
let Normalized { value: sig, obligations } = normalize_with_depth(
selcx,
@@ -2146,16 +2231,21 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
);
confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes)
- .with_addl_obligations(fn_pointer_impl_source.nested)
+ .with_addl_obligations(nested)
.with_addl_obligations(obligations)
}
fn confirm_closure_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- impl_source: ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>,
+ nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let closure_sig = impl_source.substs.as_closure().sig();
+ let ty::Closure(_, substs) =
+ selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
+ else {
+ unreachable!()
+ };
+ let closure_sig = substs.as_closure().sig();
let Normalized { value: closure_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@@ -2167,7 +2257,7 @@ fn confirm_closure_candidate<'cx, 'tcx>(
debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate");
confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No)
- .with_addl_obligations(impl_source.nested)
+ .with_addl_obligations(nested)
.with_addl_obligations(obligations)
}
@@ -2263,7 +2353,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
obligation, poly_cache_entry, e,
);
debug!("confirm_param_env_candidate: {}", msg);
- let err = infcx.tcx.ty_error_with_message(obligation.cause.span, msg);
+ let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg);
Progress { term: err.into(), obligations: vec![] }
}
}
@@ -2295,7 +2385,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
"confirm_impl_candidate: no associated type {:?} for {:?}",
assoc_ty.item.name, obligation.predicate
);
- return Progress { term: tcx.ty_error_misc().into(), obligations: nested };
+ return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested };
}
// If we're trying to normalize `<Vec<u32> as X>::A<S>` using
//`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
@@ -2311,13 +2401,14 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
let did = assoc_ty.item.def_id;
let identity_substs = crate::traits::InternalSubsts::identity_for_item(tcx, did);
- let kind = ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs));
- ty.map_bound(|ty| tcx.mk_const(kind, ty).into())
+ let uv = ty::UnevaluatedConst::new(did, identity_substs);
+ ty.map_bound(|ty| ty::Const::new_unevaluated(tcx, uv, ty).into())
} else {
ty.map_bound(|ty| ty.into())
};
if !check_substs_compatible(tcx, assoc_ty.item, substs) {
- let err = tcx.ty_error_with_message(
+ let err = Ty::new_error_with_message(
+ tcx,
obligation.cause.span,
"impl item and trait item have different parameters",
);
@@ -2328,47 +2419,6 @@ fn confirm_impl_candidate<'cx, 'tcx>(
}
}
-// Verify that the trait item and its implementation have compatible substs lists
-fn check_substs_compatible<'tcx>(
- tcx: TyCtxt<'tcx>,
- assoc_item: ty::AssocItem,
- substs: ty::SubstsRef<'tcx>,
-) -> bool {
- fn check_substs_compatible_inner<'tcx>(
- tcx: TyCtxt<'tcx>,
- generics: &'tcx ty::Generics,
- args: &'tcx [ty::GenericArg<'tcx>],
- ) -> bool {
- if generics.count() != args.len() {
- return false;
- }
-
- let (parent_args, own_args) = args.split_at(generics.parent_count);
-
- if let Some(parent) = generics.parent
- && let parent_generics = tcx.generics_of(parent)
- && !check_substs_compatible_inner(tcx, parent_generics, parent_args) {
- return false;
- }
-
- for (param, arg) in std::iter::zip(&generics.params, own_args) {
- match (&param.kind, arg.unpack()) {
- (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
- | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
- | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
- _ => return false,
- }
- }
-
- true
- }
-
- let generics = tcx.generics_of(assoc_item.def_id);
- // Chop off any additional substs (RPITIT) substs
- let substs = &substs[0..generics.count().min(substs.len())];
- check_substs_compatible_inner(tcx, generics, substs)
-}
-
fn confirm_impl_trait_in_trait_candidate<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
@@ -2385,13 +2435,14 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
// We don't support specialization for RPITITs anyways... yet.
// 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 };
+ return Progress { term: Ty::new_misc_error(tcx).into(), obligations };
}
// Use the default `impl Trait` for the trait, e.g., for a default trait body
if leaf_def.item.container == ty::AssocItemContainer::TraitContainer {
return Progress {
- term: tcx.mk_opaque(obligation.predicate.def_id, obligation.predicate.substs).into(),
+ term: Ty::new_opaque(tcx, obligation.predicate.def_id, obligation.predicate.substs)
+ .into(),
obligations,
};
}
@@ -2409,7 +2460,8 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
);
if !check_substs_compatible(tcx, leaf_def.item, impl_fn_substs) {
- let err = tcx.ty_error_with_message(
+ let err = Ty::new_error_with_message(
+ tcx,
obligation.cause.span,
"impl method and trait method have different parameters",
);
@@ -2455,7 +2507,7 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
cause.clone(),
obligation.recursion_depth + 1,
tcx.collect_return_position_impl_trait_in_trait_tys(impl_fn_def_id).map_or_else(
- |guar| tcx.ty_error(guar),
+ |guar| Ty::new_error(tcx, guar),
|tys| tys[&obligation.predicate.def_id].subst(tcx, impl_fn_substs),
),
&mut obligations,
diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
index 4e4172e7f..709c3f432 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -307,13 +307,13 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
// there, but that needs some way to handle cycles.
constraints
.dtorck_types
- .extend(dtorck_types.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ .extend(dtorck_types.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs)));
constraints
.outlives
- .extend(outlives.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ .extend(outlives.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs)));
constraints
.overflows
- .extend(overflows.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ .extend(overflows.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs)));
}
// Objects must be alive in order for their destructor
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 edbe2de81..a50644bb7 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -67,7 +67,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
let mut _orig_values = OriginalQueryValues::default();
let param_env = match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
// we ignore the value set to it.
let mut _constness = pred.constness;
obligation
@@ -78,9 +78,9 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
_ => obligation.param_env.without_const(),
};
- if self.tcx.trait_solver_next() {
+ if self.next_trait_solver() {
self.probe(|snapshot| {
- let mut fulfill_cx = crate::solve::FulfillmentCtxt::new();
+ let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self);
fulfill_cx.register_predicate_obligation(self, obligation.clone());
// True errors
// FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK?
@@ -90,13 +90,14 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
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() {
+ } else if self.region_constraints_added_in_snapshot(snapshot) {
Ok(EvaluationResult::EvaluatedToOkModuloRegions)
} else {
Ok(EvaluationResult::EvaluatedToOk)
}
})
} else {
+ assert!(!self.intercrate);
let c_pred = self.canonicalize_query_keep_static(
param_env.and(obligation.predicate),
&mut _orig_values,
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 8bf934cb7..7fe79fd86 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -30,7 +30,7 @@ pub trait QueryNormalizeExt<'tcx> {
///
/// After codegen, when lifetimes do not matter, it is preferable to instead
/// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
- fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+ fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
where
T: TypeFoldable<TyCtxt<'tcx>>;
}
@@ -49,7 +49,7 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
/// normalizing, but for now should be used only when we actually
/// know that normalization will succeed, since error reporting
/// and other details are still "under development".
- fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+ fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
@@ -60,6 +60,16 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
self.param_env,
self.cause,
);
+
+ if self.infcx.next_trait_solver() {
+ match crate::solve::deeply_normalize(self, value) {
+ Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
+ Err(_errors) => {
+ return Err(NoSolution);
+ }
+ }
+ }
+
if !needs_normalization(&value, self.param_env.reveal()) {
return Ok(Normalized { value, obligations: vec![] });
}
@@ -211,10 +221,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
// Wrap this in a closure so we don't accidentally return from the outer function
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::Opaque if !data.substs.has_escaping_bound_vars() => {
+ ty::Opaque => {
// Only normalize `impl Trait` outside of type inference, usually in codegen.
match self.param_env.reveal() {
Reveal::UserFacing => ty.try_super_fold_with(self)?,
@@ -255,9 +262,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
}
}
- ty::Opaque => ty.try_super_fold_with(self)?,
-
- ty::Projection | ty::Inherent => {
+ ty::Projection | ty::Inherent | ty::Weak => {
// See note in `rustc_trait_selection::traits::project`
let infcx = self.infcx;
@@ -282,6 +287,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
let result = match kind {
ty::Projection => tcx.normalize_projection_ty(c_data),
+ ty::Weak => tcx.normalize_weak_ty(c_data),
ty::Inherent => tcx.normalize_inherent_projection_ty(c_data),
_ => unreachable!(),
}?;
@@ -321,8 +327,12 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
};
// `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)?
+ // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type
+ // and we need to continue folding it to reveal the TAIT behind it.
+ if res != ty
+ && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak)
+ {
+ res.try_fold_with(self)?
} else {
res
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
index 01d7a1e79..44671a076 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
@@ -67,7 +67,8 @@ fn relate_mir_and_user_ty<'tcx>(
ocx.eq(&cause, param_env, mir_ty, user_ty)?;
// FIXME(#104764): We should check well-formedness before normalization.
- let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(user_ty.into()));
+ let predicate =
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(user_ty.into())));
ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
Ok(())
}
@@ -119,7 +120,9 @@ fn relate_mir_and_user_substs<'tcx>(
let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
- let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into()));
+ let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ impl_self_ty.into(),
+ )));
ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
}
@@ -134,7 +137,8 @@ fn relate_mir_and_user_substs<'tcx>(
// them? This would only be relevant if some input
// type were ill-formed but did not appear in `ty`,
// which...could happen with normalization...
- let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()));
+ let predicate =
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty.into())));
ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate));
Ok(())
}
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 6d8d2103f..5420caee3 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
@@ -5,6 +5,7 @@ use crate::traits::ObligationCtxt;
use rustc_errors::ErrorGuaranteed;
use rustc_infer::infer::region_constraints::RegionConstraintData;
use rustc_middle::traits::query::NoSolution;
+use rustc_middle::ty::{TyCtxt, TypeFoldable};
use rustc_span::source_map::DUMMY_SP;
use rustc_span::Span;
@@ -24,9 +25,10 @@ impl<F> CustomTypeOp<F> {
}
}
-impl<'tcx, F, R: fmt::Debug> super::TypeOp<'tcx> for CustomTypeOp<F>
+impl<'tcx, F, R> super::TypeOp<'tcx> for CustomTypeOp<F>
where
F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
+ R: fmt::Debug + TypeFoldable<TyCtxt<'tcx>>,
{
type Output = R;
/// We can't do any custom error reporting for `CustomTypeOp`, so
@@ -57,12 +59,16 @@ impl<F> fmt::Debug for CustomTypeOp<F> {
/// Executes `op` and then scrapes out all the "old style" region
/// constraints that result, creating query-region-constraints.
-pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
+pub fn scrape_region_constraints<'tcx, Op, R>(
infcx: &InferCtxt<'tcx>,
op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
name: &'static str,
span: Span,
-) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> {
+) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed>
+where
+ R: TypeFoldable<TyCtxt<'tcx>>,
+ Op: super::TypeOp<'tcx, Output = R>,
+{
// During NLL, we expect that nobody will register region
// obligations **except** as part of a custom type op (and, at the
// end of each custom type op, we scrape out the region
@@ -76,7 +82,7 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
);
let value = infcx.commit_if_ok(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
let value = op(&ocx).map_err(|_| {
infcx.tcx.sess.delay_span_bug(span, format!("error performing operation: {name}"))
})?;
@@ -91,6 +97,9 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
}
})?;
+ // Next trait solver performs operations locally, and normalize goals should resolve vars.
+ let value = infcx.resolve_vars_if_possible(value);
+
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(
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index 9989fc9c4..979498fb6 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -1,3 +1,4 @@
+use crate::solve;
use crate::traits::query::NoSolution;
use crate::traits::wf;
use crate::traits::ObligationCtxt;
@@ -6,6 +7,7 @@ use rustc_infer::infer::canonical::Canonical;
use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
use rustc_infer::traits::query::OutlivesBound;
use rustc_middle::infer::canonical::CanonicalQueryResponse;
+use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::source_map::DUMMY_SP;
@@ -108,7 +110,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
// learn anything new from those.
if obligation.predicate.has_non_region_infer() {
match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::AliasRelate(..) => {
ocx.register_obligation(obligation.clone());
}
@@ -121,33 +123,33 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
Some(pred) => pred,
};
match pred {
- ty::PredicateKind::Clause(ty::Clause::Trait(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
// FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
// if we ever support that
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::Clause(ty::Clause::Projection(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => {}
+ => {}
// We need to search through *all* WellFormed predicates
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
wf_args.push(arg);
}
// We need to register region relationships
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
r_a,
r_b,
))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)),
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
ty_a,
r_b,
))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)),
@@ -164,19 +166,29 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
// We lazily compute the outlives components as
// `select_all_or_error` constrains inference variables.
- let implied_bounds = outlives_bounds
- .into_iter()
- .flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() {
- ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)],
+ let mut implied_bounds = Vec::new();
+ for ty::OutlivesPredicate(a, r_b) in outlives_bounds {
+ match a.unpack() {
+ ty::GenericArgKind::Lifetime(r_a) => {
+ implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a))
+ }
ty::GenericArgKind::Type(ty_a) => {
- let ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
+ let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
+ // Need to manually normalize in the new solver as `wf::obligations` does not.
+ if ocx.infcx.next_trait_solver() {
+ ty_a = solve::deeply_normalize(
+ ocx.infcx.at(&ObligationCause::dummy(), param_env),
+ ty_a,
+ )
+ .map_err(|_errs| NoSolution)?;
+ }
let mut components = smallvec![];
push_outlives_components(tcx, ty_a, &mut components);
- implied_bounds_from_components(r_b, components)
+ implied_bounds.extend(implied_bounds_from_components(r_b, components))
}
ty::GenericArgKind::Const(_) => unreachable!(),
- })
- .collect();
+ }
+ }
Ok(implied_bounds)
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index 642fdec2d..9d7933e23 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -146,7 +146,7 @@ where
infcx: &InferCtxt<'tcx>,
span: Span,
) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
- if infcx.tcx.trait_solver_next() {
+ if infcx.next_trait_solver() {
return Ok(scrape_region_constraints(
infcx,
|ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self),
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
index 57ca14aa4..9559f5002 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
@@ -50,12 +50,12 @@ impl<'tcx> Normalizable<'tcx> for Ty<'tcx> {
}
}
-impl<'tcx> Normalizable<'tcx> for ty::Predicate<'tcx> {
+impl<'tcx> Normalizable<'tcx> for ty::Clause<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
- tcx.type_op_normalize_predicate(canonicalized)
+ tcx.type_op_normalize_clause(canonicalized)
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
index 47850bc33..789ef6472 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
@@ -18,7 +18,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
// `&T`, accounts for about 60% percentage of the predicates
// we have to prove. No need to canonicalize and all that for
// such cases.
- if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_ref)) =
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
key.value.predicate.kind().skip_binder()
{
if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
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 8bc82b9f5..d5f6aaa7f 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -10,7 +10,7 @@ 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_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
@@ -137,13 +137,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self, candidates))]
fn assemble_candidates_from_projected_tys(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Before we go into the whole placeholder thing, just
// quickly check if the self-type is a projection at all.
match obligation.predicate.skip_binder().trait_ref.self_ty().kind() {
- // Excluding IATs here as they don't have meaningful item bounds.
+ // Excluding IATs and type aliases here as they don't have meaningful item bounds.
ty::Alias(ty::Projection | ty::Opaque, _) => {}
ty::Infer(ty::TyVar(_)) => {
span_bug!(
@@ -181,7 +181,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.caller_bounds()
.iter()
.filter(|p| !p.references_error())
- .filter_map(|p| p.to_opt_poly_trait_pred());
+ .filter_map(|p| p.as_trait_clause());
// Micro-optimization: filter out predicates relating to different traits.
let matching_bounds =
@@ -206,7 +206,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_generator_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Okay to skip binder because the substs on generator types never
@@ -231,7 +231,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_future_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = obligation.self_ty().skip_binder();
@@ -254,7 +254,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// unified during the confirmation step.
fn assemble_closure_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let Some(kind) = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()) else {
@@ -292,7 +292,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// Implements one of the `Fn()` family for a fn pointer.
fn assemble_fn_pointer_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// We provide impl of all fn traits for fn pointers.
@@ -334,7 +334,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self, candidates))]
fn assemble_candidates_from_impls(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Essentially any user-written impl will match with an error type,
@@ -360,7 +360,9 @@ 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 !drcx.substs_refs_may_unify(obligation_substs, impl_trait_ref.0.substs) {
+ if !drcx
+ .substs_refs_may_unify(obligation_substs, impl_trait_ref.skip_binder().substs)
+ {
return;
}
if self.reject_fn_ptr_impls(
@@ -386,9 +388,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// `FnPtr`, when we wanted to report that it doesn't implement `Trait`.
#[instrument(level = "trace", skip(self), ret)]
fn reject_fn_ptr_impls(
- &self,
+ &mut self,
impl_def_id: DefId,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
impl_self_ty: Ty<'tcx>,
) -> bool {
// Let `impl<T: FnPtr> Trait for Vec<T>` go through the normal rejection path.
@@ -400,7 +402,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
};
for &(predicate, _) in self.tcx().predicates_of(impl_def_id).predicates {
- let ty::PredicateKind::Clause(ty::Clause::Trait(pred))
+ let ty::ClauseKind::Trait(pred)
= predicate.kind().skip_binder() else { continue };
if fn_ptr_trait != pred.trait_ref.def_id {
continue;
@@ -415,17 +417,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// 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::Param(..)
+ | ty::Bound(_, _) => {}
- 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
@@ -461,10 +461,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.tcx().mk_predicate(obligation.predicate.map_bound(|mut pred| {
pred.trait_ref =
ty::TraitRef::new(self.tcx(), fn_ptr_trait, [pred.trait_ref.self_ty()]);
- ty::PredicateKind::Clause(ty::Clause::Trait(pred))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
})),
);
- if let Ok(r) = self.infcx.evaluate_obligation(&obligation) {
+ if let Ok(r) = self.evaluate_root_obligation(&obligation) {
if !r.may_apply() {
return true;
}
@@ -475,7 +475,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_candidates_from_auto_impls(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Okay to skip binder here because the tests we do below do not involve bound regions.
@@ -544,7 +544,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// Searches for impls that might apply to `obligation`.
fn assemble_candidates_from_object_ty(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
debug!(
@@ -552,6 +552,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
"assemble_candidates_from_object_ty",
);
+ if !self.tcx().trait_def(obligation.predicate.def_id()).implement_via_object {
+ return;
+ }
+
self.infcx.probe(|_snapshot| {
if obligation.has_non_region_late_bound() {
return;
@@ -664,7 +668,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// Searches for unsizing that might apply to `obligation`.
fn assemble_candidates_for_unsizing(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// We currently never consider higher-ranked obligations e.g.
@@ -778,7 +782,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self, obligation, candidates))]
fn assemble_candidates_for_transmutability(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
if obligation.predicate.has_non_region_param() {
@@ -796,7 +800,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self, obligation, candidates))]
fn assemble_candidates_for_trait_alias(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Okay to skip binder here because the tests we do below do not involve bound regions.
@@ -833,7 +837,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_const_destruct_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// If the predicate is `~const Destruct` in a non-const environment, we don't actually need
@@ -920,7 +924,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_candidate_for_tuple(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
@@ -962,7 +966,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_candidate_for_pointer_like(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// The regions of a type don't affect the size of the type
@@ -987,7 +991,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_candidates_for_fn_ptr_trait(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 0d9f55d4c..7adc29bbb 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -6,6 +6,7 @@
//!
//! [rustc dev guide]:
//! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation
+use rustc_ast::Mutability;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
@@ -13,9 +14,8 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
use rustc_middle::ty::{
self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate,
- TraitRef, Ty, TyCtxt, TypeVisitableExt,
+ TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt,
};
-use rustc_session::config::TraitSolver;
use rustc_span::def_id::DefId;
use crate::traits::project::{normalize_with_depth, normalize_with_depth_to};
@@ -26,12 +26,9 @@ use crate::traits::vtable::{
};
use crate::traits::{
BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
- ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
- ImplSourceConstDestructData, ImplSourceFnPointerData, ImplSourceFutureData,
- ImplSourceGeneratorData, ImplSourceObjectData, ImplSourceTraitAliasData,
- ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, Obligation,
- ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection, SelectionError,
- TraitNotObjectSafe, TraitObligation, Unimplemented,
+ ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized,
+ Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation,
+ PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented,
};
use super::BuiltinImplConditions;
@@ -45,7 +42,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self))]
pub(super) fn confirm_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidate: SelectionCandidate<'tcx>,
) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
let mut impl_src = match candidate {
@@ -70,8 +67,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
AutoImplCandidate => {
- let data = self.confirm_auto_impl_candidate(obligation);
- ImplSource::AutoImpl(data)
+ let data = self.confirm_auto_impl_candidate(obligation)?;
+ ImplSource::Builtin(data)
}
ProjectionCandidate(idx, constness) => {
@@ -86,34 +83,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ClosureCandidate { .. } => {
let vtable_closure = self.confirm_closure_candidate(obligation)?;
- ImplSource::Closure(vtable_closure)
+ ImplSource::Builtin(vtable_closure)
}
GeneratorCandidate => {
let vtable_generator = self.confirm_generator_candidate(obligation)?;
- ImplSource::Generator(vtable_generator)
+ ImplSource::Builtin(vtable_generator)
}
FutureCandidate => {
let vtable_future = self.confirm_future_candidate(obligation)?;
- ImplSource::Future(vtable_future)
+ ImplSource::Builtin(vtable_future)
}
FnPointerCandidate { is_const } => {
let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
- ImplSource::FnPointer(data)
+ ImplSource::Builtin(data)
}
TraitAliasCandidate => {
let data = self.confirm_trait_alias_candidate(obligation);
- ImplSource::TraitAlias(data)
+ ImplSource::Builtin(data)
}
BuiltinObjectCandidate => {
// This indicates something like `Trait + Send: Send`. In this case, we know that
// this holds because that's what the object type is telling us, and there's really
// no additional obligations to prove and no types in particular to unify, etc.
- ImplSource::Param(Vec::new(), ty::BoundConstness::NotConst)
+ ImplSource::Builtin(Vec::new())
}
BuiltinUnsizeCandidate => {
@@ -128,7 +125,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ConstDestructCandidate(def_id) => {
let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
- ImplSource::ConstDestruct(data)
+ ImplSource::Builtin(data)
}
};
@@ -151,7 +148,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_projection_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
idx: usize,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
@@ -162,7 +159,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let placeholder_self_ty = placeholder_trait_predicate.self_ty();
let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate);
let (def_id, substs) = match *placeholder_self_ty.kind() {
- // Excluding IATs here as they don't have meaningful item bounds.
+ // Excluding IATs and type aliases here as they don't have meaningful item bounds.
ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
(def_id, substs)
}
@@ -171,7 +168,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let candidate_predicate = tcx.item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs);
let candidate = candidate_predicate
- .to_opt_poly_trait_pred()
+ .as_trait_clause()
.expect("projection candidate is not a trait predicate")
.map_bound(|t| t.trait_ref);
let mut obligations = Vec::new();
@@ -218,7 +215,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_param_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
param: ty::PolyTraitRef<'tcx>,
) -> Vec<PredicateObligation<'tcx>> {
debug!(?obligation, ?param, "confirm_param_candidate");
@@ -241,9 +238,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_builtin_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
has_nested: bool,
- ) -> ImplSourceBuiltinData<PredicateObligation<'tcx>> {
+ ) -> Vec<PredicateObligation<'tcx>> {
debug!(?obligation, ?has_nested, "confirm_builtin_candidate");
let lang_items = self.tcx().lang_items();
@@ -276,14 +273,63 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?obligations);
- ImplSourceBuiltinData { nested: obligations }
+ obligations
}
+ #[instrument(level = "debug", skip(self))]
fn confirm_transmutability_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
- debug!(?obligation, "confirm_transmutability_candidate");
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ use rustc_transmute::{Answer, Condition};
+ #[instrument(level = "debug", skip(tcx, obligation, predicate))]
+ fn flatten_answer_tree<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
+ predicate: TraitPredicate<'tcx>,
+ cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
+ ) -> Vec<PredicateObligation<'tcx>> {
+ match cond {
+ // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
+ // Not possible until the trait solver supports disjunctions of obligations
+ Condition::IfAll(conds) | Condition::IfAny(conds) => conds
+ .into_iter()
+ .flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
+ .collect(),
+ Condition::IfTransmutable { src, dst } => {
+ let trait_def_id = obligation.predicate.def_id();
+ let scope = predicate.trait_ref.substs.type_at(2);
+ let assume_const = predicate.trait_ref.substs.const_at(3);
+ let make_obl = |from_ty, to_ty| {
+ let trait_ref1 = ty::TraitRef::new(
+ tcx,
+ trait_def_id,
+ [
+ ty::GenericArg::from(to_ty),
+ ty::GenericArg::from(from_ty),
+ ty::GenericArg::from(scope),
+ ty::GenericArg::from(assume_const),
+ ],
+ );
+ Obligation::with_depth(
+ tcx,
+ obligation.cause.clone(),
+ obligation.recursion_depth + 1,
+ obligation.param_env,
+ trait_ref1,
+ )
+ };
+
+ // If Dst is mutable, check bidirectionally.
+ // For example, transmuting bool -> u8 is OK as long as you can't update that u8
+ // to be > 1, because you could later transmute the u8 back to a bool and get UB.
+ match dst.mutability {
+ Mutability::Not => vec![make_obl(src.ty, dst.ty)],
+ Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)],
+ }
+ }
+ }
+ }
// We erase regions here because transmutability calls layout queries,
// which does not handle inference regions and doesn't particularly
@@ -301,21 +347,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Err(Unimplemented);
};
+ let dst = predicate.trait_ref.substs.type_at(0);
+ let src = predicate.trait_ref.substs.type_at(1);
+ debug!(?src, ?dst);
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
let maybe_transmutable = transmute_env.is_transmutable(
obligation.cause.clone(),
- rustc_transmute::Types {
- dst: predicate.trait_ref.substs.type_at(0),
- src: predicate.trait_ref.substs.type_at(1),
- },
+ rustc_transmute::Types { dst, src },
predicate.trait_ref.substs.type_at(2),
assume,
);
- match maybe_transmutable {
- rustc_transmute::Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
- _ => Err(Unimplemented),
- }
+ let fully_flattened = match maybe_transmutable {
+ Answer::No(_) => Err(Unimplemented)?,
+ Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond),
+ Answer::Yes => vec![],
+ };
+
+ debug!(?fully_flattened);
+ Ok(fully_flattened)
}
/// This handles the case where an `auto trait Foo` impl is being used.
@@ -325,22 +375,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds.
fn confirm_auto_impl_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> ImplSourceAutoImplData<PredicateObligation<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
debug!(?obligation, "confirm_auto_impl_candidate");
let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty());
- let types = self.constituent_types_for_ty(self_ty);
- self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types)
+ let types = self.constituent_types_for_ty(self_ty)?;
+ Ok(self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types))
}
/// See `confirm_auto_impl_candidate`.
fn vtable_auto_impl(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
trait_def_id: DefId,
nested: ty::Binder<'tcx, Vec<Ty<'tcx>>>,
- ) -> ImplSourceAutoImplData<PredicateObligation<'tcx>> {
+ ) -> Vec<PredicateObligation<'tcx>> {
debug!(?nested, "vtable_auto_impl");
ensure_sufficient_stack(|| {
let cause = obligation.derived_cause(BuiltinDerivedObligation);
@@ -370,13 +420,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?obligations, "vtable_auto_impl");
- ImplSourceAutoImplData { trait_def_id, nested: obligations }
+ obligations
})
}
fn confirm_impl_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
impl_def_id: DefId,
) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
debug!(?obligation, ?impl_def_id, "confirm_impl_candidate");
@@ -431,9 +481,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_object_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
index: usize,
- ) -> Result<ImplSourceObjectData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ ) -> Result<ImplSourceObjectData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
debug!(?obligation, ?index, "confirm_object_candidate");
@@ -527,9 +577,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
substs.extend(trait_predicate.trait_ref.substs.iter());
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
smallvec::SmallVec::with_capacity(
- bound.0.kind().bound_vars().len() + defs.count(),
+ bound.skip_binder().kind().bound_vars().len() + defs.count(),
);
- bound_vars.extend(bound.0.kind().bound_vars().into_iter());
+ bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter());
InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param
.kind
{
@@ -537,7 +587,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let kind = ty::BoundTyKind::Param(param.def_id, param.name);
let bound_var = ty::BoundVariableKind::Ty(kind);
bound_vars.push(bound_var);
- tcx.mk_bound(
+ Ty::new_bound(
+ tcx,
ty::INNERMOST,
ty::BoundTy {
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
@@ -550,7 +601,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
let bound_var = ty::BoundVariableKind::Region(kind);
bound_vars.push(bound_var);
- tcx.mk_re_late_bound(
+ ty::Region::new_late_bound(
+ tcx,
ty::INNERMOST,
ty::BoundRegion {
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
@@ -562,11 +614,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
GenericParamDefKind::Const { .. } => {
let bound_var = ty::BoundVariableKind::Const;
bound_vars.push(bound_var);
- tcx.mk_const(
- ty::ConstKind::Bound(
- ty::INNERMOST,
- ty::BoundVar::from_usize(bound_vars.len() - 1),
- ),
+ ty::Const::new_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundVar::from_usize(bound_vars.len() - 1),
tcx.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic"),
@@ -578,7 +629,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let assoc_ty_substs = tcx.mk_substs(&substs);
let bound =
bound.map_bound(|b| b.kind().skip_binder()).subst(tcx, assoc_ty_substs);
- tcx.mk_predicate(ty::Binder::bind_with_vars(bound, bound_vars))
+ ty::Binder::bind_with_vars(bound, bound_vars).to_predicate(tcx)
};
let normalized_bound = normalize_with_depth_to(
self,
@@ -599,15 +650,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
(unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)),
);
- Ok(ImplSourceObjectData { upcast_trait_ref, vtable_base, nested })
+ Ok(ImplSourceObjectData { vtable_base, nested })
}
fn confirm_fn_pointer_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
is_const: bool,
- ) -> Result<ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
- {
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
debug!(?obligation, "confirm_fn_pointer_candidate");
let tcx = self.tcx();
@@ -659,16 +709,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let tr = ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [output_ty]);
nested.push(Obligation::new(self.infcx.tcx, cause, obligation.param_env, tr));
- Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested })
+ Ok(nested)
}
fn confirm_trait_alias_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> ImplSourceTraitAliasData<'tcx, PredicateObligation<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Vec<PredicateObligation<'tcx>> {
debug!(?obligation, "confirm_trait_alias_candidate");
- let alias_def_id = obligation.predicate.def_id();
let predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
let trait_ref = predicate.trait_ref;
let trait_def_id = trait_ref.def_id;
@@ -685,14 +734,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?trait_def_id, ?trait_obligations, "trait alias obligations");
- ImplSourceTraitAliasData { alias_def_id, substs, nested: trait_obligations }
+ trait_obligations
}
fn confirm_generator_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceGeneratorData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
- {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// Okay to skip binder because the substs on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
@@ -725,13 +773,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?trait_ref, ?nested, "generator candidate obligations");
- Ok(ImplSourceGeneratorData { generator_def_id, substs, nested })
+ Ok(nested)
}
fn confirm_future_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// Okay to skip binder because the substs on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
@@ -755,14 +803,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?trait_ref, ?nested, "future candidate obligations");
- Ok(ImplSourceFutureData { generator_def_id, substs, nested })
+ Ok(nested)
}
#[instrument(skip(self), level = "debug")]
fn confirm_closure_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let kind = self
.tcx()
.fn_trait_kind_from_def_id(obligation.predicate.def_id())
@@ -781,15 +829,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
- // FIXME: Chalk
- if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Chalk {
- nested.push(obligation.with(
- self.tcx(),
- ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)),
- ));
- }
+ nested.push(obligation.with(
+ self.tcx(),
+ ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)),
+ ));
- Ok(ImplSourceClosureData { closure_def_id, substs, nested })
+ Ok(nested)
}
/// In the case of closure types and fn pointers,
@@ -820,7 +865,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(skip(self), level = "trace")]
fn confirm_poly_trait_refs(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
self_ty_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let obligation_trait_ref = obligation.predicate.to_poly_trait_ref();
@@ -855,10 +900,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_trait_upcasting_unsize_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
idx: usize,
- ) -> Result<ImplSourceTraitUpcastingData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
- {
+ ) -> Result<ImplSourceTraitUpcastingData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
@@ -903,7 +947,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.map(ty::Binder::dummy),
);
let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter);
- let source_trait = tcx.mk_dynamic(existential_predicates, r_b, repr_a);
+ let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, repr_a);
// Require that the traits involved in this upcast are **equal**;
// only the **lifetime bound** is changed.
@@ -955,13 +999,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let vtable_vptr_slot =
prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap();
- Ok(ImplSourceTraitUpcastingData { upcast_trait_ref, vtable_vptr_slot, nested })
+ Ok(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
}
fn confirm_builtin_unsize_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
@@ -996,7 +1040,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.map(ty::Binder::dummy),
);
let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter);
- let source_trait = tcx.mk_dynamic(existential_predicates, r_b, dyn_a);
+ let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, dyn_a);
// Require that the traits involved in this upcast are **equal**;
// only the **lifetime bound** is changed.
@@ -1054,12 +1098,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.cause.span,
[source],
);
- nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx)));
+ nested.push(predicate_to_obligation(tr.to_predicate(tcx)));
// If the type is `Foo + 'a`, ensure that the type
// being cast to `Foo + 'a` outlives `'a`:
let outlives = ty::OutlivesPredicate(source, r);
- nested.push(predicate_to_obligation(ty::Binder::dummy(outlives).to_predicate(tcx)));
+ nested.push(predicate_to_obligation(
+ ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx),
+ ));
}
// `[T; n]` -> `[T]`
@@ -1079,12 +1125,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Err(Unimplemented);
}
- let tail_field = def
- .non_enum_variant()
- .fields
- .raw
- .last()
- .expect("expected unsized ADT to have a tail field");
+ let tail_field = def.non_enum_variant().tail();
let tail_field_ty = tcx.type_of(tail_field.did);
// Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`,
@@ -1112,7 +1153,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let substs = tcx.mk_substs_from_iter(substs_a.iter().enumerate().map(|(i, k)| {
if unsizing_params.contains(i as u32) { substs_b[i] } else { k }
}));
- let new_struct = tcx.mk_adt(def, substs);
+ let new_struct = Ty::new_adt(tcx, def, substs);
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
@@ -1143,7 +1184,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Check that the source tuple with the target's
// last element is equal to the target.
let new_tuple =
- tcx.mk_tup_from_iter(a_mid.iter().copied().chain(iter::once(b_last)));
+ Ty::new_tup_from_iter(tcx, a_mid.iter().copied().chain(iter::once(b_last)));
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
@@ -1162,17 +1203,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
_ => bug!("source: {source}, target: {target}"),
};
- Ok(ImplSourceBuiltinData { nested })
+ Ok(nested)
}
fn confirm_const_destruct_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
impl_def_id: Option<DefId>,
- ) -> Result<ImplSourceConstDestructData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// `~const Destruct` in a non-const environment is always trivially true, since our type is `Drop`
if !obligation.is_const() {
- return Ok(ImplSourceConstDestructData { nested: vec![] });
+ return Ok(vec![]);
}
let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None);
@@ -1326,6 +1367,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- Ok(ImplSourceConstDestructData { nested })
+ Ok(nested)
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 3baf1c97c..7f31ab751 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -15,11 +15,12 @@ use super::util::closure_trait_ref_and_return_type;
use super::wf;
use super::{
ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, Normalized, Obligation,
- ObligationCause, ObligationCauseCode, Overflow, PredicateObligation, Selection, SelectionError,
- SelectionResult, TraitObligation, TraitQueryMode,
+ ObligationCause, ObligationCauseCode, Overflow, PolyTraitObligation, PredicateObligation,
+ Selection, SelectionError, SelectionResult, TraitQueryMode,
};
use crate::infer::{InferCtxt, InferOk, TypeFreshener};
+use crate::solve::InferCtxtSelectExt;
use crate::traits::error_reporting::TypeErrCtxtExt;
use crate::traits::project::try_normalize_with_depth_to;
use crate::traits::project::ProjectAndUnifyResult;
@@ -33,8 +34,7 @@ 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_infer::traits::TraitObligation;
use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -123,7 +123,7 @@ pub struct SelectionContext<'cx, 'tcx> {
// A stack that walks back up the stack frame.
struct TraitObligationStack<'prev, 'tcx> {
- obligation: &'prev TraitObligation<'tcx>,
+ obligation: &'prev PolyTraitObligation<'tcx>,
/// The trait predicate from `obligation` but "freshened" with the
/// selection-context's freshener. Used to check for recursion.
@@ -260,10 +260,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// Attempts to satisfy the obligation. If successful, this will affect the surrounding
/// type environment by performing unification.
#[instrument(level = "debug", skip(self), ret)]
- pub fn select(
+ pub fn poly_select(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
+ if self.infcx.next_trait_solver() {
+ return self.infcx.select_in_new_trait_solver(obligation);
+ }
+
let candidate = match self.select_from_obligation(obligation) {
Err(SelectionError::Overflow(OverflowError::Canonical)) => {
// In standard mode, overflow must have been caught and reported
@@ -290,9 +294,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- pub(crate) fn select_from_obligation(
+ pub fn select(
&mut self,
obligation: &TraitObligation<'tcx>,
+ ) -> SelectionResult<'tcx, Selection<'tcx>> {
+ self.poly_select(&Obligation {
+ cause: obligation.cause.clone(),
+ param_env: obligation.param_env,
+ predicate: ty::Binder::dummy(obligation.predicate),
+ recursion_depth: obligation.recursion_depth,
+ })
+ }
+
+ fn select_from_obligation(
+ &mut self,
+ obligation: &PolyTraitObligation<'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
debug_assert!(!obligation.predicate.has_escaping_bound_vars());
@@ -307,6 +323,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+ debug_assert!(!self.infcx.next_trait_solver());
// Watch out for overflow. This intentionally bypasses (and does
// not update) the cache.
self.check_recursion_limit(&stack.obligation, &stack.obligation)?;
@@ -365,7 +382,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
if !candidate_set.ambiguous && no_candidates_apply {
- let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
+ let trait_ref = self.infcx.resolve_vars_if_possible(
+ stack.obligation.predicate.skip_binder().trait_ref,
+ );
if !trait_ref.references_error() {
let self_ty = trait_ref.self_ty();
let (trait_desc, self_desc) = with_no_trimmed_paths!({
@@ -516,37 +535,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// The result is "true" if the obligation *may* hold and "false" if
// we can be sure it does not.
- /// Evaluates whether the obligation `obligation` can be satisfied (by any means).
- pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool {
- debug!(?obligation, "predicate_may_hold_fatal");
-
- // This fatal query is a stopgap that should only be used in standard mode,
- // where we do not expect overflow to be propagated.
- assert!(self.query_mode == TraitQueryMode::Standard);
-
- self.evaluate_root_obligation(obligation)
- .expect("Overflow should be caught earlier in standard query mode")
- .may_apply()
- }
-
/// Evaluates whether the obligation `obligation` can be satisfied
/// and returns an `EvaluationResult`. This is meant for the
/// *initial* call.
+ ///
+ /// Do not use this directly, use `infcx.evaluate_obligation` instead.
pub fn evaluate_root_obligation(
&mut self,
obligation: &PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
+ debug_assert!(!self.infcx.next_trait_solver());
self.evaluation_probe(|this| {
let goal =
this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
- let mut result = 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(),
- )?
- };
+ let mut result = this.evaluate_predicate_recursively(
+ TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
+ obligation.clone(),
+ )?;
// If the predicate has done any inference, then downgrade the
// result to ambiguous.
if this.infcx.shallow_resolve(goal) != goal {
@@ -561,9 +566,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>,
) -> Result<EvaluationResult, OverflowError> {
self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
+ let outer_universe = self.infcx.universe();
let result = op(self)?;
- match self.infcx.leak_check(true, snapshot) {
+ match self.infcx.leak_check(outer_universe, Some(snapshot)) {
Ok(()) => {}
Err(_) => return Ok(EvaluatedToErr),
}
@@ -572,9 +578,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Ok(result.max(EvaluatedToOkModuloOpaqueTypes));
}
- match self.infcx.region_constraints_added_in_snapshot(snapshot) {
- None => Ok(result),
- Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)),
+ if self.infcx.region_constraints_added_in_snapshot(snapshot) {
+ Ok(result.max(EvaluatedToOkModuloRegions))
+ } else {
+ Ok(result)
}
})
}
@@ -591,42 +598,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
where
I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
{
- if self.tcx().trait_solver_next() {
- self.evaluate_predicates_recursively_in_new_solver(predicates)
- } else {
- let mut result = EvaluatedToOk;
- 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,
- // so we don't need to look on the other predicates.
- return Ok(EvaluatedToErr);
- } else {
- result = cmp::max(result, eval);
- }
+ let mut result = EvaluatedToOk;
+ 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,
+ // so we don't need to look on the other predicates.
+ return Ok(EvaluatedToErr);
+ } else {
+ result = cmp::max(result, eval);
}
- Ok(result)
}
- }
-
- /// Evaluates the predicates using the new solver when `-Ztrait-solver=next` is enabled
- fn evaluate_predicates_recursively_in_new_solver(
- &mut self,
- predicates: impl IntoIterator<Item = PredicateObligation<'tcx>>,
- ) -> Result<EvaluationResult, OverflowError> {
- 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);
- }
- if !fulfill_cx.select_all_or_error(self.infcx).is_empty() {
- return Ok(EvaluatedToAmbig);
- }
- // Regions and opaques are handled in the `evaluation_probe` by looking at the snapshot
- Ok(EvaluatedToOk)
+ Ok(result)
}
#[instrument(
@@ -640,7 +624,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
previous_stack: TraitObligationStackList<'o, 'tcx>,
obligation: PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
- // `previous_stack` stores a `TraitObligation`, while `obligation` is
+ debug_assert!(!self.infcx.next_trait_solver());
+ // `previous_stack` stores a `PolyTraitObligation`, while `obligation` is
// a `PredicateObligation`. These are distinct types, so we can't
// use any `Option` combinator method that would force them to be
// the same.
@@ -652,7 +637,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ensure_sufficient_stack(|| {
let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(t)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
let t = bound_predicate.rebind(t);
debug_assert!(!t.has_escaping_bound_vars());
let obligation = obligation.with(self.tcx(), t);
@@ -683,7 +668,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
// So, there is a bit going on here. First, `WellFormed` predicates
// are coinductive, like trait predicates with auto traits.
// This means that we need to detect if we have recursively
@@ -769,7 +754,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(pred)) => {
// A global type with no free lifetimes or generic parameters
// outlives anything.
if pred.0.has_free_regions()
@@ -783,7 +768,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => {
// We do not consider region relationships when evaluating trait matches.
Ok(EvaluatedToOkModuloRegions)
}
@@ -796,7 +781,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::Clause(ty::Clause::Projection(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
let data = bound_predicate.rebind(data);
let project_obligation = obligation.with(self.tcx(), data);
match project::poly_project_and_unify_type(self, &project_obligation) {
@@ -871,7 +856,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::ConstEvaluatable(uv) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => {
match const_evaluatable::is_const_evaluatable(
self.infcx,
uv,
@@ -901,7 +886,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
use rustc_hir::def::DefKind;
- use ty::ConstKind::Unevaluated;
+ use ty::Unevaluated;
match (c1.kind(), c2.kind()) {
(Unevaluated(a), Unevaluated(b))
if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
@@ -976,14 +961,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
}
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("TypeWellFormedFromEnv is only used for chalk")
- }
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used for new solver")
}
ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig),
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
match self.infcx.at(&obligation.cause, obligation.param_env).eq(
DefineOpaqueTypes::No,
ct.ty(),
@@ -1004,7 +986,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn evaluate_trait_predicate_recursively<'o>(
&mut self,
previous_stack: TraitObligationStackList<'o, 'tcx>,
- mut obligation: TraitObligation<'tcx>,
+ mut obligation: PolyTraitObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
if !self.is_intercrate()
&& obligation.is_global()
@@ -1186,6 +1168,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> Result<EvaluationResult, OverflowError> {
+ debug_assert!(!self.infcx.next_trait_solver());
// In intercrate mode, whenever any of the generics are unbound,
// there can always be an impl. Even if there are no impls in
// this crate, perhaps the type would be unified with
@@ -1409,7 +1392,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn filter_impls(
&mut self,
candidates: Vec<SelectionCandidate<'tcx>>,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> Vec<SelectionCandidate<'tcx>> {
trace!("{candidates:#?}");
let tcx = self.tcx();
@@ -1472,7 +1455,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn filter_reservation_impls(
&mut self,
candidate: SelectionCandidate<'tcx>,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
let tcx = self.tcx();
// Treat reservation impls as ambiguity.
@@ -1644,7 +1627,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self), ret)]
fn match_projection_obligation_against_definition_bounds(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> smallvec::SmallVec<[(usize, ty::BoundConstness); 2]> {
let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
let placeholder_trait_predicate =
@@ -1677,9 +1660,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.enumerate()
.filter_map(|(idx, bound)| {
let bound_predicate = bound.kind();
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) =
- bound_predicate.skip_binder()
- {
+ if let ty::ClauseKind::Trait(pred) = bound_predicate.skip_binder() {
let bound = bound_predicate.rebind(pred.trait_ref);
if self.infcx.probe(|_| {
match self.match_normalize_trait_ref(
@@ -1709,7 +1690,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// variables or placeholders, the normalized bound is returned.
fn match_normalize_trait_ref(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
trait_bound: ty::PolyTraitRef<'tcx>,
placeholder_trait_ref: ty::TraitRef<'tcx>,
) -> Result<Option<ty::PolyTraitRef<'tcx>>, ()> {
@@ -2110,7 +2091,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
impl<'tcx> SelectionContext<'_, 'tcx> {
fn sized_conditions(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> BuiltinImplConditions<'tcx> {
use self::BuiltinImplConditions::{Ambiguous, None, Where};
@@ -2149,13 +2130,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
ty::Adt(def, substs) => {
let sized_crit = def.sized_constraint(self.tcx());
// (*) binder moved here
- Where(obligation.predicate.rebind({
- sized_crit
- .0
- .iter()
- .map(|ty| sized_crit.rebind(*ty).subst(self.tcx(), substs))
- .collect()
- }))
+ Where(
+ obligation
+ .predicate
+ .rebind(sized_crit.subst_iter_copied(self.tcx(), substs).collect()),
+ )
}
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None,
@@ -2172,7 +2151,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn copy_clone_conditions(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> BuiltinImplConditions<'tcx> {
// NOTE: binder moved to (*)
let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty());
@@ -2305,8 +2284,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn constituent_types_for_ty(
&self,
t: ty::Binder<'tcx, Ty<'tcx>>,
- ) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
- match *t.skip_binder().kind() {
+ ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> {
+ Ok(match *t.skip_binder().kind() {
ty::Uint(_)
| ty::Int(_)
| ty::Bool
@@ -2319,13 +2298,13 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| ty::Char => ty::Binder::dummy(Vec::new()),
// Treat this like `struct str([u8]);`
- ty::Str => ty::Binder::dummy(vec![self.tcx().mk_slice(self.tcx().types.u8)]),
+ ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]),
ty::Placeholder(..)
| ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
- | ty::Alias(ty::Projection | ty::Inherent, ..)
+ | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
| ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("asked to assemble constituent types of unexpected type: {:?}", t);
@@ -2370,12 +2349,16 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ let ty = self.tcx().type_of(def_id);
+ if ty.skip_binder().references_error() {
+ return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
+ }
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
- t.rebind(vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)])
+ t.rebind(vec![ty.subst(self.tcx(), substs)])
}
- }
+ })
}
fn collect_predicates_for_types(
@@ -2444,7 +2427,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn rematch_impl(
&mut self,
impl_def_id: DefId,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> Normalized<'tcx, SubstsRef<'tcx>> {
let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
match self.match_impl(impl_def_id, impl_trait_ref, obligation) {
@@ -2465,7 +2448,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
),
);
let value = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id);
- let err = self.tcx().ty_error(guar);
+ let err = Ty::new_error(self.tcx(), guar);
let value = value.fold_with(&mut BottomUpFolder {
tcx: self.tcx(),
ty_op: |_| err,
@@ -2482,7 +2465,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
&mut self,
impl_def_id: DefId,
impl_trait_ref: EarlyBinder<ty::TraitRef<'tcx>>,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> Result<Normalized<'tcx, SubstsRef<'tcx>>, ()> {
let placeholder_obligation =
self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
@@ -2540,7 +2523,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
/// result from the normalization.
fn match_where_clause_trait_ref(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
where_clause_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
self.match_poly_trait_ref(obligation, where_clause_trait_ref)
@@ -2551,7 +2534,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn match_poly_trait_ref(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
self.infcx
@@ -2577,7 +2560,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn push_stack<'o>(
&mut self,
previous_stack: TraitObligationStackList<'o, 'tcx>,
- obligation: &'o TraitObligation<'tcx>,
+ obligation: &'o PolyTraitObligation<'tcx>,
) -> TraitObligationStack<'o, 'tcx> {
let fresh_trait_pred = obligation.predicate.fold_with(&mut self.freshener);
@@ -2596,7 +2579,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn closure_trait_ref_unnormalized(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
substs: SubstsRef<'tcx>,
) -> ty::PolyTraitRef<'tcx> {
let closure_sig = substs.as_closure().sig();
@@ -2670,7 +2653,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}))
})
};
- let predicate = normalize_with_depth_to(
+ let clause = normalize_with_depth_to(
self,
param_env,
cause.clone(),
@@ -2678,7 +2661,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
predicate,
&mut obligations,
);
- obligations.push(Obligation { cause, recursion_depth, param_env, predicate });
+ obligations.push(Obligation {
+ cause,
+ recursion_depth,
+ param_env,
+ predicate: clause.as_predicate(),
+ });
}
obligations
@@ -3029,7 +3017,7 @@ fn bind_generator_hidden_types_above<'tcx>(
kind: ty::BrAnon(None),
};
counter += 1;
- tcx.mk_re_late_bound(current_depth, br)
+ ty::Region::new_late_bound(tcx, current_depth, br)
}
r => bug!("unexpected region: {r:?}"),
})
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 9a4b72013..96f128741 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -238,7 +238,7 @@ fn fulfill_implication<'tcx>(
// Needs to be `in_snapshot` because this function is used to rebase
// substitutions, which may happen inside of a select within a probe.
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
// attempt to prove all of the predicates for impl2 given those for impl1
// (which are packed up in penv)
ocx.register_obligations(obligations.chain(more_obligations));
@@ -508,21 +508,14 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
for (mut p, _) in predicates {
- if let Some(poly_trait_ref) = p.to_opt_poly_trait_pred() {
+ if let Some(poly_trait_ref) = p.as_trait_clause() {
if Some(poly_trait_ref.def_id()) == sized_trait {
types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder());
continue;
}
if ty::BoundConstness::ConstIfConst == poly_trait_ref.skip_binder().constness {
- let new_trait_pred = poly_trait_ref.map_bound(|mut trait_pred| {
- trait_pred.constness = ty::BoundConstness::NotConst;
- trait_pred
- });
-
- p = tcx.mk_predicate(
- new_trait_pred.map_bound(|p| ty::PredicateKind::Clause(ty::Clause::Trait(p))),
- )
+ p = p.without_const(tcx);
}
}
pretty_predicates.push(p.to_string());
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index e38ae9381..420f8c5dc 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -34,24 +34,7 @@ pub fn search_for_structural_match_violation<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
- ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false })
- .break_value()
-}
-
-/// This method traverses the structure of `ty`, trying to find any
-/// types that are not allowed to be used in a const generic.
-///
-/// This is either because the type does not implement `StructuralEq`
-/// and `StructuralPartialEq`, or because the type is intentionally
-/// not supported in const generics (such as floats and raw pointers,
-/// which are allowed in match blocks).
-pub fn search_for_adt_const_param_violation<'tcx>(
- span: Span,
- tcx: TyCtxt<'tcx>,
- ty: Ty<'tcx>,
-) -> Option<Ty<'tcx>> {
- ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true })
- .break_value()
+ ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value()
}
/// This implements the traversal over the structure of a given type to try to
@@ -65,11 +48,6 @@ struct Search<'tcx> {
/// Tracks ADTs previously encountered during search, so that
/// we will not recur on them again.
seen: FxHashSet<hir::def_id::DefId>,
-
- // Additionally deny things that have been allowed in patterns,
- // but are not allowed in adt const params, such as floats and
- // fn ptrs.
- adt_const_param: bool,
}
impl<'tcx> Search<'tcx> {
@@ -124,41 +102,29 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> {
}
ty::FnPtr(..) => {
- if !self.adt_const_param {
- return ControlFlow::Continue(());
- } else {
- return ControlFlow::Break(ty);
- }
+ return ControlFlow::Continue(());
}
ty::RawPtr(..) => {
- if !self.adt_const_param {
- // structural-match ignores substructure of
- // `*const _`/`*mut _`, so skip `super_visit_with`.
- //
- // For example, if you have:
- // ```
- // struct NonStructural;
- // #[derive(PartialEq, Eq)]
- // struct T(*const NonStructural);
- // const C: T = T(std::ptr::null());
- // ```
- //
- // Even though `NonStructural` does not implement `PartialEq`,
- // structural equality on `T` does not recur into the raw
- // pointer. Therefore, one can still use `C` in a pattern.
- return ControlFlow::Continue(());
- } else {
- return ControlFlow::Break(ty);
- }
+ // structural-match ignores substructure of
+ // `*const _`/`*mut _`, so skip `super_visit_with`.
+ //
+ // For example, if you have:
+ // ```
+ // struct NonStructural;
+ // #[derive(PartialEq, Eq)]
+ // struct T(*const NonStructural);
+ // const C: T = T(std::ptr::null());
+ // ```
+ //
+ // Even though `NonStructural` does not implement `PartialEq`,
+ // structural equality on `T` does not recur into the raw
+ // pointer. Therefore, one can still use `C` in a pattern.
+ return ControlFlow::Continue(());
}
ty::Float(_) => {
- if !self.adt_const_param {
- return ControlFlow::Continue(());
- } else {
- return ControlFlow::Break(ty);
- }
+ return ControlFlow::Continue(());
}
ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
index af8dd0da5..84746eba3 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
@@ -21,7 +21,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
assert!(!ty.is_ty_var(), "should have resolved vars before calling");
- if self.infcx.tcx.trait_solver_next() {
+ if self.infcx.next_trait_solver() {
while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() {
let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::NormalizeProjectionType,
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 82f3df401..302b6cacf 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -41,7 +41,12 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> {
/// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate
/// trait aliases.
- pub fn label_with_exp_info(&self, diag: &mut Diagnostic, top_label: &str, use_desc: &str) {
+ pub fn label_with_exp_info(
+ &self,
+ diag: &mut Diagnostic,
+ top_label: &'static str,
+ use_desc: &str,
+ ) {
diag.span_label(self.top().1, top_label);
if self.path.len() > 1 {
for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) {
@@ -120,7 +125,7 @@ impl<'tcx> TraitAliasExpander<'tcx> {
let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
pred.subst_supertrait(tcx, &trait_ref)
- .to_opt_poly_trait_pred()
+ .as_trait_clause()
.map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span))
});
debug!("expand_trait_aliases: items={:?}", items.clone().collect::<Vec<_>>());
@@ -177,7 +182,7 @@ impl Iterator for SupertraitDefIds<'_> {
predicates
.predicates
.iter()
- .filter_map(|(pred, _)| pred.to_opt_poly_trait_pred())
+ .filter_map(|(pred, _)| pred.as_trait_clause())
.map(|trait_ref| trait_ref.def_id())
.filter(|&super_def_id| visited.insert(super_def_id)),
);
@@ -238,12 +243,12 @@ pub fn upcast_choices<'tcx>(
/// `object.upcast_trait_ref`) within the vtable for `object`.
pub fn get_vtable_index_of_object_method<'tcx, N>(
tcx: TyCtxt<'tcx>,
- object: &super::ImplSourceObjectData<'tcx, N>,
+ object: &super::ImplSourceObjectData<N>,
method_def_id: DefId,
) -> Option<usize> {
// Count number of methods preceding the one we are selecting and
// add them to the total offset.
- tcx.own_existential_vtable_entries(object.upcast_trait_ref.def_id())
+ tcx.own_existential_vtable_entries(tcx.parent(method_def_id))
.iter()
.copied()
.position(|def_id| def_id == method_def_id)
@@ -260,7 +265,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>(
assert!(!self_ty.has_escaping_bound_vars());
let arguments_tuple = match tuple_arguments {
TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
- TupleArgumentsFlag::Yes => tcx.mk_tup(sig.skip_binder().inputs()),
+ TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()),
};
let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]);
sig.map_bound(|sig| (trait_ref, sig.output()))
@@ -290,10 +295,51 @@ pub fn future_trait_ref_and_outputs<'tcx>(
pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
assoc_item.defaultness(tcx).is_final()
- && tcx.impl_defaultness(assoc_item.container_id(tcx)).is_final()
+ && tcx.defaultness(assoc_item.container_id(tcx)).is_final()
}
pub enum TupleArgumentsFlag {
Yes,
No,
}
+
+// Verify that the trait item and its implementation have compatible substs lists
+pub fn check_substs_compatible<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ assoc_item: ty::AssocItem,
+ substs: ty::SubstsRef<'tcx>,
+) -> bool {
+ fn check_substs_compatible_inner<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ generics: &'tcx ty::Generics,
+ args: &'tcx [ty::GenericArg<'tcx>],
+ ) -> bool {
+ if generics.count() != args.len() {
+ return false;
+ }
+
+ let (parent_args, own_args) = args.split_at(generics.parent_count);
+
+ if let Some(parent) = generics.parent
+ && let parent_generics = tcx.generics_of(parent)
+ && !check_substs_compatible_inner(tcx, parent_generics, parent_args) {
+ return false;
+ }
+
+ for (param, arg) in std::iter::zip(&generics.params, own_args) {
+ match (&param.kind, arg.unpack()) {
+ (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
+ | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
+ | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
+ _ => return false,
+ }
+ }
+
+ true
+ }
+
+ let generics = tcx.generics_of(assoc_item.def_id);
+ // Chop off any additional substs (RPITIT) substs
+ let substs = &substs[0..generics.count().min(substs.len())];
+ check_substs_compatible_inner(tcx, generics, substs)
+}
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index cc674ceee..1f83f1f44 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -15,13 +15,13 @@ use std::fmt::Debug;
use std::ops::ControlFlow;
#[derive(Clone, Debug)]
-pub(super) enum VtblSegment<'tcx> {
+pub enum VtblSegment<'tcx> {
MetadataDSA,
TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
}
/// Prepare the segments for a vtable
-pub(super) fn prepare_vtable_segments<'tcx, T>(
+pub fn prepare_vtable_segments<'tcx, T>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
@@ -115,7 +115,7 @@ pub(super) fn prepare_vtable_segments<'tcx, T>(
.predicates
.into_iter()
.filter_map(move |(pred, _)| {
- pred.subst_supertrait(tcx, &inner_most_trait_ref).to_opt_poly_trait_pred()
+ pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause()
});
'diving_in_skip_visited_traits: loop {
@@ -362,7 +362,7 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]);
- match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), ty::Binder::dummy(trait_ref))) {
+ match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => {
implsrc_traitcasting.vtable_vptr_slot
}
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 086ab32b5..d81722ce2 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -77,12 +77,19 @@ pub fn unnormalized_obligations<'tcx>(
param_env: ty::ParamEnv<'tcx>,
arg: GenericArg<'tcx>,
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
+ debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
+
+ // However, if `arg` IS an unresolved inference variable, returns `None`,
+ // because we are not able to make any progress at all. This is to prevent
+ // "livelock" where we say "$0 is WF if $0 is WF".
+ if arg.is_non_region_infer() {
+ return None;
+ }
+
if let ty::GenericArgKind::Lifetime(..) = arg.unpack() {
return Some(vec![]);
}
- debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
-
let mut wf = WfPredicates {
infcx,
param_env,
@@ -142,29 +149,32 @@ pub fn predicate_obligations<'tcx>(
// It's ok to skip the binder here because wf code is prepared for it
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(t)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
wf.compute_trait_pred(&t, Elaborate::None);
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => {}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty, _reg))) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => {}
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
+ ty,
+ _reg,
+ ))) => {
wf.compute(ty.into());
}
- ty::PredicateKind::Clause(ty::Clause::Projection(t)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(t)) => {
wf.compute_projection(t.projection_ty);
wf.compute(match t.term.unpack() {
ty::TermKind::Ty(ty) => ty.into(),
ty::TermKind::Const(c) => c.into(),
})
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
wf.compute(ct.into());
wf.compute(ty.into());
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
wf.compute(arg);
}
- ty::PredicateKind::ConstEvaluatable(ct) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
wf.compute(ct.into());
}
@@ -174,8 +184,7 @@ pub fn predicate_obligations<'tcx>(
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => {
+ | ty::PredicateKind::AliasRelate(..) => {
bug!("We should only wf check where clauses, unexpected predicate: {predicate:?}")
}
}
@@ -247,7 +256,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
// It is fine to skip the binder as we don't care about regions here.
match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) => {
// The obligation comes not from the current `impl` nor the `trait` being implemented,
// but rather from a "second order" obligation, where an associated type has a
// projection coming from another associated type. See
@@ -264,7 +273,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
cause.span = impl_item_span;
}
}
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
// An associated item obligation born out of the `trait` failed to be met. An example
// can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`.
debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred);
@@ -293,6 +302,16 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
}
fn normalize(self, infcx: &InferCtxt<'tcx>) -> Vec<traits::PredicateObligation<'tcx>> {
+ // Do not normalize `wf` obligations with the new solver.
+ //
+ // The current deep normalization routine with the new solver does not
+ // handle ambiguity and the new solver correctly deals with unnnormalized goals.
+ // If the user relies on normalized types, e.g. for `fn implied_outlives_bounds`,
+ // it is their responsibility to normalize while avoiding ambiguity.
+ if infcx.next_trait_solver() {
+ return self.out;
+ }
+
let cause = self.cause(traits::WellFormed(None));
let param_env = self.param_env;
let mut obligations = Vec::with_capacity(self.out.len());
@@ -386,7 +405,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
depth,
param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ arg,
+ ))),
)
}),
);
@@ -478,7 +499,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause.clone(),
depth,
param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ arg,
+ ))),
)
}),
);
@@ -521,8 +544,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let obligations = self.nominal_obligations(uv.def, uv.substs);
self.out.extend(obligations);
- let predicate =
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
+ let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::ConstEvaluatable(ct),
+ ));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
@@ -541,7 +565,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
self.recursion_depth,
self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
+ ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::WellFormed(ct.into()),
+ )),
));
}
ty::ConstKind::Expr(_) => {
@@ -552,8 +578,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// the future we may allow directly lowering to `ConstKind::Expr` in which case
// we would not be proving bounds we should.
- let predicate =
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
+ let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::ConstEvaluatable(ct),
+ ));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
@@ -654,9 +681,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
depth,
param_env,
- ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
- ty::OutlivesPredicate(rty, r),
- ))),
+ ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)),
+ )),
));
}
}
@@ -731,6 +758,11 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
}
}
+ ty::Alias(ty::Weak, ty::AliasTy { def_id, substs, .. }) => {
+ let obligations = self.nominal_obligations(def_id, substs);
+ self.out.extend(obligations);
+ }
+
ty::Dynamic(data, r, _) => {
// WfObject
//
@@ -779,7 +811,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
self.recursion_depth,
param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ ty.into(),
+ ))),
));
}
}
@@ -918,7 +952,7 @@ pub fn object_region_bounds<'tcx>(
// Since we don't actually *know* the self type for an object,
// this "open(err)" serves as a kind of dummy standin -- basically
// a placeholder type.
- let open_ty = tcx.mk_fresh_ty(0);
+ let open_ty = Ty::new_fresh(tcx, 0);
let predicates = existential_predicates.iter().filter_map(|predicate| {
if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() {
@@ -951,7 +985,7 @@ pub fn object_region_bounds<'tcx>(
pub(crate) fn required_region_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
erased_self_ty: Ty<'tcx>,
- predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
+ predicates: impl Iterator<Item = ty::Clause<'tcx>>,
) -> Vec<ty::Region<'tcx>> {
assert!(!erased_self_ty.has_escaping_bound_vars());
@@ -959,24 +993,7 @@ pub(crate) fn required_region_bounds<'tcx>(
.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(..))
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
- ref t,
- ref r,
- ))) => {
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => {
// Search for a bound of the form `erased_self_ty
// : 'a`, but be wary of something like `for<'a>
// erased_self_ty : 'a` (we interpret a
@@ -992,6 +1009,12 @@ pub(crate) fn required_region_bounds<'tcx>(
None
}
}
+ ty::ClauseKind::Trait(_)
+ | ty::ClauseKind::RegionOutlives(_)
+ | ty::ClauseKind::Projection(_)
+ | ty::ClauseKind::ConstArgHasType(_, _)
+ | ty::ClauseKind::WellFormed(_)
+ | ty::ClauseKind::ConstEvaluatable(_) => None,
}
})
.collect()