summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection')
-rw-r--r--compiler/rustc_trait_selection/Cargo.toml2
-rw-r--r--compiler/rustc_trait_selection/messages.ftl14
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs8
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs14
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs188
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs11
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonicalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs144
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs128
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs99
-rw-r--r--compiler/rustc_trait_selection/src/solve/opaques.rs67
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs120
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/cache.rs8
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs56
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs168
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs102
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs1382
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs34
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs545
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs103
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs65
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs43
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs54
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs232
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs269
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs123
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs68
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs181
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs68
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs24
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs16
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs50
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs138
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs89
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs71
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_normalize.rs55
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs19
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs135
51 files changed, 3314 insertions, 1722 deletions
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index d3eba43b4..83605627d 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -14,6 +14,7 @@ rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_hir = { path = "../rustc_hir" }
+rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_macros = { path = "../rustc_macros" }
@@ -24,4 +25,3 @@ rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-itertools = "0.10.1"
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 14eb4a550..217ba71b6 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -1,17 +1,13 @@
trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
-trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated}
-
trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
.label = empty on-clause here
+trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}`
+
trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]`
.label = invalid on-clause here
-trait_selection_no_value_in_rustc_on_unimplemented = this attribute must have a valid value
- .label = expected value here
- .note = eg `#[rustc_on_unimplemented(message="foo")]`
-
trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc ->
[none] {""}
*[default] {" "}for type `{$self_desc}`
@@ -20,3 +16,9 @@ trait_selection_negative_positive_conflict = found both positive and negative im
.negative_implementation_in_crate = negative implementation in crate `{$negative_impl_cname}`
.positive_implementation_here = positive implementation here
.positive_implementation_in_crate = positive implementation in crate `{$positive_impl_cname}`
+
+trait_selection_no_value_in_rustc_on_unimplemented = this attribute must have a valid value
+ .label = expected value here
+ .note = eg `#[rustc_on_unimplemented(message="foo")]`
+
+trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated}
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index df7c4df18..54e22cc3d 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -89,3 +89,11 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> {
diag
}
}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_inherent_projection_normalization_overflow)]
+pub struct InherentProjectionNormalizationOverflow {
+ #[primary_span]
+ pub span: Span,
+ pub ty: String,
+}
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 911cc0b88..312bd3817 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -5,7 +5,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse};
-use rustc_middle::traits::query::Fallible;
+use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_middle::ty::{GenericArg, ToPredicate};
use rustc_span::DUMMY_SP;
@@ -41,7 +41,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
let ty = self.resolve_vars_if_possible(ty);
- if !(param_env, ty).needs_infer() {
+ if !(param_env, ty).has_infer() {
return ty.is_copy_modulo_regions(self.tcx, param_env);
}
@@ -66,7 +66,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
param_env: ty::ParamEnv<'tcx>,
) -> traits::EvaluationResult {
- let trait_ref = self.tcx.mk_trait_ref(trait_def_id, params);
+ let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, params);
let obligation = traits::Obligation {
cause: traits::ObligationCause::dummy(),
@@ -82,8 +82,8 @@ pub trait InferCtxtBuilderExt<'tcx> {
fn enter_canonical_trait_query<K, R>(
&mut self,
canonical_key: &Canonical<'tcx, K>,
- operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Fallible<R>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, R>>
+ operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>,
+ ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution>
where
K: TypeFoldable<TyCtxt<'tcx>>,
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
@@ -110,8 +110,8 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
fn enter_canonical_trait_query<K, R>(
&mut self,
canonical_key: &Canonical<'tcx, K>,
- operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Fallible<R>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, R>>
+ operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>,
+ ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution>
where
K: TypeFoldable<TyCtxt<'tcx>>,
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index f866cb016..ed3994be9 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -37,7 +37,7 @@ extern crate rustc_middle;
extern crate smallvec;
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
-use rustc_macros::fluent_messages;
+use rustc_fluent_macro::fluent_messages;
pub mod errors;
pub mod infer;
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 10d817f75..f32ff0442 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -2,12 +2,12 @@
use super::search_graph::OverflowHandler;
use super::{EvalCtxt, SolverMode};
-use crate::solve::CanonicalResponseExt;
use crate::traits::coherence;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def_id::DefId;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::elaborate;
+use rustc_infer::traits::Reveal;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
use rustc_middle::ty::fast_reject::TreatProjections;
use rustc_middle::ty::TypeFoldable;
@@ -51,7 +51,7 @@ pub(super) enum CandidateSource {
BuiltinImpl,
/// An assumption from the environment.
///
- /// More precicely we've used the `n-th` assumption in the `param_env`.
+ /// More precisely we've used the `n-th` assumption in the `param_env`.
///
/// ## Examples
///
@@ -87,7 +87,9 @@ pub(super) enum CandidateSource {
}
/// Methods used to assemble candidates for either trait or projection goals.
-pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
+pub(super) trait GoalKind<'tcx>:
+ TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
+{
fn self_ty(self) -> Ty<'tcx>;
fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>;
@@ -96,6 +98,17 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
+ // Try equating an assumption predicate against a goal's predicate. If it
+ // holds, then execute the `then` callback, which should do any additional
+ // work, then produce a response (typically by executing
+ // [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
+ fn probe_and_match_goal_against_assumption(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ assumption: ty::Predicate<'tcx>,
+ then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
+ ) -> QueryResult<'tcx>;
+
// Consider a clause, which consists of a "assumption" and some "requirements",
// to satisfy a goal. If the requirements hold, then attempt to satisfy our
// goal by equating it with the assumption.
@@ -104,7 +117,26 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
goal: Goal<'tcx, Self>,
assumption: ty::Predicate<'tcx>,
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
- ) -> QueryResult<'tcx>;
+ ) -> QueryResult<'tcx> {
+ Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
+ ecx.add_goals(requirements);
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+
+ /// Consider a bound originating from the item bounds of an alias. For this we
+ /// require that the well-formed requirements of the self type of the goal
+ /// are "satisfied from the param-env".
+ /// See [`EvalCtxt::validate_alias_bound_self_from_param_env`].
+ fn consider_alias_bound_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ assumption: ty::Predicate<'tcx>,
+ ) -> QueryResult<'tcx> {
+ Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
+ ecx.validate_alias_bound_self_from_param_env(goal)
+ })
+ }
// Consider a clause specifically for a `dyn Trait` self type. This requires
// additionally checking all of the supertraits and object bounds to hold,
@@ -113,7 +145,25 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
assumption: ty::Predicate<'tcx>,
- ) -> QueryResult<'tcx>;
+ ) -> QueryResult<'tcx> {
+ Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
+ let tcx = ecx.tcx();
+ let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
+ bug!("expected object type in `consider_object_bound_candidate`");
+ };
+ ecx.add_goals(
+ structural_traits::predicates_for_object_candidate(
+ &ecx,
+ goal.param_env,
+ goal.predicate.trait_ref(tcx),
+ bounds,
+ )
+ .into_iter()
+ .map(|pred| goal.with(tcx, pred)),
+ );
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
fn consider_impl_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
@@ -241,7 +291,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule,
// object bound, alias bound, etc. We are unable to determine this until we can at
- // least structually resolve the type one layer.
+ // least structurally resolve the type one layer.
if goal.predicate.self_ty().is_ty_var() {
return vec![Candidate {
source: CandidateSource::BuiltinImpl,
@@ -282,8 +332,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
candidates: &mut Vec<Candidate<'tcx>>,
) {
let tcx = self.tcx();
- // FIXME: We also have to normalize opaque types, not sure where to best fit that in.
- let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
+ let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else {
return
};
@@ -305,8 +354,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}),
);
ecx.add_goal(normalizes_to_goal);
- let _ = ecx.try_evaluate_added_goals()?;
+ 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.
@@ -455,15 +507,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
| ty::Param(_)
| ty::Placeholder(..)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+ | ty::Alias(ty::Inherent, _)
| ty::Error(_) => return,
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
| ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
- ty::Alias(_, alias_ty) => alias_ty,
+ // Excluding IATs here as they don't have meaningful item bounds.
+ ty::Alias(ty::Projection | ty::Opaque, alias_ty) => alias_ty,
};
for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs)
{
- match G::consider_implied_clause(self, goal, assumption, []) {
+ match G::consider_alias_bound_candidate(self, goal, assumption) {
Ok(result) => {
candidates.push(Candidate { source: CandidateSource::AliasBound, result })
}
@@ -472,6 +526,105 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}
+ /// Check that we are allowed to use an alias bound originating from the self
+ /// type of this goal. This means something different depending on the self type's
+ /// alias kind.
+ ///
+ /// * Projection: Given a goal with a self type such as `<Ty as Trait>::Assoc`,
+ /// we require that the bound `Ty: Trait` can be proven using either a nested alias
+ /// bound candidate, or a param-env candidate.
+ ///
+ /// * Opaque: The param-env must be in `Reveal::UserFacing` mode. Otherwise,
+ /// the goal should be proven by using the hidden type instead.
+ #[instrument(level = "debug", skip(self), ret)]
+ pub(super) fn validate_alias_bound_self_from_param_env<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ ) -> QueryResult<'tcx> {
+ match *goal.predicate.self_ty().kind() {
+ ty::Alias(ty::Projection, projection_ty) => {
+ let mut param_env_candidates = vec![];
+ let self_trait_ref = projection_ty.trait_ref(self.tcx());
+
+ if self_trait_ref.self_ty().is_ty_var() {
+ return self
+ .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ }
+
+ let trait_goal: Goal<'_, ty::TraitPredicate<'tcx>> = goal.with(
+ self.tcx(),
+ ty::TraitPredicate {
+ trait_ref: self_trait_ref,
+ constness: ty::BoundConstness::NotConst,
+ polarity: ty::ImplPolarity::Positive,
+ },
+ );
+
+ self.assemble_param_env_candidates(trait_goal, &mut param_env_candidates);
+ // FIXME: We probably need some sort of recursion depth check here.
+ // Can't come up with an example yet, though, and the worst case
+ // we can have is a compiler stack overflow...
+ self.assemble_alias_bound_candidates(trait_goal, &mut param_env_candidates);
+
+ // FIXME: We must also consider alias-bound candidates for a peculiar
+ // class of built-in candidates that I'll call "defaulted" built-ins.
+ //
+ // For example, we always know that `T: Pointee` is implemented, but
+ // we do not always know what `<T as Pointee>::Metadata` actually is,
+ // similar to if we had a user-defined impl with a `default type ...`.
+ // For these traits, since we're not able to always normalize their
+ // associated types to a concrete type, we must consider their alias bounds
+ // instead, so we can prove bounds such as `<T as Pointee>::Metadata: Copy`.
+ self.assemble_alias_bound_candidates_for_builtin_impl_default_items(
+ trait_goal,
+ &mut param_env_candidates,
+ );
+
+ self.merge_candidates(param_env_candidates)
+ }
+ ty::Alias(ty::Opaque, _opaque_ty) => match goal.param_env.reveal() {
+ Reveal::UserFacing => {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+ Reveal::All => return Err(NoSolution),
+ },
+ _ => bug!("only expected to be called on alias tys"),
+ }
+ }
+
+ /// Assemble a subset of builtin impl candidates for a class of candidates called
+ /// "defaulted" built-in traits.
+ ///
+ /// For example, we always know that `T: Pointee` is implemented, but we do not
+ /// always know what `<T as Pointee>::Metadata` actually is! See the comment in
+ /// [`EvalCtxt::validate_alias_bound_self_from_param_env`] for more detail.
+ #[instrument(level = "debug", skip_all)]
+ fn assemble_alias_bound_candidates_for_builtin_impl_default_items<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
+ ) {
+ let lang_items = self.tcx().lang_items();
+ let trait_def_id = goal.predicate.trait_def_id(self.tcx());
+
+ // You probably shouldn't add anything to this list unless you
+ // know what you're doing.
+ let result = if lang_items.pointee_trait() == Some(trait_def_id) {
+ G::consider_builtin_pointee_candidate(self, goal)
+ } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
+ G::consider_builtin_discriminant_kind_candidate(self, goal)
+ } else {
+ Err(NoSolution)
+ };
+
+ match result {
+ Ok(result) => {
+ candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
+ }
+ Err(NoSolution) => (),
+ }
+ }
+
#[instrument(level = "debug", skip_all)]
fn assemble_object_bound_candidates<G: GoalKind<'tcx>>(
&mut self,
@@ -590,13 +743,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
SolverMode::Normal => {
let param_env_responses = candidates
.iter()
- .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
+ .filter(|c| {
+ matches!(
+ c.source,
+ CandidateSource::ParamEnv(_) | CandidateSource::AliasBound
+ )
+ })
.map(|c| c.result)
.collect::<Vec<_>>();
if let Some(result) = self.try_merge_responses(&param_env_responses) {
- if result.has_only_region_constraints() {
- return Ok(result);
- }
+ // We strongly prefer alias and param-env bounds here, even if they affect inference.
+ // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/11.
+ return Ok(result);
}
}
}
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 1a566e87d..0ede32c75 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -33,7 +33,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
- | ty::Alias(ty::Projection, ..)
+ | ty::Alias(ty::Projection | ty::Inherent, ..)
| ty::Placeholder(..)
| ty::Bound(..)
| ty::Infer(_) => {
@@ -91,14 +91,15 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>(
) -> ty::Binder<'tcx, Ty<'tcx>> {
debug_assert!(!ty.has_late_bound_regions());
let mut counter = 0;
- let ty = tcx.fold_regions(ty, |mut r, current_depth| {
- if let ty::ReErased = r.kind() {
+ let ty = tcx.fold_regions(ty, |r, current_depth| match r.kind() {
+ ty::ReErased => {
let br =
ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon(None) };
counter += 1;
- r = tcx.mk_re_late_bound(current_depth, br);
+ tcx.mk_re_late_bound(current_depth, br)
}
- r
+ // All free regions should be erased here.
+ r => bug!("unexpected region: {r:?}"),
});
let bound_vars = tcx.mk_bound_variable_kinds_from_iter(
(0..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon(None))),
diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
index 976849696..ff4bff10c 100644
--- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
@@ -69,7 +69,7 @@ impl<'a, 'tcx> Canonicalizer<'a, 'tcx> {
};
let value = value.fold_with(&mut canonicalizer);
- assert!(!value.needs_infer());
+ assert!(!value.has_infer());
assert!(!value.has_placeholders());
let (max_universe, variables) = canonicalizer.finalize();
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index c29b5b04e..f91c67277 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -1,14 +1,19 @@
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{
- DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt,
+ DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, RegionVariableOrigin,
+ TyCtxtInferExt,
};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::ObligationCause;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
-use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
+use rustc_middle::traits::solve::{
+ CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques,
+ PredefinedOpaquesData, QueryResult,
+};
+use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
TypeVisitor,
@@ -43,6 +48,9 @@ pub struct EvalCtxt<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
pub(super) var_values: CanonicalVarValues<'tcx>,
+
+ predefined_opaques_in_body: PredefinedOpaques<'tcx>,
+
/// The highest universe index nameable by the caller.
///
/// When we enter a new binder inside of the query we create new universes
@@ -57,6 +65,14 @@ pub struct EvalCtxt<'a, 'tcx> {
pub(super) search_graph: &'a mut SearchGraph<'tcx>,
pub(super) nested_goals: NestedGoals<'tcx>,
+
+ // Has this `EvalCtxt` errored out with `NoSolution` in `try_evaluate_added_goals`?
+ //
+ // If so, then it can no longer be used to make a canonical query response,
+ // since subsequent calls to `try_evaluate_added_goals` have possibly dropped
+ // ambiguous goals. Instead, a probe needs to be introduced somewhere in the
+ // evaluation code.
+ tainted: Result<(), NoSolution>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -117,10 +133,16 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
let mut ecx = EvalCtxt {
search_graph: &mut search_graph,
infcx: self,
+ // Only relevant when canonicalizing the response,
+ // which we don't do within this evaluation context.
+ predefined_opaques_in_body: self
+ .tcx
+ .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
// Only relevant when canonicalizing the response.
max_input_universe: ty::UniverseIndex::ROOT,
var_values: CanonicalVarValues::dummy(),
nested_goals: NestedGoals::new(),
+ tainted: Ok(()),
};
let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
@@ -152,28 +174,53 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
fn evaluate_canonical_goal(
tcx: TyCtxt<'tcx>,
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
- canonical_goal: CanonicalGoal<'tcx>,
+ canonical_input: CanonicalInput<'tcx>,
) -> QueryResult<'tcx> {
// Deal with overflow, caching, and coinduction.
//
// The actual solver logic happens in `ecx.compute_goal`.
- search_graph.with_new_goal(tcx, canonical_goal, |search_graph| {
+ 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, goal, var_values) = tcx
+ let (ref infcx, input, var_values) = tcx
.infer_ctxt()
.intercrate(intercrate)
- .build_with_canonical(DUMMY_SP, &canonical_goal);
+ .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,
- max_input_universe: canonical_goal.max_universe,
+ predefined_opaques_in_body: input.predefined_opaques_in_body,
+ max_input_universe: canonical_input.max_universe,
search_graph,
nested_goals: NestedGoals::new(),
+ tainted: Ok(()),
};
- ecx.compute_goal(goal)
+
+ 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
})
}
@@ -188,7 +235,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let canonical_response =
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
- let has_changed = !canonical_response.value.var_values.is_identity();
+ let 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(
goal.param_env,
orig_values,
@@ -213,18 +261,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
{
debug!("rerunning goal to check result is stable");
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
- let canonical_response =
+ let new_canonical_response =
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
- if !canonical_response.value.var_values.is_identity() {
+ if !new_canonical_response.value.var_values.is_identity() {
bug!(
"unstable result: re-canonicalized goal={canonical_goal:#?} \
- response={canonical_response:#?}"
+ first_response={canonical_response:#?} \
+ second_response={new_canonical_response:#?}"
);
}
- if certainty != canonical_response.value.certainty {
+ if certainty != new_canonical_response.value.certainty {
bug!(
"unstable certainty: {certainty:#?} re-canonicalized goal={canonical_goal:#?} \
- response={canonical_response:#?}"
+ first_response={canonical_response:#?} \
+ second_response={new_canonical_response:#?}"
);
}
}
@@ -391,6 +441,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
},
);
+ if response.is_err() {
+ self.tainted = Err(NoSolution);
+ }
+
self.nested_goals = goals;
response
}
@@ -401,9 +455,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
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))
}
@@ -419,6 +475,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
})
}
+ pub(super) fn next_region_infer(&self) -> ty::Region<'tcx> {
+ self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP))
+ }
+
pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> {
self.infcx.next_const_var(
ty,
@@ -649,7 +709,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
ObligationCause::dummy(),
- ty::Binder::dummy(src_and_dst),
+ src_and_dst,
scope,
assume,
) {
@@ -660,4 +720,56 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
| rustc_transmute::Answer::IfAny(_) => Err(NoSolution),
}
}
+
+ pub(super) fn can_define_opaque_ty(&mut self, def_id: LocalDefId) -> bool {
+ self.infcx.opaque_type_origin(def_id).is_some()
+ }
+
+ pub(super) fn register_opaque_ty(
+ &mut self,
+ a: ty::OpaqueTypeKey<'tcx>,
+ b: Ty<'tcx>,
+ param_env: ty::ParamEnv<'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()));
+ Ok(())
+ }
+
+ // Do something for each opaque/hidden pair defined with `def_id` in the
+ // current inference context.
+ pub(super) fn unify_existing_opaque_tys(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ key: ty::OpaqueTypeKey<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Vec<CanonicalResponse<'tcx>> {
+ // FIXME: Super inefficient to be cloning this...
+ let opaques = self.infcx.clone_opaque_types_for_query_response();
+
+ let mut values = vec![];
+ for (candidate_key, candidate_ty) in opaques {
+ if candidate_key.def_id != key.def_id {
+ continue;
+ }
+ values.extend(self.probe(|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(),
+ 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
+ }
}
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 ada868705..fdb209fbf 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -8,15 +8,19 @@
/// section of the [rustc-dev-guide][c].
///
/// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
-use super::{CanonicalGoal, Certainty, EvalCtxt, Goal};
+use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer};
use crate::solve::{CanonicalResponse, QueryResult, Response};
+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};
-use rustc_middle::ty::{self, GenericArgKind};
+use rustc_middle::traits::solve::{
+ ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
+};
+use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty};
use rustc_span::DUMMY_SP;
use std::iter;
use std::ops::Deref;
@@ -27,13 +31,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn canonicalize_goal(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
- ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalGoal<'tcx>) {
+ ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx>) {
let mut orig_values = Default::default();
let canonical_goal = Canonicalizer::canonicalize(
self.infcx,
CanonicalizeMode::Input,
&mut orig_values,
- goal,
+ QueryInput {
+ goal,
+ anchor: self.infcx.defining_use_anchor,
+ predefined_opaques_in_body: self.tcx().mk_predefined_opaques_in_body(
+ PredefinedOpaquesData {
+ opaque_types: self.infcx.clone_opaque_types_for_query_response(),
+ },
+ ),
+ },
);
(orig_values, canonical_goal)
}
@@ -50,11 +62,36 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
certainty: Certainty,
) -> QueryResult<'tcx> {
let goals_certainty = self.try_evaluate_added_goals()?;
+ assert_eq!(
+ self.tainted,
+ Ok(()),
+ "EvalCtxt is tainted -- nested goals may have been dropped in a \
+ previous call to `try_evaluate_added_goals!`"
+ );
+
let certainty = certainty.unify_with(goals_certainty);
- let external_constraints = self.compute_external_query_constraints()?;
+ let response = match certainty {
+ Certainty::Yes | Certainty::Maybe(MaybeCause::Ambiguity) => {
+ let external_constraints = self.compute_external_query_constraints()?;
+ Response { var_values: self.var_values, external_constraints, certainty }
+ }
+ Certainty::Maybe(MaybeCause::Overflow) => {
+ // If we have overflow, it's probable that we're substituting a type
+ // into itself infinitely and any partial substitutions in the query
+ // response are probably not useful anyways, so just return an empty
+ // query response.
+ //
+ // This may prevent us from potentially useful inference, e.g.
+ // 2 candidates, one ambiguous and one overflow, which both
+ // have the same inference constraints.
+ //
+ // Changing this to retain some constraints in the future
+ // won't be a breaking change, so this is good enough for now.
+ return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow));
+ }
+ };
- let response = Response { var_values: self.var_values, external_constraints, certainty };
let canonical = Canonicalizer::canonicalize(
self.infcx,
CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
@@ -64,6 +101,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Ok(canonical)
}
+ /// Constructs a totally unconstrained, ambiguous response to a goal.
+ ///
+ /// Take care when using this, since often it's useful to respond with
+ /// ambiguity but return constrained variables to guide inference.
+ pub(in crate::solve) fn make_ambiguous_response_no_constraints(
+ &self,
+ maybe_cause: MaybeCause,
+ ) -> CanonicalResponse<'tcx> {
+ let unconstrained_response = Response {
+ var_values: CanonicalVarValues {
+ var_values: self.tcx().mk_substs_from_iter(self.var_values.var_values.iter().map(
+ |arg| -> ty::GenericArg<'tcx> {
+ match arg.unpack() {
+ GenericArgKind::Lifetime(_) => self.next_region_infer().into(),
+ GenericArgKind::Type(_) => self.next_ty_infer().into(),
+ GenericArgKind::Const(ct) => self.next_const_infer(ct.ty()).into(),
+ }
+ },
+ )),
+ },
+ external_constraints: self
+ .tcx()
+ .mk_external_constraints(ExternalConstraintsData::default()),
+ certainty: Certainty::Maybe(maybe_cause),
+ };
+
+ Canonicalizer::canonicalize(
+ self.infcx,
+ CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
+ &mut Default::default(),
+ unconstrained_response,
+ )
+ }
+
#[instrument(level = "debug", skip(self), ret)]
fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
// Cannot use `take_registered_region_obligations` as we may compute the response
@@ -78,7 +149,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
region_constraints,
)
});
- let opaque_types = self.infcx.clone_opaque_types_for_query_response();
+
+ 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, _)| {
+ self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
+ });
+
Ok(self
.tcx()
.mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types }))
@@ -104,10 +181,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?;
- // FIXME: implement external constraints.
- let ExternalConstraintsData { region_constraints, opaque_types: _ } =
+ let ExternalConstraintsData { region_constraints, opaque_types } =
external_constraints.deref();
self.register_region_constraints(region_constraints);
+ self.register_opaque_types(param_env, opaque_types)?;
Ok((certainty, nested_goals))
}
@@ -139,25 +216,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
//
// We therefore instantiate the existential variable in the canonical response with the
// inference variable of the input right away, which is more performant.
- let mut opt_values = vec![None; response.variables.len()];
+ let mut opt_values = IndexVec::from_elem_n(None, response.variables.len());
for (original_value, result_value) in iter::zip(original_values, var_values.var_values) {
match result_value.unpack() {
GenericArgKind::Type(t) => {
if let &ty::Bound(debruijn, b) = t.kind() {
assert_eq!(debruijn, ty::INNERMOST);
- opt_values[b.var.index()] = Some(*original_value);
+ opt_values[b.var] = Some(*original_value);
}
}
GenericArgKind::Lifetime(r) => {
if let ty::ReLateBound(debruijn, br) = *r {
assert_eq!(debruijn, ty::INNERMOST);
- opt_values[br.var.index()] = Some(*original_value);
+ opt_values[br.var] = Some(*original_value);
}
}
GenericArgKind::Const(c) => {
- if let ty::ConstKind::Bound(debrujin, b) = c.kind() {
- assert_eq!(debrujin, ty::INNERMOST);
- opt_values[b.index()] = Some(*original_value);
+ if let ty::ConstKind::Bound(debruijn, b) = c.kind() {
+ assert_eq!(debruijn, ty::INNERMOST);
+ opt_values[b] = Some(*original_value);
}
}
}
@@ -176,11 +253,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// As an optimization we sometimes avoid creating a new inference variable here.
//
// All new inference variables we create start out in the current universe of the caller.
- // This is conceptionally wrong as these inference variables would be able to name
+ // This is conceptually wrong as these inference variables would be able to name
// more placeholders then they should be able to. However the inference variables have
// to "come from somewhere", so by equating them with the original values of the caller
// later on, we pull them down into their correct universe again.
- if let Some(v) = opt_values[index] {
+ if let Some(v) = opt_values[BoundVar::from_usize(index)] {
v
} else {
self.infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe)
@@ -227,4 +304,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let _ = member_constraint;
}
}
+
+ fn register_opaque_types(
+ &mut self,
+ 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;
+ }
+ Ok(())
+ }
}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 32bd10f0b..4a403196c 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -133,12 +133,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::ConstEvaluatable(_)
- | ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous => {
FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
)
}
+ ty::PredicateKind::TypeWellFormedFromEnv(_) => {
+ bug!("unexpected goal: {goal:?}")
+ }
},
root_obligation: obligation,
});
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 19bcbd461..56a254d9c 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -24,6 +24,7 @@ mod assembly;
mod canonicalize;
mod eval_ctxt;
mod fulfill;
+mod opaques;
mod project_goals;
mod search_graph;
mod trait_goals;
@@ -212,7 +213,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
);
}
- match (lhs.to_projection_term(tcx), rhs.to_projection_term(tcx)) {
+ 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
@@ -230,44 +231,60 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let mut candidates = Vec::new();
// LHS normalizes-to RHS
- candidates.extend(
- evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No).ok(),
- );
+ 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).ok(),
- );
+ candidates.extend(evaluate_normalizes_to(
+ self,
+ alias_rhs,
+ lhs,
+ direction,
+ Invert::Yes,
+ ));
// Relate via substs
- candidates.extend(
- self.probe(|ecx| {
- let span = tracing::span!(
- tracing::Level::DEBUG,
- "compute_alias_relate_goal(relate_via_substs)",
- ?alias_lhs,
- ?alias_rhs,
- ?direction
- );
- let _enter = span.enter();
-
- match direction {
- ty::AliasRelationDirection::Equate => {
- ecx.eq(goal.param_env, alias_lhs, alias_rhs)?;
- }
- ty::AliasRelationDirection::Subtype => {
- ecx.sub(goal.param_env, alias_lhs, alias_rhs)?;
- }
+ 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)
- })
- .ok(),
- );
+ 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)
} else {
- self.flounder(&candidates)
+ // 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)
+ }
+ }
}
}
}
@@ -340,17 +357,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if responses.is_empty() {
return Err(NoSolution);
}
- let certainty = responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
- certainty.unify_with(response.value.certainty)
- });
-
- let response = self.evaluate_added_goals_and_make_canonical_response(certainty);
- if let Ok(response) = response {
- assert!(response.has_no_inference_or_external_constraints());
- Ok(response)
- } else {
- bug!("failed to make floundered response: {responses:?}");
- }
+
+ let Certainty::Maybe(maybe_cause) = responses.iter().fold(
+ Certainty::AMBIGUOUS,
+ |certainty, response| {
+ certainty.unify_with(response.value.certainty)
+ },
+ ) else {
+ bug!("expected flounder response to be ambiguous")
+ };
+
+ Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs
new file mode 100644
index 000000000..a5de4ddee
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/opaques.rs
@@ -0,0 +1,67 @@
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::traits::Reveal;
+use rustc_middle::ty;
+use rustc_middle::ty::util::NotUniqueParam;
+
+use super::{EvalCtxt, SolverMode};
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ pub(super) fn normalize_opaque_type(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let opaque_ty = goal.predicate.projection_ty;
+ let expected = goal.predicate.term.ty().expect("no such thing as an opaque const");
+
+ match (goal.param_env.reveal(), self.solver_mode()) {
+ (Reveal::UserFacing, SolverMode::Normal) => {
+ 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".
+ if !self.can_define_opaque_ty(opaque_ty_def_id) {
+ return Err(NoSolution);
+ }
+ // FIXME: This may have issues when the substs contain aliases...
+ match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.substs) {
+ Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
+ return self.evaluate_added_goals_and_make_canonical_response(
+ Certainty::AMBIGUOUS,
+ );
+ }
+ Err(_) => {
+ return Err(NoSolution);
+ }
+ Ok(()) => {}
+ }
+ // Prefer opaques registered already.
+ let matches = self.unify_existing_opaque_tys(goal.param_env, opaque_ty, expected);
+ if !matches.is_empty() {
+ if let Some(response) = self.try_merge_responses(&matches) {
+ return Ok(response);
+ } else {
+ return self.flounder(&matches);
+ }
+ }
+ // Otherwise, define a new opaque type
+ self.register_opaque_ty(opaque_ty, expected, goal.param_env)?;
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+ (Reveal::UserFacing, SolverMode::Coherence) => {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+ }
+ (Reveal::All, _) => {
+ // FIXME: Add an assertion that opaque type storage is empty.
+ let actual = tcx.type_of(opaque_ty.def_id).subst(tcx, opaque_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/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 14cb43b89..7d7dfa2c8 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -22,19 +22,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
&mut self,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> {
- // To only compute normalization once for each projection we only
- // normalize if the expected term is an unconstrained inference variable.
- //
- // 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.
- if self.term_is_fully_unconstrained(goal) {
- let candidates = self.assemble_and_evaluate_candidates(goal);
- self.merge_candidates(candidates)
- } else {
- self.set_normalizes_to_hack_goal(goal);
- self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ match goal.predicate.projection_ty.kind(self.tcx()) {
+ ty::AliasKind::Projection => {
+ // To only compute normalization once for each projection we only
+ // normalize if the expected term is an unconstrained inference variable.
+ //
+ // 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.
+ if self.term_is_fully_unconstrained(goal) {
+ let candidates = self.assemble_and_evaluate_candidates(goal);
+ self.merge_candidates(candidates)
+ } 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"),
}
}
}
@@ -56,11 +62,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
self.trait_def_id(tcx)
}
- fn consider_implied_clause(
+ fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
assumption: ty::Predicate<'tcx>,
- requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'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()
@@ -73,49 +79,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal.predicate.projection_ty,
assumption_projection_pred.projection_ty,
)?;
- ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)?;
- ecx.add_goals(requirements);
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- })
- } else {
- Err(NoSolution)
- }
- }
-
- fn consider_object_bound_candidate(
- ecx: &mut EvalCtxt<'_, 'tcx>,
- goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'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 tcx = ecx.tcx();
-
- 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,
- )?;
-
- let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
- bug!("expected object type in `consider_object_bound_candidate`");
- };
- ecx.add_goals(
- structural_traits::predicates_for_object_candidate(
- &ecx,
- goal.param_env,
- goal.predicate.projection_ty.trait_ref(tcx),
- bounds,
- )
- .into_iter()
- .map(|pred| goal.with(tcx, pred)),
- );
- ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)?;
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ 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)
@@ -164,10 +130,24 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
};
if !assoc_def.item.defaultness(tcx).has_value() {
- tcx.sess.delay_span_bug(
+ 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(
+ 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);
}
// Getting the right substitutions here is complex, e.g. given:
@@ -198,7 +178,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
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 = ty::WithOptConstParam::unknown(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())
@@ -206,7 +186,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
ty.map_bound(|ty| ty.into())
};
- ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))?;
+ 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)
})
}
@@ -271,8 +252,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}
};
- let output_is_sized_pred = tupled_inputs_and_output
- .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
+ let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
+ ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
+ });
let pred = tupled_inputs_and_output
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
@@ -330,10 +312,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
// FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
- let sized_predicate = ty::Binder::dummy(tcx.at(DUMMY_SP).mk_trait_ref(
+ let sized_predicate = ty::TraitRef::from_lang_item(
+ tcx,
LangItem::Sized,
+ DUMMY_SP,
[ty::GenericArg::from(goal.predicate.self_ty())],
- ));
+ );
ecx.add_goal(goal.with(tcx, sized_predicate));
tcx.types.unit
}
@@ -375,7 +359,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
),
};
- ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())?;
+ ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())
+ .expect("expected goal term to be fully unconstrained");
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
@@ -513,7 +498,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
};
ecx.probe(|ecx| {
- ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())?;
+ 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/cache.rs b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
index d1b4fa554..56f126e91 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
@@ -10,8 +10,8 @@
//! before then or if I still haven't done that before January 2023.
use super::StackDepth;
use rustc_data_structures::fx::FxHashMap;
-use rustc_index::vec::IndexVec;
-use rustc_middle::traits::solve::{CanonicalGoal, QueryResult};
+use rustc_index::IndexVec;
+use rustc_middle::traits::solve::{CanonicalInput, QueryResult};
rustc_index::newtype_index! {
pub struct EntryIndex {}
@@ -34,7 +34,7 @@ pub(super) struct ProvisionalEntry<'tcx> {
// The goal for this entry. Should always be equal to the corresponding goal
// in the lookup table.
- pub(super) goal: CanonicalGoal<'tcx>,
+ pub(super) input: CanonicalInput<'tcx>,
}
pub(super) struct ProvisionalCache<'tcx> {
@@ -42,7 +42,7 @@ pub(super) struct ProvisionalCache<'tcx> {
// FIXME: This is only used to quickly check whether a given goal
// is in the cache. We should experiment with using something like
// `SsoHashSet` here because in most cases there are only a few entries.
- pub(super) lookup_table: FxHashMap<CanonicalGoal<'tcx>, EntryIndex>,
+ pub(super) lookup_table: FxHashMap<CanonicalInput<'tcx>, EntryIndex>,
}
impl<'tcx> ProvisionalCache<'tcx> {
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 050269fa9..19e4b2300 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -6,9 +6,9 @@ pub(super) use overflow::OverflowHandler;
use self::cache::ProvisionalEntry;
use cache::ProvisionalCache;
use overflow::OverflowData;
-use rustc_index::vec::IndexVec;
+use rustc_index::IndexVec;
use rustc_middle::dep_graph::DepKind;
-use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
+use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryResult};
use rustc_middle::ty::TyCtxt;
use std::{collections::hash_map::Entry, mem};
@@ -19,7 +19,7 @@ rustc_index::newtype_index! {
}
struct StackElem<'tcx> {
- goal: CanonicalGoal<'tcx>,
+ input: CanonicalInput<'tcx>,
has_been_used: bool,
}
@@ -77,7 +77,7 @@ impl<'tcx> SearchGraph<'tcx> {
}
// ...or it depends on a goal with a lower depth.
- let current_goal = self.stack[stack_depth].goal;
+ let current_goal = self.stack[stack_depth].input;
let entry_index = self.provisional_cache.lookup_table[&current_goal];
self.provisional_cache.entries[entry_index].depth != stack_depth
} else {
@@ -92,20 +92,20 @@ impl<'tcx> SearchGraph<'tcx> {
fn try_push_stack(
&mut self,
tcx: TyCtxt<'tcx>,
- goal: CanonicalGoal<'tcx>,
+ input: CanonicalInput<'tcx>,
) -> Result<(), QueryResult<'tcx>> {
// Look at the provisional cache to check for cycles.
let cache = &mut self.provisional_cache;
- match cache.lookup_table.entry(goal) {
+ match cache.lookup_table.entry(input) {
// No entry, simply push this goal on the stack after dealing with overflow.
Entry::Vacant(v) => {
if self.overflow_data.has_overflow(self.stack.len()) {
- return Err(self.deal_with_overflow(tcx, goal));
+ return Err(self.deal_with_overflow(tcx, input));
}
- let depth = self.stack.push(StackElem { goal, has_been_used: false });
- let response = super::response_no_constraints(tcx, goal, Certainty::Yes);
- let entry_index = cache.entries.push(ProvisionalEntry { response, depth, goal });
+ let depth = self.stack.push(StackElem { input, has_been_used: false });
+ let response = super::response_no_constraints(tcx, input, Certainty::Yes);
+ let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input });
v.insert(entry_index);
Ok(())
}
@@ -135,13 +135,13 @@ impl<'tcx> SearchGraph<'tcx> {
// the stack is enough.
if self.stack.raw[stack_depth.index()..]
.iter()
- .all(|g| g.goal.value.predicate.is_coinductive(tcx))
+ .all(|g| g.input.value.goal.predicate.is_coinductive(tcx))
{
Err(cache.provisional_result(entry_index))
} else {
Err(super::response_no_constraints(
tcx,
- goal,
+ input,
Certainty::Maybe(MaybeCause::Overflow),
))
}
@@ -161,18 +161,18 @@ impl<'tcx> SearchGraph<'tcx> {
/// updated the provisional cache and we have to recompute the current goal.
///
/// FIXME: Refer to the rustc-dev-guide entry once it exists.
- #[instrument(level = "debug", skip(self, actual_goal), ret)]
+ #[instrument(level = "debug", skip(self, actual_input), ret)]
fn try_finalize_goal(
&mut self,
- actual_goal: CanonicalGoal<'tcx>,
+ actual_input: CanonicalInput<'tcx>,
response: QueryResult<'tcx>,
) -> bool {
let stack_elem = self.stack.pop().unwrap();
- let StackElem { goal, has_been_used } = stack_elem;
- assert_eq!(goal, actual_goal);
+ let StackElem { input, has_been_used } = stack_elem;
+ assert_eq!(input, actual_input);
let cache = &mut self.provisional_cache;
- let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap();
+ let provisional_entry_index = *cache.lookup_table.get(&input).unwrap();
let provisional_entry = &mut cache.entries[provisional_entry_index];
// We eagerly update the response in the cache here. If we have to reevaluate
// this goal we use the new response when hitting a cycle, and we definitely
@@ -194,7 +194,7 @@ impl<'tcx> SearchGraph<'tcx> {
cache.entries.truncate(provisional_entry_index.index() + 1);
// ...and finally push our goal back on the stack and reevaluate it.
- self.stack.push(StackElem { goal, has_been_used: false });
+ self.stack.push(StackElem { input, has_been_used: false });
false
} else {
true
@@ -204,17 +204,17 @@ impl<'tcx> SearchGraph<'tcx> {
pub(super) fn with_new_goal(
&mut self,
tcx: TyCtxt<'tcx>,
- canonical_goal: CanonicalGoal<'tcx>,
+ canonical_input: CanonicalInput<'tcx>,
mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
) -> QueryResult<'tcx> {
if self.should_use_global_cache() {
- if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_goal, tcx) {
- debug!(?canonical_goal, ?result, "cache hit");
+ if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
+ debug!(?canonical_input, ?result, "cache hit");
return result;
}
}
- match self.try_push_stack(tcx, canonical_goal) {
+ match self.try_push_stack(tcx, canonical_input) {
Ok(()) => {}
// Our goal is already on the stack, eager return.
Err(response) => return response,
@@ -226,19 +226,19 @@ impl<'tcx> SearchGraph<'tcx> {
let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || {
self.repeat_while_none(
|this| {
- let result = this.deal_with_overflow(tcx, canonical_goal);
+ let result = this.deal_with_overflow(tcx, canonical_input);
let _ = this.stack.pop().unwrap();
result
},
|this| {
let result = loop_body(this);
- this.try_finalize_goal(canonical_goal, result).then(|| result)
+ this.try_finalize_goal(canonical_input, result).then(|| result)
},
)
});
let cache = &mut self.provisional_cache;
- let provisional_entry_index = *cache.lookup_table.get(&canonical_goal).unwrap();
+ let provisional_entry_index = *cache.lookup_table.get(&canonical_input).unwrap();
let provisional_entry = &mut cache.entries[provisional_entry_index];
let depth = provisional_entry.depth;
@@ -254,13 +254,13 @@ impl<'tcx> SearchGraph<'tcx> {
// cycle participants without moving them to the global cache.
let other_cycle_participants = provisional_entry_index.index() + 1;
for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) {
- let actual_index = cache.lookup_table.remove(&entry.goal);
+ let actual_index = cache.lookup_table.remove(&entry.input);
debug_assert_eq!(Some(i), actual_index);
debug_assert!(entry.depth == depth);
}
let current_goal = cache.entries.pop().unwrap();
- let actual_index = cache.lookup_table.remove(&current_goal.goal);
+ let actual_index = cache.lookup_table.remove(&current_goal.input);
debug_assert_eq!(Some(provisional_entry_index), actual_index);
debug_assert!(current_goal.depth == depth);
@@ -274,7 +274,7 @@ impl<'tcx> SearchGraph<'tcx> {
let can_cache = !self.overflow_data.did_overflow() || self.stack.is_empty();
if self.should_use_global_cache() && can_cache {
tcx.new_solver_evaluation_cache.insert(
- current_goal.goal,
+ current_goal.input,
dep_node,
current_goal.response,
);
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index abd11a15a..f722f2813 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -78,16 +78,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
})
}
- fn consider_implied_clause(
+ fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
assumption: ty::Predicate<'tcx>,
- requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'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 and polarity
+ // FIXME: Constness
ecx.probe(|ecx| {
let assumption_trait_pred =
ecx.instantiate_binder_with_infer(poly_trait_pred);
@@ -96,57 +97,21 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
goal.predicate.trait_ref,
assumption_trait_pred.trait_ref,
)?;
- ecx.add_goals(requirements);
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ then(ecx)
})
} else {
Err(NoSolution)
}
}
- fn consider_object_bound_candidate(
+ fn consider_auto_trait_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
) -> QueryResult<'tcx> {
- if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
- && poly_trait_pred.def_id() == goal.predicate.def_id()
- {
- // FIXME: Constness and polarity
- ecx.probe(|ecx| {
- let assumption_trait_pred =
- ecx.instantiate_binder_with_infer(poly_trait_pred);
- ecx.eq(
- goal.param_env,
- goal.predicate.trait_ref,
- assumption_trait_pred.trait_ref,
- )?;
-
- let tcx = ecx.tcx();
- let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
- bug!("expected object type in `consider_object_bound_candidate`");
- };
- ecx.add_goals(
- structural_traits::predicates_for_object_candidate(
- &ecx,
- goal.param_env,
- goal.predicate.trait_ref,
- bounds,
- )
- .into_iter()
- .map(|pred| goal.with(tcx, pred)),
- );
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- })
- } else {
- Err(NoSolution)
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
}
- }
- fn consider_auto_trait_candidate(
- ecx: &mut EvalCtxt<'_, 'tcx>,
- goal: Goal<'tcx, Self>,
- ) -> QueryResult<'tcx> {
if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) {
return result;
}
@@ -161,6 +126,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
let tcx = ecx.tcx();
ecx.probe(|ecx| {
@@ -176,6 +145,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
ecx.probe_and_evaluate_goal_for_constituent_tys(
goal,
structural_traits::instantiate_constituent_tys_for_sized_trait,
@@ -186,6 +159,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
ecx.probe_and_evaluate_goal_for_constituent_tys(
goal,
structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
@@ -196,14 +173,22 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
- if goal.predicate.self_ty().has_non_region_infer() {
- return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
}
+ // The regions of a type don't affect the size of the type
let tcx = ecx.tcx();
- let self_ty = tcx.erase_regions(goal.predicate.self_ty());
+ // We should erase regions from both the param-env and type, since both
+ // may have infer regions. Specifically, after canonicalizing and instantiating,
+ // early bound regions turn into region vars in both the new and old solver.
+ let key = tcx.erase_regions(goal.param_env.and(goal.predicate.self_ty()));
+ // But if there are inference variables, we have to wait until it's resolved.
+ if key.has_non_region_infer() {
+ return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ }
- if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty))
+ if let Ok(layout) = tcx.layout_of(key)
&& layout.layout.is_pointer_like(&tcx.data_layout)
{
// FIXME: We could make this faster by making a no-constraints response
@@ -217,6 +202,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
if let ty::FnPtr(..) = goal.predicate.self_ty().kind() {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
@@ -229,6 +218,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
goal: Goal<'tcx, Self>,
goal_kind: ty::ClosureKind,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
let tcx = ecx.tcx();
let tupled_inputs_and_output =
match structural_traits::extract_tupled_inputs_and_output_from_callable(
@@ -242,12 +235,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}
};
- let output_is_sized_pred = tupled_inputs_and_output
- .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
+ let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
+ ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
+ });
let pred = tupled_inputs_and_output
.map_bound(|(inputs, _)| {
- tcx.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
+ ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
})
.to_predicate(tcx);
// A built-in `Fn` impl only holds if the output is sized.
@@ -259,6 +253,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
@@ -268,8 +266,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
fn consider_builtin_pointee_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
- _goal: Goal<'tcx, Self>,
+ goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
@@ -277,6 +279,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
let ty::Generator(def_id, _, _) = *goal.predicate.self_ty().kind() else {
return Err(NoSolution);
};
@@ -297,6 +303,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
let self_ty = goal.predicate.self_ty();
let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
return Err(NoSolution);
@@ -312,10 +322,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
Self::consider_implied_clause(
ecx,
goal,
- ty::Binder::dummy(
- tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
- )
- .to_predicate(tcx),
+ ty::TraitRef::new(tcx, goal.predicate.def_id(), [self_ty, generator.resume_ty()])
+ .to_predicate(tcx),
// Technically, we need to check that the generator types are Sized,
// but that's already proven by the generator being WF.
[],
@@ -326,6 +334,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
let tcx = ecx.tcx();
let a_ty = goal.predicate.self_ty();
let b_ty = goal.predicate.trait_ref.substs.type_at(1);
@@ -346,7 +358,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
// Can only unsize to an object-safe type
if data
.principal_def_id()
- .map_or(false, |def_id| !tcx.check_is_object_safe(def_id))
+ .is_some_and(|def_id| !tcx.check_is_object_safe(def_id))
{
return Err(NoSolution);
}
@@ -360,9 +372,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
);
// The type must be Sized to be unsized.
- ecx.add_goal(
- goal.with(tcx, ty::Binder::dummy(tcx.mk_trait_ref(sized_def_id, [a_ty]))),
- );
+ ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
// The type must outlive the lifetime of the `dyn` we're unsizing into.
ecx.add_goal(
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))),
@@ -411,9 +421,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
ecx.add_goal(goal.with(
tcx,
- ty::Binder::dummy(
- tcx.mk_trait_ref(goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
- ),
+ ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
@@ -432,9 +440,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
// Similar to ADTs, require that the rest of the fields are equal.
ecx.add_goal(goal.with(
tcx,
- ty::Binder::dummy(
- tcx.mk_trait_ref(goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
- ),
+ ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
@@ -447,6 +453,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<CanonicalResponse<'tcx>> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return vec![];
+ }
+
let tcx = ecx.tcx();
let a_ty = goal.predicate.self_ty();
@@ -521,8 +531,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
fn consider_builtin_discriminant_kind_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
- _goal: Goal<'tcx, Self>,
+ goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
// `DiscriminantKind` is automatically implemented for every type.
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
@@ -531,6 +545,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
if !goal.param_env.is_const() {
// `Destruct` is automatically implemented for every type in
// non-const environments.
@@ -545,6 +563,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
+ }
+
// `rustc_transmute` does not have support for type or const params
if goal.has_non_region_placeholders() {
return Err(NoSolution);
@@ -591,12 +613,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS))
}
- // These types cannot be structurally decomposed into constitutent
+ // These types cannot be structurally decomposed into constituent
// types, and therefore have no built-in auto impl.
ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
- | ty::Alias(ty::Projection, ..)
+ | ty::Alias(ty::Projection | ty::Inherent, ..)
| ty::Placeholder(..) => Some(Err(NoSolution)),
ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
@@ -645,12 +667,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// FIXME: Handling opaques here is kinda sus. Especially because we
// simplify them to PlaceholderSimplifiedType.
| ty::Alias(ty::Opaque, _) => {
- if let Some(def_id) = self.tcx().find_map_relevant_impl(
+ let mut disqualifying_impl = None;
+ self.tcx().for_each_relevant_impl_treating_projections(
goal.predicate.def_id(),
goal.predicate.self_ty(),
TreatProjections::NextSolverLookup,
- Some,
- ) {
+ |impl_def_id| {
+ disqualifying_impl = Some(impl_def_id);
+ },
+ );
+ if let Some(def_id) = disqualifying_impl {
debug!(?def_id, ?goal, "disqualified auto-trait implementation");
// No need to actually consider the candidate here,
// since we do that in `consider_impl_candidate`.
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 182d995c4..62d2aad52 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -86,7 +86,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
) -> AutoTraitResult<A> {
let tcx = self.tcx;
- let trait_ref = tcx.mk_trait_ref(trait_did, [ty]);
+ let trait_ref = ty::TraitRef::new(tcx, trait_did, [ty]);
let infcx = tcx.infer_ctxt().build();
let mut selcx = SelectionContext::new(&infcx);
@@ -263,7 +263,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
let mut already_visited = FxHashSet::default();
let mut predicates = VecDeque::new();
predicates.push_back(ty::Binder::dummy(ty::TraitPredicate {
- trait_ref: infcx.tcx.mk_trait_ref(trait_did, [ty]),
+ trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]),
constness: ty::BoundConstness::NotConst,
// Auto traits are positive
@@ -591,7 +591,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
fn evaluate_nested_obligations(
&self,
ty: Ty<'_>,
- nested: impl Iterator<Item = Obligation<'tcx, ty::Predicate<'tcx>>>,
+ nested: impl Iterator<Item = PredicateObligation<'tcx>>,
computed_preds: &mut FxIndexSet<ty::Predicate<'tcx>>,
fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
predicates: &mut VecDeque<ty::PolyTraitPredicate<'tcx>>,
@@ -796,13 +796,12 @@ impl<'tcx> AutoTraitFinder<'tcx> {
Ok(Some(valtree)) => Ok(selcx.tcx().mk_const(valtree, c.ty())),
Ok(None) => {
let tcx = self.tcx;
- let def_id = unevaluated.def.did;
let reported =
tcx.sess.emit_err(UnableToConstructConstantValue {
- span: tcx.def_span(def_id),
+ span: tcx.def_span(unevaluated.def),
unevaluated: unevaluated,
});
- Err(ErrorHandled::Reported(reported))
+ Err(ErrorHandled::Reported(reported.into()))
}
Err(err) => Err(err),
}
@@ -835,8 +834,10 @@ impl<'tcx> AutoTraitFinder<'tcx> {
| ty::PredicateKind::Subtype(..)
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
| ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => {}
+ | 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/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 20c2605f2..e8c5a8fab 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -17,9 +17,10 @@ use crate::traits::{
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::Diagnostic;
use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
-use rustc_infer::infer::{DefineOpaqueTypes, DefiningAnchor, InferCtxt, TyCtxtInferExt};
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::util;
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};
@@ -322,7 +323,9 @@ fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> b
let selcx = &mut SelectionContext::new(&infcx);
let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
let (subject2, obligations) =
- impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs);
+ impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs, |_, _| {
+ ObligationCause::dummy()
+ });
!equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id)
}
@@ -582,7 +585,7 @@ fn orphan_check_trait_ref<'tcx>(
trait_ref: ty::TraitRef<'tcx>,
in_crate: InCrate,
) -> Result<(), OrphanCheckErr<'tcx>> {
- if trait_ref.needs_infer() && trait_ref.needs_subst() {
+ if trait_ref.has_infer() && trait_ref.has_param() {
bug!(
"can't orphan check a trait ref with both params and inference variables {:?}",
trait_ref
@@ -673,7 +676,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> {
| ty::RawPtr(..)
| ty::Never
| ty::Tuple(..)
- | ty::Alias(ty::Projection, ..) => self.found_non_local_ty(ty),
+ | ty::Alias(ty::Projection | ty::Inherent, ..) => self.found_non_local_ty(ty),
ty::Param(..) => self.found_param_ty(ty),
@@ -704,7 +707,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> {
}
ty::Dynamic(tt, ..) => {
let principal = tt.principal().map(|p| p.def_id());
- if principal.map_or(false, |p| self.def_id_is_local(p)) {
+ if principal.is_some_and(|p| self.def_id_is_local(p)) {
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
} else {
self.found_non_local_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 345e84990..bd1ea43a7 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -44,7 +44,7 @@ pub fn is_const_evaluatable<'tcx>(
let ct = tcx.expand_abstract_consts(unexpanded_ct);
let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
- tcx.def_kind(uv.def.did) == DefKind::AnonConst
+ tcx.def_kind(uv.def) == DefKind::AnonConst
} else {
false
};
@@ -79,7 +79,7 @@ pub fn is_const_evaluatable<'tcx>(
"Missing value for constant, but no error reported?",
)))
}
- Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
+ Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e.into())),
Ok(_) => Ok(()),
}
}
@@ -119,7 +119,7 @@ pub fn is_const_evaluatable<'tcx>(
tcx.sess
.struct_span_fatal(
// Slightly better span than just using `span` alone
- if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
+ if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def) } else { span },
"failed to evaluate generic const expression",
)
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
@@ -147,7 +147,7 @@ pub fn is_const_evaluatable<'tcx>(
Err(err)
}
- Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
+ Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e.into())),
Ok(_) => Ok(()),
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index 2beebe94b..2c5ffd664 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -14,11 +14,11 @@ use rustc_infer::infer::canonical::{
};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
-use rustc_infer::traits::query::Fallible;
use rustc_infer::traits::{
FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _,
};
use rustc_middle::arena::ArenaAllocatable;
+use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::TypeFoldable;
@@ -97,7 +97,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
def_id: DefId,
) {
let tcx = self.infcx.tcx;
- let trait_ref = tcx.mk_trait_ref(def_id, [ty]);
+ let trait_ref = ty::TraitRef::new(tcx, def_id, [ty]);
self.register_obligation(Obligation {
cause,
recursion_depth: 0,
@@ -235,7 +235,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
- ) -> Fallible<CanonicalQueryResponse<'tcx, T>>
+ ) -> Result<CanonicalQueryResponse<'tcx, T>, NoSolution>
where
T: Debug + TypeFoldable<TyCtxt<'tcx>>,
Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
index 0475f24d8..7ab652761 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
@@ -87,7 +87,7 @@ pub fn recompute_applicable_impls<'tcx>(
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
{
- if kind.rebind(trait_pred.trait_ref) == ty::TraitRef::identity(tcx, trait_pred.def_id()) {
+ if kind.rebind(trait_pred.trait_ref) == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) {
ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id())))
} else {
ambiguities.push(Ambiguity::ParamEnv(span))
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs
deleted file mode 100644
index 7e1dba4ed..000000000
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use crate::infer::InferCtxt;
-
-use rustc_infer::infer::ObligationEmittingRelation;
-use rustc_infer::traits::PredicateObligations;
-use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
-use rustc_middle::ty::{self, Ty, TyCtxt};
-
-pub struct CollectAllMismatches<'a, 'tcx> {
- pub infcx: &'a InferCtxt<'tcx>,
- pub param_env: ty::ParamEnv<'tcx>,
- pub errors: Vec<TypeError<'tcx>>,
-}
-
-impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> {
- fn tag(&self) -> &'static str {
- "CollectAllMismatches"
- }
-
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.infcx.tcx
- }
-
- fn param_env(&self) -> ty::ParamEnv<'tcx> {
- self.param_env
- }
-
- fn a_is_expected(&self) -> bool {
- true
- }
-
- fn relate_with_variance<T: Relate<'tcx>>(
- &mut self,
- _: ty::Variance,
- _: ty::VarianceDiagInfo<'tcx>,
- a: T,
- b: T,
- ) -> RelateResult<'tcx, T> {
- self.relate(a, b)
- }
-
- fn regions(
- &mut self,
- a: ty::Region<'tcx>,
- _b: ty::Region<'tcx>,
- ) -> RelateResult<'tcx, ty::Region<'tcx>> {
- Ok(a)
- }
-
- fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
- self.infcx.probe(|_| {
- if a.is_ty_var() || b.is_ty_var() {
- Ok(a)
- } else {
- self.infcx.super_combine_tys(self, a, b).or_else(|e| {
- self.errors.push(e);
- Ok(a)
- })
- }
- })
- }
-
- fn consts(
- &mut self,
- a: ty::Const<'tcx>,
- b: ty::Const<'tcx>,
- ) -> RelateResult<'tcx, ty::Const<'tcx>> {
- self.infcx.probe(|_| {
- if a.is_ct_infer() || b.is_ct_infer() {
- Ok(a)
- } else {
- relate::super_relate_consts(self, a, b) // could do something similar here for constants!
- }
- })
- }
-
- fn binders<T: Relate<'tcx>>(
- &mut self,
- a: ty::Binder<'tcx, T>,
- b: ty::Binder<'tcx, T>,
- ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> {
- Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
- }
-}
-
-impl<'tcx> ObligationEmittingRelation<'tcx> for CollectAllMismatches<'_, 'tcx> {
- fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
- // FIXME(deferred_projection_equality): We really should get rid of this relation.
- ty::AliasRelationDirection::Equate
- }
-
- fn register_obligations(&mut self, _obligations: PredicateObligations<'tcx>) {
- // FIXME(deferred_projection_equality)
- }
-
- fn register_predicates(
- &mut self,
- _obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>,
- ) {
- // FIXME(deferred_projection_equality)
- }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 1b741b730..a10ececbb 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1,5 +1,4 @@
mod ambiguity;
-pub mod method_chain;
pub mod on_unimplemented;
pub mod suggestions;
@@ -15,8 +14,7 @@ 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::OnUnimplementedNote;
-use on_unimplemented::TypeErrCtxtExt as _;
+use on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@@ -30,9 +28,9 @@ 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::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::fast_reject::TreatProjections;
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print};
use rustc_middle::ty::{
@@ -273,7 +271,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
let underscores = vec!["_"; expected_args.len()].join(", ");
err.span_suggestion_verbose(
closure_arg_span.unwrap_or(found_span),
- &format!(
+ format!(
"consider changing the closure to take and ignore the expected argument{}",
pluralize!(expected_args.len())
),
@@ -360,7 +358,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
span: DUMMY_SP,
kind: TypeVariableOriginKind::MiscVariable,
});
- let trait_ref = self.tcx.mk_trait_ref(trait_def_id, [ty.skip_binder(), var]);
+ let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]);
let obligation = Obligation::new(
self.tcx,
ObligationCause::dummy(),
@@ -439,7 +437,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// 1) strictly implied by another error.
// 2) implied by an error with a smaller index.
for error2 in error_set {
- if error2.index.map_or(false, |index2| is_suppressed[index2]) {
+ if error2.index.is_some_and(|index2| is_suppressed[index2]) {
// Avoid errors being suppressed by already-suppressed
// errors, to prevent all errors from being suppressed
// at once.
@@ -467,7 +465,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- self.tcx.sess.delay_span_bug(DUMMY_SP, "expected fullfillment errors")
+ self.tcx.sess.delay_span_bug(DUMMY_SP, "expected fulfillment errors")
}
/// Reports that an overflow has occurred and halts compilation. We
@@ -561,6 +559,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
suggest_increasing_limit,
|err| {
self.note_obligation_cause_code(
+ obligation.cause.body_id,
err,
predicate,
obligation.param_env,
@@ -577,7 +576,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
Limit(0) => Limit(2),
limit => limit * 2,
};
- err.help(&format!(
+ err.help(format!(
"consider increasing the recursion limit by adding a \
`#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
suggested_limit,
@@ -707,42 +706,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
conversion on the error value using the `From` trait"
.to_owned(),
),
- Some(None),
+ Some(AppendConstMessage::Default),
)
} else {
(message, note, append_const_msg)
};
- let err_msg = message
- .and_then(|cannot_do_this| {
- match (predicate_is_const, append_const_msg) {
- // do nothing if predicate is not const
- (false, _) => Some(cannot_do_this),
- // suggested using default post message
- (true, Some(None)) => {
- Some(format!("{cannot_do_this} in const contexts"))
- }
- // overridden post message
- (true, Some(Some(post_message))) => {
- Some(format!("{cannot_do_this}{post_message}"))
- }
- // fallback to generic message
- (true, None) => None,
- }
- })
- .unwrap_or_else(|| {
- format!(
- "the trait bound `{}` is not satisfied{}",
- trait_predicate, post_message,
- )
- });
+ let err_msg = self.get_standard_error_message(
+ &trait_predicate,
+ message,
+ predicate_is_const,
+ append_const_msg,
+ post_message,
+ );
let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id())
== self.tcx.lang_items().transmute_trait()
{
// Recompute the safe transmute reason and use that for the error reporting
self.get_safe_transmute_error_and_reason(
- trait_predicate,
obligation.clone(),
trait_ref,
span,
@@ -756,7 +738,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
err.span_label(
ret_span,
- &format!(
+ format!(
"expected `{}` because of this",
trait_ref.skip_binder().self_ty()
),
@@ -764,22 +746,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() {
- match obligation.cause.code().peel_derives() {
- ObligationCauseCode::RustCall => {
- err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument");
- }
- ObligationCauseCode::BindingObligation(def_id, _)
- | ObligationCauseCode::ItemObligation(def_id)
- if tcx.is_fn_trait(*def_id) =>
- {
- err.code(rustc_errors::error_code!(E0059));
- err.set_primary_message(format!(
- "type parameter to bare `{}` trait must be a tuple",
- tcx.def_path_str(*def_id)
- ));
- }
- _ => {}
- }
+ self.add_tuple_trait_message(
+ &obligation.cause.code().peel_derives(),
+ &mut err,
+ );
}
if Some(trait_ref.def_id()) == tcx.lang_items().drop_trait()
@@ -789,33 +759,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details");
}
- let explanation = if let ObligationCauseCode::MainFunctionType =
- obligation.cause.code()
- {
- "consider using `()`, or a `Result`".to_owned()
- } else {
- let ty_desc = match trait_ref.skip_binder().self_ty().kind() {
- ty::FnDef(_, _) => Some("fn item"),
- ty::Closure(_, _) => Some("closure"),
- _ => None,
- };
+ let explanation = get_explanation_based_on_obligation(
+ &obligation,
+ trait_ref,
+ &trait_predicate,
+ pre_message,
+ );
- match ty_desc {
- Some(desc) => format!(
- "{}the trait `{}` is not implemented for {} `{}`",
- pre_message,
- trait_predicate.print_modifiers_and_trait_path(),
- desc,
- trait_ref.skip_binder().self_ty(),
- ),
- None => format!(
- "{}the trait `{}` is not implemented for `{}`",
- pre_message,
- trait_predicate.print_modifiers_and_trait_path(),
- trait_ref.skip_binder().self_ty(),
- ),
- }
- };
self.check_for_binding_assigned_block_without_tail_expression(
&obligation,
&mut err,
@@ -831,7 +781,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.emit();
return;
}
- if let Some(ref s) = label {
+ if let Some(s) = label {
// If it has a custom `#[rustc_on_unimplemented]`
// error message, let's display it as the label!
err.span_label(span, s);
@@ -839,7 +789,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// When the self type is a type param We don't need to "the trait
// `std::marker::Sized` is not implemented for `T`" as we will point
// at the type param with a label to suggest constraining it.
- err.help(&explanation);
+ err.help(explanation);
}
} else if let Some(custom_explanation) = safe_transmute_explanation {
err.span_label(span, custom_explanation);
@@ -847,42 +797,36 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.span_label(span, explanation);
}
- if let ObligationCauseCode::ObjectCastObligation(concrete_ty, obj_ty) = obligation.cause.code().peel_derives() &&
- Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() {
- self.suggest_borrowing_for_object_cast(&mut err, &root_obligation, *concrete_ty, *obj_ty);
- }
-
- let mut unsatisfied_const = false;
- if trait_predicate.is_const_if_const() && obligation.param_env.is_const() {
- let non_const_predicate = trait_ref.without_const();
- let non_const_obligation = Obligation {
- cause: obligation.cause.clone(),
- param_env: obligation.param_env.without_const(),
- predicate: non_const_predicate.to_predicate(tcx),
- recursion_depth: obligation.recursion_depth,
- };
- if self.predicate_may_hold(&non_const_obligation) {
- unsatisfied_const = true;
- err.span_note(
- span,
- &format!(
- "the trait `{}` is implemented for `{}`, \
- but that implementation is not `const`",
- non_const_predicate.print_modifiers_and_trait_path(),
- trait_ref.skip_binder().self_ty(),
- ),
+ if let ObligationCauseCode::Coercion { source, target } =
+ *obligation.cause.code().peel_derives()
+ {
+ if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() {
+ self.suggest_borrowing_for_object_cast(
+ &mut err,
+ &root_obligation,
+ source,
+ target,
);
}
}
+ let UnsatisfiedConst(unsatisfied_const) = self
+ .maybe_add_note_for_unsatisfied_const(
+ &obligation,
+ trait_ref,
+ &trait_predicate,
+ &mut err,
+ span,
+ );
+
if let Some((msg, span)) = type_def {
- err.span_label(span, &msg);
+ err.span_label(span, msg);
}
- if let Some(ref s) = note {
+ if let Some(s) = note {
// If it has a custom `#[rustc_on_unimplemented]` note, let's display it
- err.note(s.as_str());
+ err.note(s);
}
- if let Some(ref s) = parent_label {
+ if let Some(s) = parent_label {
let body = obligation.cause.body_id;
err.span_label(tcx.def_span(body), s);
}
@@ -941,7 +885,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return;
}
- if self.suggest_impl_trait(&mut err, span, &obligation, trait_predicate) {
+ if self.suggest_impl_trait(&mut err, &obligation, trait_predicate) {
err.emit();
return;
}
@@ -971,137 +915,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
}
- let body_def_id = obligation.cause.body_id;
- // Try to report a help message
- if is_fn_trait
- && let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
- obligation.param_env,
- trait_ref.self_ty(),
- trait_predicate.skip_binder().constness,
- trait_predicate.skip_binder().polarity,
- )
- {
- // If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following
- // suggestion to add trait bounds for the type, since we only typically implement
- // these traits once.
-
- // Note if the `FnMut` or `FnOnce` is less general than the trait we're trying
- // to implement.
- let selected_kind =
- self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id())
- .expect("expected to map DefId to ClosureKind");
- if !implemented_kind.extends(selected_kind) {
- err.note(
- &format!(
- "`{}` implements `{}`, but it must implement `{}`, which is more general",
- trait_ref.skip_binder().self_ty(),
- implemented_kind,
- selected_kind
- )
- );
- }
-
- // Note any argument mismatches
- let given_ty = params.skip_binder();
- let expected_ty = trait_ref.skip_binder().substs.type_at(1);
- if let ty::Tuple(given) = given_ty.kind()
- && let ty::Tuple(expected) = expected_ty.kind()
- {
- if expected.len() != given.len() {
- // Note number of types that were expected and given
- err.note(
- &format!(
- "expected a closure taking {} argument{}, but one taking {} argument{} was given",
- given.len(),
- pluralize!(given.len()),
- expected.len(),
- pluralize!(expected.len()),
- )
- );
- } else if !self.same_type_modulo_infer(given_ty, expected_ty) {
- // Print type mismatch
- let (expected_args, given_args) =
- self.cmp(given_ty, expected_ty);
- err.note_expected_found(
- &"a closure with arguments",
- expected_args,
- &"a closure with arguments",
- given_args,
- );
- }
- }
- } else if !trait_ref.has_non_region_infer()
- && self.predicate_can_apply(obligation.param_env, trait_predicate)
- {
- // If a where-clause may be useful, remind the
- // user that they can add it.
- //
- // don't display an on-unimplemented note, as
- // these notes will often be of the form
- // "the type `T` can't be frobnicated"
- // which is somewhat confusing.
- self.suggest_restricting_param_bound(
- &mut err,
- trait_predicate,
- None,
- obligation.cause.body_id,
- );
- } else if !suggested && !unsatisfied_const {
- // Can't show anything else useful, try to find similar impls.
- let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
- if !self.report_similar_impl_candidates(
- &impl_candidates,
- trait_ref,
- body_def_id,
- &mut err,
- true,
- ) {
- // This is *almost* equivalent to
- // `obligation.cause.code().peel_derives()`, but it gives us the
- // trait predicate for that corresponding root obligation. This
- // lets us get a derived obligation from a type parameter, like
- // when calling `string.strip_suffix(p)` where `p` is *not* an
- // implementer of `Pattern<'_>`.
- let mut code = obligation.cause.code();
- let mut trait_pred = trait_predicate;
- let mut peeled = false;
- while let Some((parent_code, parent_trait_pred)) = code.parent() {
- code = parent_code;
- if let Some(parent_trait_pred) = parent_trait_pred {
- trait_pred = parent_trait_pred;
- peeled = true;
- }
- }
- let def_id = trait_pred.def_id();
- // Mention *all* the `impl`s for the *top most* obligation, the
- // user might have meant to use one of them, if any found. We skip
- // auto-traits or fundamental traits that might not be exactly what
- // the user might expect to be presented with. Instead this is
- // useful for less general traits.
- if peeled
- && !self.tcx.trait_is_auto(def_id)
- && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id)
- {
- let trait_ref = trait_pred.to_poly_trait_ref();
- let impl_candidates =
- self.find_similar_impl_candidates(trait_pred);
- self.report_similar_impl_candidates(
- &impl_candidates,
- trait_ref,
- body_def_id,
- &mut err,
- true,
- );
- }
- }
-
- self.maybe_suggest_convert_to_slice(
- &mut err,
- trait_ref,
- impl_candidates.as_slice(),
- span,
- );
- }
+ self.try_to_add_help_message(
+ &obligation,
+ trait_ref,
+ &trait_predicate,
+ &mut err,
+ span,
+ is_fn_trait,
+ suggested,
+ unsatisfied_const,
+ );
// Changing mutability doesn't make a difference to whether we have
// an `Unsize` impl (Fixes ICE in #71036)
@@ -1196,59 +1019,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => {
let found_kind = self.closure_kind(closure_substs).unwrap();
- let closure_span = self.tcx.def_span(closure_def_id);
- let mut err = struct_span_err!(
- self.tcx.sess,
- closure_span,
- E0525,
- "expected a closure that implements the `{}` trait, \
- but this closure only implements `{}`",
- kind,
- found_kind
- );
-
- err.span_label(
- closure_span,
- format!("this closure implements `{}`, not `{}`", found_kind, kind),
- );
- err.span_label(
- obligation.cause.span,
- format!("the requirement to implement `{}` derives from here", kind),
- );
-
- // Additional context information explaining why the closure only implements
- // a particular trait.
- if let Some(typeck_results) = &self.typeck_results {
- let hir_id = self
- .tcx
- .hir()
- .local_def_id_to_hir_id(closure_def_id.expect_local());
- match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) {
- (ty::ClosureKind::FnOnce, Some((span, place))) => {
- err.span_label(
- *span,
- format!(
- "closure is `FnOnce` because it moves the \
- variable `{}` out of its environment",
- ty::place_to_string_for_capture(tcx, place)
- ),
- );
- }
- (ty::ClosureKind::FnMut, Some((span, place))) => {
- err.span_label(
- *span,
- format!(
- "closure is `FnMut` because it mutates the \
- variable `{}` here",
- ty::place_to_string_for_capture(tcx, place)
- ),
- );
- }
- _ => {}
- }
- }
-
- err
+ self.report_closure_error(&obligation, closure_def_id, found_kind, kind)
}
ty::PredicateKind::WellFormed(ty) => {
@@ -1266,7 +1037,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// which bounds actually failed to hold.
self.tcx.sess.struct_span_err(
span,
- &format!("the type `{}` is not well-formed", ty),
+ format!("the type `{}` is not well-formed", ty),
)
}
}
@@ -1309,7 +1080,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
let mut diag = self.tcx.sess.struct_span_err(
span,
- &format!("the constant `{}` is not of type `{}`", ct, ty),
+ format!("the constant `{}` is not of type `{}`", ct, ty),
);
self.note_type_err(
&mut diag,
@@ -1325,121 +1096,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- OutputTypeParameterMismatch(
+ OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch {
found_trait_ref,
expected_trait_ref,
- terr @ TypeError::CyclicTy(_),
- ) => {
- let self_ty = found_trait_ref.self_ty().skip_binder();
- let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() {
- (
- ObligationCause::dummy_with_span(tcx.def_span(def_id)),
- TypeError::CyclicTy(self_ty),
- )
- } else {
- (obligation.cause.clone(), terr)
- };
- self.report_and_explain_type_error(
- TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref),
- terr,
- )
- }
- OutputTypeParameterMismatch(found_trait_ref, expected_trait_ref, _) => {
- let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref);
- let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref);
-
- if expected_trait_ref.self_ty().references_error() {
- return;
- }
-
- let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else {
- return;
- };
-
- let found_did = match *found_trait_ty.kind() {
- ty::Closure(did, _)
- | ty::Foreign(did)
- | ty::FnDef(did, _)
- | ty::Generator(did, ..) => Some(did),
- ty::Adt(def, _) => Some(def.did()),
- _ => None,
- };
-
- let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did));
- let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did));
-
- if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) {
- // We check closures twice, with obligations flowing in different directions,
- // but we want to complain about them only once.
- return;
- }
-
- self.reported_closure_mismatch.borrow_mut().insert((span, found_span));
-
- let mut not_tupled = false;
-
- let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() {
- ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()],
- _ => {
- not_tupled = true;
- vec![ArgKind::empty()]
- }
- };
-
- let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1);
- let expected = match expected_ty.kind() {
- ty::Tuple(ref tys) => {
- tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect()
- }
- _ => {
- not_tupled = true;
- vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())]
- }
- };
-
- // If this is a `Fn` family trait and either the expected or found
- // is not tupled, then fall back to just a regular mismatch error.
- // This shouldn't be common unless manually implementing one of the
- // traits manually, but don't make it more confusing when it does
- // happen.
- if Some(expected_trait_ref.def_id()) != tcx.lang_items().gen_trait() && not_tupled {
- self.report_and_explain_type_error(
- TypeTrace::poly_trait_refs(
- &obligation.cause,
- true,
- expected_trait_ref,
- found_trait_ref,
- ),
- ty::error::TypeError::Mismatch,
- )
- } else if found.len() == expected.len() {
- self.report_closure_arg_mismatch(
- span,
- found_span,
- found_trait_ref,
- expected_trait_ref,
- obligation.cause.code(),
- found_node,
- obligation.param_env,
- )
- } else {
- let (closure_span, closure_arg_span, found) = found_did
- .and_then(|did| {
- let node = self.tcx.hir().get_if_local(did)?;
- let (found_span, closure_arg_span, found) =
- self.get_fn_like_arguments(node)?;
- Some((Some(found_span), closure_arg_span, found))
- })
- .unwrap_or((found_span, None, found));
-
- self.report_arg_count_mismatch(
- span,
- closure_span,
- expected,
- found,
- found_trait_ty.is_closure(),
- closure_arg_span,
- )
+ terr: terr @ TypeError::CyclicTy(_),
+ }) => self.report_type_parameter_mismatch_cyclic_type_error(
+ &obligation,
+ found_trait_ref,
+ expected_trait_ref,
+ terr,
+ ),
+ OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch {
+ found_trait_ref,
+ expected_trait_ref,
+ terr: _,
+ }) => {
+ match self.report_type_parameter_mismatch_error(
+ &obligation,
+ span,
+ found_trait_ref,
+ expected_trait_ref,
+ ) {
+ Some(err) => err,
+ None => return,
}
}
@@ -1454,45 +1133,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
}
SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => {
- if !self.tcx.features().generic_const_exprs {
- let mut err = self.tcx.sess.struct_span_err(
- span,
- "constant expression depends on a generic parameter",
- );
- // FIXME(const_generics): we should suggest to the user how they can resolve this
- // issue. However, this is currently not actually possible
- // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
- //
- // Note that with `feature(generic_const_exprs)` this case should not
- // be reachable.
- err.note("this may fail depending on what value the parameter takes");
- err.emit();
- return;
- }
-
- match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::ConstEvaluatable(ct) => {
- let ty::ConstKind::Unevaluated(uv) = ct.kind() else {
- bug!("const evaluatable failed for non-unevaluated const `{ct:?}`");
- };
- let mut err =
- self.tcx.sess.struct_span_err(span, "unconstrained generic constant");
- let const_span = self.tcx.def_span(uv.def.did);
- match self.tcx.sess.source_map().span_to_snippet(const_span) {
- Ok(snippet) => err.help(&format!(
- "try adding a `where` bound using this expression: `where [(); {}]:`",
- snippet
- )),
- _ => err.help("consider adding a `where` bound using this expression"),
- };
- err
- }
- _ => {
- span_bug!(
- span,
- "unexpected non-ConstEvaluatable predicate, this should not be reachable"
- )
- }
+ match self.report_not_const_evaluatable_error(&obligation, span) {
+ Some(err) => err,
+ None => return,
}
}
@@ -1564,6 +1207,14 @@ trait InferCtxtPrivExt<'tcx> {
other: bool,
) -> bool;
+ fn report_similar_impl_candidates_for_root_obligation(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
+ body_def_id: LocalDefId,
+ err: &mut Diagnostic,
+ );
+
/// Gets the parent trait chain start
fn get_parent_trait_ref(
&self,
@@ -1627,13 +1278,86 @@ trait InferCtxtPrivExt<'tcx> {
cause_code: &ObligationCauseCode<'tcx>,
) -> bool;
+ fn get_standard_error_message(
+ &self,
+ trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+ message: Option<String>,
+ predicate_is_const: bool,
+ append_const_msg: Option<AppendConstMessage>,
+ post_message: String,
+ ) -> String;
+
fn get_safe_transmute_error_and_reason(
&self,
- trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
- obligation: Obligation<'tcx, ty::Predicate<'tcx>>,
- trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ obligation: PredicateObligation<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
) -> (String, Option<String>);
+
+ fn add_tuple_trait_message(
+ &self,
+ obligation_cause_code: &ObligationCauseCode<'tcx>,
+ err: &mut Diagnostic,
+ );
+
+ fn try_to_add_help_message(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+ err: &mut Diagnostic,
+ span: Span,
+ is_fn_trait: bool,
+ suggested: bool,
+ unsatisfied_const: bool,
+ );
+
+ fn add_help_message_for_fn_trait(
+ &self,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ err: &mut Diagnostic,
+ implemented_kind: ty::ClosureKind,
+ params: ty::Binder<'tcx, Ty<'tcx>>,
+ );
+
+ fn maybe_add_note_for_unsatisfied_const(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+ err: &mut Diagnostic,
+ span: Span,
+ ) -> UnsatisfiedConst;
+
+ fn report_closure_error(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ closure_def_id: DefId,
+ found_kind: ty::ClosureKind,
+ kind: ty::ClosureKind,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+
+ fn report_type_parameter_mismatch_cyclic_type_error(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ terr: TypeError<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+
+ fn report_type_parameter_mismatch_error(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ span: Span,
+ found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>;
+
+ fn report_not_const_evaluatable_error(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ span: Span,
+ ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>;
}
impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
@@ -1720,6 +1444,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::ExprItemObligation(..) = code
{
self.note_obligation_cause_code(
+ error.obligation.cause.body_id,
&mut diag,
error.obligation.predicate,
error.obligation.param_env,
@@ -1773,7 +1498,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
.tcx
.mk_const(
ty::UnevaluatedConst {
- def: ty::WithOptConstParam::unknown(data.projection_ty.def_id),
+ def: data.projection_ty.def_id,
substs: data.projection_ty.substs,
},
ct.ty(),
@@ -1793,7 +1518,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::BindingObligation(_, _)
| ObligationCauseCode::ExprItemObligation(..)
| ObligationCauseCode::ExprBindingObligation(..)
- | ObligationCauseCode::ObjectCastObligation(..)
+ | ObligationCauseCode::Coercion { .. }
| ObligationCauseCode::OpaqueType
);
@@ -1838,57 +1563,61 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
});
let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}");
- let secondary_span = match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => self
- .tcx
- .opt_associated_item(proj.projection_ty.def_id)
- .and_then(|trait_assoc_item| {
- self.tcx
- .trait_of_item(proj.projection_ty.def_id)
- .map(|id| (trait_assoc_item, id))
- })
- .and_then(|(trait_assoc_item, id)| {
- let trait_assoc_ident = trait_assoc_item.ident(self.tcx);
- self.tcx.find_map_relevant_impl(
- id,
- proj.projection_ty.self_ty(),
- TreatProjections::ForLookup,
- |did| {
- self.tcx
- .associated_items(did)
- .in_definition_order()
- .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident)
- },
- )
- })
- .and_then(|item| match self.tcx.hir().get_if_local(item.def_id) {
- Some(
- hir::Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Type(_, Some(ty)),
- ..
- })
- | hir::Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Type(ty),
- ..
- }),
- ) => Some((
- ty.span,
- with_forced_trimmed_paths!(format!(
- "type mismatch resolving `{}`",
- self.resolve_vars_if_possible(predicate)
- .print(FmtPrinter::new_with_limit(
- self.tcx,
- Namespace::TypeNS,
- rustc_session::Limit(5),
- ))
- .unwrap()
- .into_buffer()
- )),
+ let secondary_span = (|| {
+ let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) =
+ predicate.kind().skip_binder()
+ else {
+ return None;
+ };
+
+ let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_ty.def_id)?;
+ let trait_assoc_ident = trait_assoc_item.ident(self.tcx);
+
+ let mut associated_items = vec![];
+ self.tcx.for_each_relevant_impl(
+ self.tcx.trait_of_item(proj.projection_ty.def_id)?,
+ proj.projection_ty.self_ty(),
+ |impl_def_id| {
+ associated_items.extend(
+ self.tcx
+ .associated_items(impl_def_id)
+ .in_definition_order()
+ .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident),
+ );
+ },
+ );
+
+ let [associated_item]: &[ty::AssocItem] = &associated_items[..] else {
+ return None;
+ };
+ match self.tcx.hir().get_if_local(associated_item.def_id) {
+ Some(
+ hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Type(_, Some(ty)),
+ ..
+ })
+ | hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Type(ty),
+ ..
+ }),
+ ) => Some((
+ ty.span,
+ with_forced_trimmed_paths!(format!(
+ "type mismatch resolving `{}`",
+ self.resolve_vars_if_possible(predicate)
+ .print(FmtPrinter::new_with_limit(
+ self.tcx,
+ Namespace::TypeNS,
+ rustc_session::Limit(5),
+ ))
+ .unwrap()
+ .into_buffer()
)),
- _ => None,
- }),
- _ => None,
- };
+ )),
+ _ => None,
+ }
+ })();
+
self.note_type_err(
&mut diag,
&obligation.cause,
@@ -1971,13 +1700,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::Tuple(..) => Some(10),
ty::Param(..) => Some(11),
ty::Alias(ty::Projection, ..) => Some(12),
- ty::Alias(ty::Opaque, ..) => Some(13),
- ty::Never => Some(14),
- ty::Adt(..) => Some(15),
- ty::Generator(..) => Some(16),
- ty::Foreign(..) => Some(17),
- ty::GeneratorWitness(..) => Some(18),
- ty::GeneratorWitnessMIR(..) => Some(19),
+ 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::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None,
}
}
@@ -2058,7 +1788,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if candidates.iter().any(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })) {
// If any of the candidates is a perfect match, we don't want to show all of them.
// This is particularly relevant for the case of numeric types (as they all have the
- // same cathegory).
+ // same category).
candidates.retain(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. }));
}
candidates
@@ -2120,7 +1850,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
candidates.sort();
candidates.dedup();
let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
- err.help(&format!(
+ err.help(format!(
"the following {other}types implement trait `{}`:{}{}",
trait_ref.print_only_trait_path(),
candidates[..end].join(""),
@@ -2197,6 +1927,51 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
report(normalized_impl_candidates, err)
}
+ fn report_similar_impl_candidates_for_root_obligation(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
+ body_def_id: LocalDefId,
+ err: &mut Diagnostic,
+ ) {
+ // This is *almost* equivalent to
+ // `obligation.cause.code().peel_derives()`, but it gives us the
+ // trait predicate for that corresponding root obligation. This
+ // lets us get a derived obligation from a type parameter, like
+ // when calling `string.strip_suffix(p)` where `p` is *not* an
+ // implementer of `Pattern<'_>`.
+ let mut code = obligation.cause.code();
+ let mut trait_pred = trait_predicate;
+ let mut peeled = false;
+ while let Some((parent_code, parent_trait_pred)) = code.parent() {
+ code = parent_code;
+ if let Some(parent_trait_pred) = parent_trait_pred {
+ trait_pred = parent_trait_pred;
+ peeled = true;
+ }
+ }
+ let def_id = trait_pred.def_id();
+ // Mention *all* the `impl`s for the *top most* obligation, the
+ // user might have meant to use one of them, if any found. We skip
+ // auto-traits or fundamental traits that might not be exactly what
+ // the user might expect to be presented with. Instead this is
+ // useful for less general traits.
+ if peeled
+ && !self.tcx.trait_is_auto(def_id)
+ && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id)
+ {
+ let trait_ref = trait_pred.to_poly_trait_ref();
+ let impl_candidates = self.find_similar_impl_candidates(trait_pred);
+ self.report_similar_impl_candidates(
+ &impl_candidates,
+ trait_ref,
+ body_def_id,
+ err,
+ true,
+ );
+ }
+ }
+
/// Gets the parent trait chain start
fn get_parent_trait_ref(
&self,
@@ -2230,14 +2005,18 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err: &mut Diagnostic,
trait_ref: &ty::PolyTraitRef<'tcx>,
) -> bool {
- let get_trait_impl = |trait_def_id| {
- self.tcx.find_map_relevant_impl(
+ let get_trait_impls = |trait_def_id| {
+ let mut trait_impls = vec![];
+ self.tcx.for_each_relevant_impl(
trait_def_id,
trait_ref.skip_binder().self_ty(),
- TreatProjections::ForLookup,
- Some,
- )
+ |impl_def_id| {
+ trait_impls.push(impl_def_id);
+ },
+ );
+ trait_impls
};
+
let required_trait_path = self.tcx.def_path_str(trait_ref.def_id());
let traits_with_same_path: std::collections::BTreeSet<_> = self
.tcx
@@ -2247,17 +2026,23 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
.collect();
let mut suggested = false;
for trait_with_same_path in traits_with_same_path {
- if let Some(impl_def_id) = get_trait_impl(trait_with_same_path) {
- let impl_span = self.tcx.def_span(impl_def_id);
- err.span_help(impl_span, "trait impl with same name found");
- let trait_crate = self.tcx.crate_name(trait_with_same_path.krate);
- let crate_msg = format!(
- "perhaps two different versions of crate `{}` are being used?",
- trait_crate
- );
- err.note(&crate_msg);
- suggested = true;
+ let trait_impls = get_trait_impls(trait_with_same_path);
+ if trait_impls.is_empty() {
+ continue;
}
+ let impl_spans: Vec<_> =
+ trait_impls.iter().map(|impl_def_id| self.tcx.def_span(*impl_def_id)).collect();
+ err.span_help(
+ impl_spans,
+ format!("trait impl{} with same name found", pluralize!(trait_impls.len())),
+ );
+ let trait_crate = self.tcx.crate_name(trait_with_same_path.krate);
+ let crate_msg = format!(
+ "perhaps two different versions of crate `{}` are being used?",
+ trait_crate
+ );
+ err.note(crate_msg);
+ suggested = true;
}
suggested
}
@@ -2388,7 +2173,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.cancel();
return;
}
- err.note(&format!("cannot satisfy `{}`", predicate));
+ err.note(format!("cannot satisfy `{}`", predicate));
let impl_candidates = self.find_similar_impl_candidates(
predicate.to_opt_poly_trait_pred().unwrap(),
);
@@ -2408,7 +2193,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.cancel();
return;
}
- err.note(&format!("cannot satisfy `{}`", predicate));
+ err.note(format!("cannot satisfy `{}`", predicate));
}
}
@@ -2453,9 +2238,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.cancel();
err = self.tcx.sess.struct_span_err_with_code(
span,
- &format!(
+ format!(
"cannot {verb} associated {noun} on trait without specifying the corresponding `impl` type",
- ),
+ ),
rustc_errors::error_code!(E0790),
);
@@ -2562,7 +2347,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ErrorCode::E0284,
true,
);
- err.note(&format!("cannot satisfy `{}`", predicate));
+ err.note(format!("cannot satisfy `{}`", predicate));
err
} else {
// If we can't find a substitution, just print a generic error
@@ -2573,7 +2358,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
"type annotations needed: cannot satisfy `{}`",
predicate,
);
- err.span_label(span, &format!("cannot satisfy `{}`", predicate));
+ err.span_label(span, format!("cannot satisfy `{}`", predicate));
err
}
}
@@ -2601,7 +2386,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
"type annotations needed: cannot satisfy `{}`",
predicate,
);
- err.span_label(span, &format!("cannot satisfy `{}`", predicate));
+ err.span_label(span, format!("cannot satisfy `{}`", predicate));
err
}
}
@@ -2616,7 +2401,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
"type annotations needed: cannot satisfy `{}`",
predicate,
);
- err.span_label(span, &format!("cannot satisfy `{}`", predicate));
+ err.span_label(span, format!("cannot satisfy `{}`", predicate));
err
}
};
@@ -2689,13 +2474,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
match (spans.len(), crates.len(), crate_names.len()) {
(0, 0, 0) => {
- err.note(&format!("cannot satisfy `{}`", predicate));
+ err.note(format!("cannot satisfy `{}`", predicate));
}
(0, _, 1) => {
- err.note(&format!("{} in the `{}` crate{}", msg, crates[0], post,));
+ err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,));
}
(0, _, _) => {
- err.note(&format!(
+ err.note(format!(
"{} in the following crates: {}{}",
msg,
crate_names.join(", "),
@@ -2704,19 +2489,17 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
(_, 0, 0) => {
let span: MultiSpan = spans.into();
- err.span_note(span, &msg);
+ err.span_note(span, msg);
}
(_, 1, 1) => {
let span: MultiSpan = spans.into();
- err.span_note(span, &msg);
- err.note(
- &format!("and another `impl` found in the `{}` crate{}", crates[0], post,),
- );
+ err.span_note(span, msg);
+ err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,));
}
_ => {
let span: MultiSpan = spans.into();
- err.span_note(span, &msg);
- err.note(&format!(
+ err.span_note(span, msg);
+ err.note(format!(
"and more `impl`s found in the following crates: {}{}",
crate_names.join(", "),
post,
@@ -2776,6 +2559,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// message, and fall back to regular note otherwise.
if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
self.note_obligation_cause_code(
+ obligation.cause.body_id,
err,
obligation.predicate,
obligation.param_env,
@@ -2887,7 +2671,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
err.span_help(
multispan,
- &format!(
+ format!(
"you could relax the implicit `Sized` bound on `{T}` if it were \
used through indirection like `&{T}` or `Box<{T}>`",
T = param.name.ident(),
@@ -2919,20 +2703,52 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
false
}
+ fn get_standard_error_message(
+ &self,
+ trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+ message: Option<String>,
+ predicate_is_const: bool,
+ append_const_msg: Option<AppendConstMessage>,
+ post_message: String,
+ ) -> String {
+ message
+ .and_then(|cannot_do_this| {
+ match (predicate_is_const, append_const_msg) {
+ // do nothing if predicate is not const
+ (false, _) => Some(cannot_do_this),
+ // suggested using default post message
+ (true, Some(AppendConstMessage::Default)) => {
+ Some(format!("{cannot_do_this} in const contexts"))
+ }
+ // overridden post message
+ (true, Some(AppendConstMessage::Custom(custom_msg))) => {
+ Some(format!("{cannot_do_this}{custom_msg}"))
+ }
+ // fallback to generic message
+ (true, None) => None,
+ }
+ })
+ .unwrap_or_else(|| {
+ format!("the trait bound `{}` is not satisfied{}", trait_predicate, post_message)
+ })
+ }
+
fn get_safe_transmute_error_and_reason(
&self,
- trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
- obligation: Obligation<'tcx, ty::Predicate<'tcx>>,
- trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ obligation: PredicateObligation<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
) -> (String, Option<String>) {
- let src_and_dst = trait_predicate.map_bound(|p| rustc_transmute::Types {
- dst: p.trait_ref.substs.type_at(0),
- src: p.trait_ref.substs.type_at(1),
- });
- let scope = trait_ref.skip_binder().substs.type_at(2);
+ // 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));
+
+ let src_and_dst = rustc_transmute::Types {
+ dst: trait_ref.substs.type_at(0),
+ src: trait_ref.substs.type_at(1),
+ };
+ let scope = trait_ref.substs.type_at(2);
let Some(assume) =
- rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, trait_ref.skip_binder().substs.const_at(3)) else {
+ 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(
@@ -2942,24 +2758,27 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
assume,
) {
rustc_transmute::Answer::No(reason) => {
- let dst = trait_ref.skip_binder().substs.type_at(0);
- let src = trait_ref.skip_binder().substs.type_at(1);
- let custom_err_msg = format!("`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`").to_string();
+ let dst = trait_ref.substs.type_at(0);
+ let src = trait_ref.substs.type_at(1);
+ let custom_err_msg = format!(
+ "`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`"
+ );
let reason_msg = match reason {
rustc_transmute::Reason::SrcIsUnspecified => {
- format!("`{src}` does not have a well-specified layout").to_string()
+ format!("`{src}` does not have a well-specified layout")
}
+
rustc_transmute::Reason::DstIsUnspecified => {
- format!("`{dst}` does not have a well-specified layout").to_string()
+ format!("`{dst}` does not have a well-specified layout")
}
+
rustc_transmute::Reason::DstIsBitIncompatible => {
format!("At least one value of `{src}` isn't a bit-valid value of `{dst}`")
- .to_string()
}
+
rustc_transmute::Reason::DstIsPrivate => format!(
"`{dst}` is or contains a type or field that is not visible in that scope"
- )
- .to_string(),
+ ),
// FIXME(bryangarza): Include the number of bytes of src and dst
rustc_transmute::Reason::DstIsTooBig => {
format!("The size of `{src}` is smaller than the size of `{dst}`")
@@ -2975,8 +2794,449 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
_ => span_bug!(span, "Unsupported rustc_transmute::Reason variant"),
}
}
+
+ fn add_tuple_trait_message(
+ &self,
+ obligation_cause_code: &ObligationCauseCode<'tcx>,
+ err: &mut Diagnostic,
+ ) {
+ match obligation_cause_code {
+ ObligationCauseCode::RustCall => {
+ err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument");
+ }
+ ObligationCauseCode::BindingObligation(def_id, _)
+ | ObligationCauseCode::ItemObligation(def_id)
+ if self.tcx.is_fn_trait(*def_id) =>
+ {
+ err.code(rustc_errors::error_code!(E0059));
+ err.set_primary_message(format!(
+ "type parameter to bare `{}` trait must be a tuple",
+ self.tcx.def_path_str(*def_id)
+ ));
+ }
+ _ => {}
+ }
+ }
+
+ fn try_to_add_help_message(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+ err: &mut Diagnostic,
+ span: Span,
+ is_fn_trait: bool,
+ suggested: bool,
+ unsatisfied_const: bool,
+ ) {
+ let body_def_id = obligation.cause.body_id;
+ // Try to report a help message
+ if is_fn_trait
+ && let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
+ obligation.param_env,
+ trait_ref.self_ty(),
+ trait_predicate.skip_binder().constness,
+ trait_predicate.skip_binder().polarity,
+ )
+ {
+ self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params);
+ } else if !trait_ref.has_non_region_infer()
+ && self.predicate_can_apply(obligation.param_env, *trait_predicate)
+ {
+ // If a where-clause may be useful, remind the
+ // user that they can add it.
+ //
+ // don't display an on-unimplemented note, as
+ // these notes will often be of the form
+ // "the type `T` can't be frobnicated"
+ // which is somewhat confusing.
+ self.suggest_restricting_param_bound(
+ err,
+ *trait_predicate,
+ None,
+ obligation.cause.body_id,
+ );
+ } else if !suggested && !unsatisfied_const {
+ // Can't show anything else useful, try to find similar impls.
+ let impl_candidates = self.find_similar_impl_candidates(*trait_predicate);
+ if !self.report_similar_impl_candidates(
+ &impl_candidates,
+ trait_ref,
+ body_def_id,
+ err,
+ true,
+ ) {
+ self.report_similar_impl_candidates_for_root_obligation(&obligation, *trait_predicate, body_def_id, err);
+ }
+
+ self.maybe_suggest_convert_to_slice(
+ err,
+ trait_ref,
+ impl_candidates.as_slice(),
+ span,
+ );
+ }
+ }
+
+ fn add_help_message_for_fn_trait(
+ &self,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ err: &mut Diagnostic,
+ implemented_kind: ty::ClosureKind,
+ params: ty::Binder<'tcx, Ty<'tcx>>,
+ ) {
+ // If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following
+ // suggestion to add trait bounds for the type, since we only typically implement
+ // these traits once.
+
+ // Note if the `FnMut` or `FnOnce` is less general than the trait we're trying
+ // to implement.
+ let selected_kind = self
+ .tcx
+ .fn_trait_kind_from_def_id(trait_ref.def_id())
+ .expect("expected to map DefId to ClosureKind");
+ if !implemented_kind.extends(selected_kind) {
+ err.note(format!(
+ "`{}` implements `{}`, but it must implement `{}`, which is more general",
+ trait_ref.skip_binder().self_ty(),
+ implemented_kind,
+ selected_kind
+ ));
+ }
+
+ // Note any argument mismatches
+ let given_ty = params.skip_binder();
+ let expected_ty = trait_ref.skip_binder().substs.type_at(1);
+ if let ty::Tuple(given) = given_ty.kind()
+ && let ty::Tuple(expected) = expected_ty.kind()
+ {
+ if expected.len() != given.len() {
+ // Note number of types that were expected and given
+ err.note(
+ format!(
+ "expected a closure taking {} argument{}, but one taking {} argument{} was given",
+ given.len(),
+ pluralize!(given.len()),
+ expected.len(),
+ pluralize!(expected.len()),
+ )
+ );
+ } else if !self.same_type_modulo_infer(given_ty, expected_ty) {
+ // Print type mismatch
+ let (expected_args, given_args) =
+ self.cmp(given_ty, expected_ty);
+ err.note_expected_found(
+ &"a closure with arguments",
+ expected_args,
+ &"a closure with arguments",
+ given_args,
+ );
+ }
+ }
+ }
+
+ fn maybe_add_note_for_unsatisfied_const(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+ err: &mut Diagnostic,
+ span: Span,
+ ) -> UnsatisfiedConst {
+ let mut unsatisfied_const = UnsatisfiedConst(false);
+ if trait_predicate.is_const_if_const() && obligation.param_env.is_const() {
+ let non_const_predicate = trait_ref.without_const();
+ let non_const_obligation = Obligation {
+ cause: obligation.cause.clone(),
+ param_env: obligation.param_env.without_const(),
+ predicate: non_const_predicate.to_predicate(self.tcx),
+ recursion_depth: obligation.recursion_depth,
+ };
+ if self.predicate_may_hold(&non_const_obligation) {
+ unsatisfied_const = UnsatisfiedConst(true);
+ err.span_note(
+ span,
+ format!(
+ "the trait `{}` is implemented for `{}`, \
+ but that implementation is not `const`",
+ non_const_predicate.print_modifiers_and_trait_path(),
+ trait_ref.skip_binder().self_ty(),
+ ),
+ );
+ }
+ }
+ unsatisfied_const
+ }
+
+ fn report_closure_error(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ closure_def_id: DefId,
+ found_kind: ty::ClosureKind,
+ kind: ty::ClosureKind,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let closure_span = self.tcx.def_span(closure_def_id);
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ closure_span,
+ E0525,
+ "expected a closure that implements the `{}` trait, \
+ but this closure only implements `{}`",
+ kind,
+ found_kind
+ );
+
+ err.span_label(
+ closure_span,
+ format!("this closure implements `{}`, not `{}`", found_kind, kind),
+ );
+ err.span_label(
+ obligation.cause.span,
+ format!("the requirement to implement `{}` derives from here", kind),
+ );
+
+ // Additional context information explaining why the closure only implements
+ // a particular trait.
+ if let Some(typeck_results) = &self.typeck_results {
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local());
+ match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) {
+ (ty::ClosureKind::FnOnce, Some((span, place))) => {
+ err.span_label(
+ *span,
+ format!(
+ "closure is `FnOnce` because it moves the \
+ variable `{}` out of its environment",
+ ty::place_to_string_for_capture(self.tcx, place)
+ ),
+ );
+ }
+ (ty::ClosureKind::FnMut, Some((span, place))) => {
+ err.span_label(
+ *span,
+ format!(
+ "closure is `FnMut` because it mutates the \
+ variable `{}` here",
+ ty::place_to_string_for_capture(self.tcx, place)
+ ),
+ );
+ }
+ _ => {}
+ }
+ }
+
+ err
+ }
+
+ fn report_type_parameter_mismatch_cyclic_type_error(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ terr: TypeError<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let self_ty = found_trait_ref.self_ty().skip_binder();
+ let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() {
+ (
+ ObligationCause::dummy_with_span(self.tcx.def_span(def_id)),
+ TypeError::CyclicTy(self_ty),
+ )
+ } else {
+ (obligation.cause.clone(), terr)
+ };
+ self.report_and_explain_type_error(
+ TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref),
+ terr,
+ )
+ }
+
+ fn report_type_parameter_mismatch_error(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ span: Span,
+ found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+ let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref);
+ let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref);
+
+ if expected_trait_ref.self_ty().references_error() {
+ return None;
+ }
+
+ let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else {
+ return None;
+ };
+
+ let found_did = match *found_trait_ty.kind() {
+ ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) | ty::Generator(did, ..) => {
+ Some(did)
+ }
+ ty::Adt(def, _) => Some(def.did()),
+ _ => None,
+ };
+
+ let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did));
+ let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did));
+
+ if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) {
+ // We check closures twice, with obligations flowing in different directions,
+ // but we want to complain about them only once.
+ return None;
+ }
+
+ self.reported_closure_mismatch.borrow_mut().insert((span, found_span));
+
+ let mut not_tupled = false;
+
+ let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() {
+ ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()],
+ _ => {
+ not_tupled = true;
+ vec![ArgKind::empty()]
+ }
+ };
+
+ let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1);
+ let expected = match expected_ty.kind() {
+ ty::Tuple(ref tys) => {
+ tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect()
+ }
+ _ => {
+ not_tupled = true;
+ vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())]
+ }
+ };
+
+ // If this is a `Fn` family trait and either the expected or found
+ // is not tupled, then fall back to just a regular mismatch error.
+ // This shouldn't be common unless manually implementing one of the
+ // traits manually, but don't make it more confusing when it does
+ // happen.
+ Some(
+ if Some(expected_trait_ref.def_id()) != self.tcx.lang_items().gen_trait() && not_tupled
+ {
+ self.report_and_explain_type_error(
+ TypeTrace::poly_trait_refs(
+ &obligation.cause,
+ true,
+ expected_trait_ref,
+ found_trait_ref,
+ ),
+ ty::error::TypeError::Mismatch,
+ )
+ } else if found.len() == expected.len() {
+ self.report_closure_arg_mismatch(
+ span,
+ found_span,
+ found_trait_ref,
+ expected_trait_ref,
+ obligation.cause.code(),
+ found_node,
+ obligation.param_env,
+ )
+ } else {
+ let (closure_span, closure_arg_span, found) = found_did
+ .and_then(|did| {
+ let node = self.tcx.hir().get_if_local(did)?;
+ let (found_span, closure_arg_span, found) =
+ self.get_fn_like_arguments(node)?;
+ Some((Some(found_span), closure_arg_span, found))
+ })
+ .unwrap_or((found_span, None, found));
+
+ self.report_arg_count_mismatch(
+ span,
+ closure_span,
+ expected,
+ found,
+ found_trait_ty.is_closure(),
+ closure_arg_span,
+ )
+ },
+ )
+ }
+
+ fn report_not_const_evaluatable_error(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ span: Span,
+ ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+ if !self.tcx.features().generic_const_exprs {
+ let mut err = self
+ .tcx
+ .sess
+ .struct_span_err(span, "constant expression depends on a generic parameter");
+ // FIXME(const_generics): we should suggest to the user how they can resolve this
+ // issue. However, this is currently not actually possible
+ // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
+ //
+ // Note that with `feature(generic_const_exprs)` this case should not
+ // be reachable.
+ err.note("this may fail depending on what value the parameter takes");
+ err.emit();
+ return None;
+ }
+
+ match obligation.predicate.kind().skip_binder() {
+ ty::PredicateKind::ConstEvaluatable(ct) => {
+ let ty::ConstKind::Unevaluated(uv) = ct.kind() else {
+ bug!("const evaluatable failed for non-unevaluated const `{ct:?}`");
+ };
+ let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant");
+ let const_span = self.tcx.def_span(uv.def);
+ match self.tcx.sess.source_map().span_to_snippet(const_span) {
+ Ok(snippet) => err.help(format!(
+ "try adding a `where` bound using this expression: `where [(); {}]:`",
+ snippet
+ )),
+ _ => err.help("consider adding a `where` bound using this expression"),
+ };
+ Some(err)
+ }
+ _ => {
+ span_bug!(
+ span,
+ "unexpected non-ConstEvaluatable predicate, this should not be reachable"
+ )
+ }
+ }
+ }
}
+struct UnsatisfiedConst(pub bool);
+
+fn get_explanation_based_on_obligation<'tcx>(
+ obligation: &PredicateObligation<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+ pre_message: String,
+) -> String {
+ if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
+ "consider using `()`, or a `Result`".to_owned()
+ } else {
+ let ty_desc = match trait_ref.skip_binder().self_ty().kind() {
+ ty::FnDef(_, _) => Some("fn item"),
+ ty::Closure(_, _) => Some("closure"),
+ _ => None,
+ };
+
+ match ty_desc {
+ Some(desc) => format!(
+ "{}the trait `{}` is not implemented for {} `{}`",
+ pre_message,
+ trait_predicate.print_modifiers_and_trait_path(),
+ desc,
+ trait_ref.skip_binder().self_ty(),
+ ),
+ None => format!(
+ "{}the trait `{}` is not implemented for `{}`",
+ pre_message,
+ trait_predicate.print_modifiers_and_trait_path(),
+ trait_ref.skip_binder().self_ty(),
+ ),
+ }
+ }
+}
/// Crude way of getting back an `Expr` from a `Span`.
pub struct FindExprBySpan<'hir> {
pub span: Span,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index a9c4e1268..10bd027b6 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
@@ -306,6 +306,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
}
+
+ // `&[{integral}]` - `FromIterator` needs that.
+ if let ty::Ref(_, ref_ty, rustc_ast::Mutability::Not) = self_ty.kind()
+ && let ty::Slice(sty) = ref_ty.kind()
+ && sty.is_integral()
+ {
+ flags.push((sym::_Self, Some("&[{integral}]".to_owned())));
+ }
});
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
@@ -327,7 +335,7 @@ pub struct OnUnimplementedDirective {
pub label: Option<OnUnimplementedFormatString>,
pub note: Option<OnUnimplementedFormatString>,
pub parent_label: Option<OnUnimplementedFormatString>,
- pub append_const_msg: Option<Option<Symbol>>,
+ pub append_const_msg: Option<AppendConstMessage>,
}
/// For the `#[rustc_on_unimplemented]` attribute
@@ -337,11 +345,21 @@ pub struct OnUnimplementedNote {
pub label: Option<String>,
pub note: Option<String>,
pub parent_label: Option<String>,
- /// Append a message for `~const Trait` errors. `None` means not requested and
- /// should fallback to a generic message, `Some(None)` suggests using the default
- /// appended message, `Some(Some(s))` suggests use the `s` message instead of the
- /// default one..
- pub append_const_msg: Option<Option<Symbol>>,
+ // If none, should fall back to a generic message
+ pub append_const_msg: Option<AppendConstMessage>,
+}
+
+/// Append a message for `~const Trait` errors.
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum AppendConstMessage {
+ Default,
+ Custom(Symbol),
+}
+
+impl Default for AppendConstMessage {
+ fn default() -> Self {
+ AppendConstMessage::Default
+ }
}
impl<'tcx> OnUnimplementedDirective {
@@ -419,10 +437,10 @@ impl<'tcx> OnUnimplementedDirective {
}
} else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() {
if let Some(msg) = item.value_str() {
- append_const_msg = Some(Some(msg));
+ append_const_msg = Some(AppendConstMessage::Custom(msg));
continue;
} else if item.is_word() {
- append_const_msg = Some(None);
+ append_const_msg = Some(AppendConstMessage::Default);
continue;
}
}
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 fb75ec767..82bad96ea 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -25,23 +25,22 @@ use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
use rustc_hir::{Expr, HirId};
use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::{InferOk, LateBoundRegionConversionTime};
+use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime};
use rustc_middle::hir::map;
use rustc_middle::ty::error::TypeError::{self, Sorts};
-use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::{
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind,
- GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, InternalSubsts,
- IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder,
- TypeSuperFoldable, TypeVisitableExt, TypeckResults,
+ GeneratorDiagnosticData, GeneratorInteriorTypeCause, InferTy, InternalSubsts, IsSuggestable,
+ ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable,
+ TypeVisitableExt, TypeckResults,
};
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::iter;
use std::ops::Deref;
-use super::method_chain::CollectAllMismatches;
use super::InferCtxtPrivExt;
use crate::infer::InferCtxtExt as _;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -262,7 +261,6 @@ pub trait TypeErrCtxtExt<'tcx> {
fn suggest_impl_trait(
&self,
err: &mut Diagnostic,
- span: Span,
obligation: &PredicateObligation<'tcx>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
@@ -319,6 +317,7 @@ pub trait TypeErrCtxtExt<'tcx> {
fn note_obligation_cause_code<T>(
&self,
+ body_id: LocalDefId,
err: &mut Diagnostic,
predicate: T,
param_env: ty::ParamEnv<'tcx>,
@@ -359,8 +358,9 @@ pub trait TypeErrCtxtExt<'tcx> {
);
fn note_function_argument_obligation(
&self,
- arg_hir_id: HirId,
+ body_id: LocalDefId,
err: &mut Diagnostic,
+ arg_hir_id: HirId,
parent_code: &ObligationCauseCode<'tcx>,
param_env: ty::ParamEnv<'tcx>,
predicate: ty::Predicate<'tcx>,
@@ -386,7 +386,7 @@ pub trait TypeErrCtxtExt<'tcx> {
fn maybe_suggest_convert_to_slice(
&self,
err: &mut Diagnostic,
- trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
candidate_impls: &[ImplCandidate<'tcx>],
span: Span,
);
@@ -420,7 +420,7 @@ fn suggest_restriction<'tcx>(
) {
if hir_generics.where_clause_span.from_expansion()
|| hir_generics.where_clause_span.desugaring_kind().is_some()
- || projection.map_or(false, |projection| tcx.opt_rpitit_info(projection.def_id).is_some())
+ || projection.is_some_and(|projection| tcx.opt_rpitit_info(projection.def_id).is_some())
{
return;
}
@@ -515,7 +515,7 @@ fn suggest_restriction<'tcx>(
err.span_suggestion_verbose(
sp,
- &format!("consider further restricting {}", msg),
+ format!("consider further restricting {}", msg),
suggestion,
Applicability::MachineApplicable,
);
@@ -530,6 +530,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
associated_ty: Option<(&'static str, Ty<'tcx>)>,
mut body_id: LocalDefId,
) {
+ if trait_pred.skip_binder().polarity == ty::ImplPolarity::Negative {
+ return;
+ }
+
let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
let self_ty = trait_pred.skip_binder().self_ty();
@@ -964,7 +968,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// a more general note.
err.span_suggestion_verbose(
obligation.cause.span.shrink_to_hi(),
- &msg,
+ msg,
format!("({args})"),
Applicability::HasPlaceholders,
);
@@ -994,7 +998,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
_ => return false,
};
- err.help(&format!("{msg}: `{name}({args})`"));
+ err.help(format!("{msg}: `{name}({args})`"));
}
true
}
@@ -1334,7 +1338,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let msg = format!("the trait bound `{}` is not satisfied", old_pred);
if has_custom_message {
- err.note(&msg);
+ err.note(msg);
} else {
err.message =
vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
@@ -1358,7 +1362,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
} else {
let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut;
let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" });
- let sugg_msg = &format!(
+ let sugg_msg = format!(
"consider{} borrowing here",
if is_mut { " mutably" } else { "" }
);
@@ -1381,7 +1385,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- // Issue #104961, we need to add parentheses properly for compond expressions
+ // Issue #104961, we need to add parentheses properly for compound expressions
// for example, `x.starts_with("hi".to_string() + "you")`
// should be `x.starts_with(&("hi".to_string() + "you"))`
let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return false; };
@@ -1437,8 +1441,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
self_ty: Ty<'tcx>,
- object_ty: Ty<'tcx>,
+ target_ty: Ty<'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);
@@ -1452,8 +1457,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.span_suggestion(
obligation.cause.span.shrink_to_lo(),
- &format!(
- "consider borrowing the value, since `&{self_ty}` can be coerced into `{object_ty}`"
+ format!(
+ "consider borrowing the value, since `&{self_ty}` can be coerced into `{target_ty}`"
),
"&",
Applicability::MaybeIncorrect,
@@ -1505,7 +1510,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
};
err.multipart_suggestion_verbose(
- &msg,
+ msg,
suggestions,
Applicability::MachineApplicable,
);
@@ -1583,55 +1588,68 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
- let span = obligation.cause.span;
-
- if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() {
- let hir = self.tcx.hir();
- if let Some(hir::Node::Expr(expr)) = hir_id.and_then(|hir_id| hir.find(hir_id)) {
- // FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()`
- // and if not maybe suggest doing something else? If we kept the expression around we
- // could also check if it is an fn call (very likely) and suggest changing *that*, if
- // it is from the local crate.
+ let hir = self.tcx.hir();
+ if let ObligationCauseCode::AwaitableExpr(Some(hir_id)) = obligation.cause.code().peel_derives()
+ && let hir::Node::Expr(expr) = hir.get(*hir_id)
+ {
+ // FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()`
+ // and if not maybe suggest doing something else? If we kept the expression around we
+ // could also check if it is an fn call (very likely) and suggest changing *that*, if
+ // it is from the local crate.
+
+ // use nth(1) to skip one layer of desugaring from `IntoIter::into_iter`
+ if let Some((_, hir::Node::Expr(await_expr))) = hir.parent_iter(*hir_id).nth(1)
+ && let Some(expr_span) = expr.span.find_ancestor_inside(await_expr.span)
+ {
+ let removal_span = self.tcx
+ .sess
+ .source_map()
+ .span_extend_while(expr_span, char::is_whitespace)
+ .unwrap_or(expr_span)
+ .shrink_to_hi()
+ .to(await_expr.span.shrink_to_hi());
err.span_suggestion(
- span,
+ removal_span,
"remove the `.await`",
"",
Applicability::MachineApplicable,
);
- // 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)) =
- obligation.predicate.kind().skip_binder()
+ } else {
+ err.span_label(obligation.cause.span, "remove the `.await`");
+ }
+ // 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)) =
+ obligation.predicate.kind().skip_binder()
+ {
+ err.span_label(*span, format!("this call returns `{}`", pred.self_ty()));
+ }
+ if let Some(typeck_results) = &self.typeck_results
+ && let ty = typeck_results.expr_ty_adjusted(base)
+ && let ty::FnDef(def_id, _substs) = ty.kind()
+ && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) =
+ hir.get_if_local(*def_id)
{
- err.span_label(*span, &format!("this call returns `{}`", pred.self_ty()));
- }
- if let Some(typeck_results) = &self.typeck_results
- && let ty = typeck_results.expr_ty_adjusted(base)
- && let ty::FnDef(def_id, _substs) = ty.kind()
- && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) =
- hir.get_if_local(*def_id)
- {
- let msg = format!(
- "alternatively, consider making `fn {}` asynchronous",
- ident
+ let msg = format!(
+ "alternatively, consider making `fn {}` asynchronous",
+ ident
+ );
+ if vis_span.is_empty() {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ msg,
+ "async ",
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_suggestion_verbose(
+ vis_span.shrink_to_hi(),
+ msg,
+ " async",
+ Applicability::MaybeIncorrect,
);
- if vis_span.is_empty() {
- err.span_suggestion_verbose(
- span.shrink_to_lo(),
- &msg,
- "async ",
- Applicability::MaybeIncorrect,
- );
- } else {
- err.span_suggestion_verbose(
- vis_span.shrink_to_hi(),
- &msg,
- " async",
- Applicability::MaybeIncorrect,
- );
- }
}
- }
+ }
}
}
}
@@ -1704,7 +1722,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
Applicability::MachineApplicable,
);
} else {
- err.note(&format!(
+ err.note(format!(
"`{}` is implemented for `{:?}`, but not for `{:?}`",
trait_pred.print_modifiers_and_trait_path(),
suggested_ty,
@@ -1741,7 +1759,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
err.span_label(
expr.span,
- &format!(
+ format!(
"this expression has type `{}`, which implements `{}`",
ty,
trait_pred.print_modifiers_and_trait_path()
@@ -1773,215 +1791,66 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn suggest_impl_trait(
&self,
err: &mut Diagnostic,
- span: Span,
obligation: &PredicateObligation<'tcx>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
- match obligation.cause.code().peel_derives() {
- // Only suggest `impl Trait` if the return type is unsized because it is `dyn Trait`.
- ObligationCauseCode::SizedReturnType => {}
- _ => return false,
- }
-
- let hir = self.tcx.hir();
- let fn_hir_id = hir.local_def_id_to_hir_id(obligation.cause.body_id);
- let node = hir.find_by_def_id(obligation.cause.body_id);
- let Some(hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Fn(sig, _, body_id),
- ..
- })) = node
- else {
+ let ObligationCauseCode::SizedReturnType = obligation.cause.code() else {
return false;
};
- let body = hir.body(*body_id);
- let trait_pred = self.resolve_vars_if_possible(trait_pred);
- let ty = trait_pred.skip_binder().self_ty();
- let is_object_safe = match ty.kind() {
- ty::Dynamic(predicates, _, ty::Dyn) => {
- // If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
- predicates
- .principal_def_id()
- .map_or(true, |def_id| self.tcx.check_is_object_safe(def_id))
- }
- // We only want to suggest `impl Trait` to `dyn Trait`s.
- // For example, `fn foo() -> str` needs to be filtered out.
- _ => return false,
- };
-
- let hir::FnRetTy::Return(ret_ty) = sig.decl.output else {
+ let ty::Dynamic(_, _, ty::Dyn) = trait_pred.self_ty().skip_binder().kind() else {
return false;
};
- // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for
- // cases like `fn foo() -> (dyn Trait, i32) {}`.
- // Recursively look for `TraitObject` types and if there's only one, use that span to
- // suggest `impl Trait`.
-
- // Visit to make sure there's a single `return` type to suggest `impl Trait`,
- // otherwise suggest using `Box<dyn Trait>` or an enum.
- let mut visitor = ReturnsVisitor::default();
- visitor.visit_body(&body);
-
- let typeck_results = self.typeck_results.as_ref().unwrap();
- let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; };
-
- let ret_types = visitor
- .returns
- .iter()
- .filter_map(|expr| Some((expr.span, typeck_results.node_type_opt(expr.hir_id)?)))
- .map(|(expr_span, ty)| (expr_span, self.resolve_vars_if_possible(ty)));
- let (last_ty, all_returns_have_same_type, only_never_return) = ret_types.clone().fold(
- (None, true, true),
- |(last_ty, mut same, only_never_return): (std::option::Option<Ty<'_>>, bool, bool),
- (_, ty)| {
- let ty = self.resolve_vars_if_possible(ty);
- same &=
- !matches!(ty.kind(), ty::Error(_))
- && last_ty.map_or(true, |last_ty| {
- // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes
- // *after* in the dependency graph.
- match (ty.kind(), last_ty.kind()) {
- (Infer(InferTy::IntVar(_)), Infer(InferTy::IntVar(_)))
- | (Infer(InferTy::FloatVar(_)), Infer(InferTy::FloatVar(_)))
- | (Infer(InferTy::FreshIntTy(_)), Infer(InferTy::FreshIntTy(_)))
- | (
- Infer(InferTy::FreshFloatTy(_)),
- Infer(InferTy::FreshFloatTy(_)),
- ) => true,
- _ => ty == last_ty,
- }
- });
- (Some(ty), same, only_never_return && matches!(ty.kind(), ty::Never))
- },
- );
- let mut spans_and_needs_box = vec![];
-
- match liberated_sig.output().kind() {
- ty::Dynamic(predicates, _, ty::Dyn) => {
- let cause = ObligationCause::misc(ret_ty.span, obligation.cause.body_id);
- let param_env = ty::ParamEnv::empty();
-
- if !only_never_return {
- for (expr_span, return_ty) in ret_types {
- let self_ty_satisfies_dyn_predicates = |self_ty| {
- predicates.iter().all(|predicate| {
- let pred = predicate.with_self_ty(self.tcx, self_ty);
- let obl = Obligation::new(self.tcx, cause.clone(), param_env, pred);
- self.predicate_may_hold(&obl)
- })
- };
-
- if let ty::Adt(def, substs) = return_ty.kind()
- && def.is_box()
- && self_ty_satisfies_dyn_predicates(substs.type_at(0))
- {
- spans_and_needs_box.push((expr_span, false));
- } else if self_ty_satisfies_dyn_predicates(return_ty) {
- spans_and_needs_box.push((expr_span, true));
- } else {
- return false;
- }
- }
- }
- }
- _ => return false,
- };
-
- let sm = self.tcx.sess.source_map();
- if !ret_ty.span.overlaps(span) {
- return false;
- }
- let snippet = if let hir::TyKind::TraitObject(..) = ret_ty.kind {
- if let Ok(snippet) = sm.span_to_snippet(ret_ty.span) {
- snippet
- } else {
- return false;
- }
- } else {
- // Substitute the type, so we can print a fixup given `type Alias = dyn Trait`
- let name = liberated_sig.output().to_string();
- let name =
- name.strip_prefix('(').and_then(|name| name.strip_suffix(')')).unwrap_or(&name);
- if !name.starts_with("dyn ") {
- return false;
- }
- name.to_owned()
- };
-
err.code(error_code!(E0746));
err.set_primary_message("return type cannot have an unboxed trait object");
err.children.clear();
- let impl_trait_msg = "for information on `impl Trait`, see \
- <https://doc.rust-lang.org/book/ch10-02-traits.html\
- #returning-types-that-implement-traits>";
- let trait_obj_msg = "for information on trait objects, see \
- <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
- #using-trait-objects-that-allow-for-values-of-different-types>";
-
- let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn");
- let trait_obj = if has_dyn { &snippet[4..] } else { &snippet };
- if only_never_return {
- // No return paths, probably using `panic!()` or similar.
- // Suggest `-> impl Trait`, and if `Trait` is object safe, `-> Box<dyn Trait>`.
- suggest_trait_object_return_type_alternatives(
- err,
- ret_ty.span,
- trait_obj,
- is_object_safe,
- );
- } else if let (Some(last_ty), true) = (last_ty, all_returns_have_same_type) {
- // Suggest `-> impl Trait`.
+
+ let span = obligation.cause.span;
+ if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span)
+ && snip.starts_with("dyn ")
+ {
err.span_suggestion(
- ret_ty.span,
- &format!(
- "use `impl {1}` as the return type, as all return paths are of type `{}`, \
- which implements `{1}`",
- last_ty, trait_obj,
- ),
- format!("impl {}", trait_obj),
- Applicability::MachineApplicable,
+ span.with_hi(span.lo() + BytePos(4)),
+ "return an `impl Trait` instead of a `dyn Trait`, \
+ if all returned values are the same type",
+ "impl ",
+ Applicability::MaybeIncorrect,
);
- err.note(impl_trait_msg);
- } else {
- if is_object_safe {
- // Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
- err.multipart_suggestion(
- "return a boxed trait object instead",
- vec![
- (ret_ty.span.shrink_to_lo(), "Box<".to_string()),
- (span.shrink_to_hi(), ">".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- for (span, needs_box) in spans_and_needs_box {
- if needs_box {
- err.multipart_suggestion(
- "... and box this value",
- vec![
- (span.shrink_to_lo(), "Box::new(".to_string()),
- (span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- }
+ }
+
+ let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(obligation.cause.body_id));
+
+ let mut visitor = ReturnsVisitor::default();
+ visitor.visit_body(&body);
+
+ let mut sugg =
+ vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())];
+ sugg.extend(visitor.returns.into_iter().flat_map(|expr| {
+ let span = expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span);
+ if !span.can_be_used_for_suggestions() {
+ vec![]
+ } else if let hir::ExprKind::Call(path, ..) = expr.kind
+ && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, method)) = path.kind
+ && method.ident.name == sym::new
+ && let hir::TyKind::Path(hir::QPath::Resolved(.., box_path)) = ty.kind
+ && box_path.res.opt_def_id().is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box())
+ {
+ // Don't box `Box::new`
+ vec![]
} else {
- // This is currently not possible to trigger because E0038 takes precedence, but
- // leave it in for completeness in case anything changes in an earlier stage.
- err.note(&format!(
- "if trait `{}` were object-safe, you could return a trait object",
- trait_obj,
- ));
+ vec![
+ (span.shrink_to_lo(), "Box::new(".to_string()),
+ (span.shrink_to_hi(), ")".to_string()),
+ ]
}
- err.note(trait_obj_msg);
- err.note(&format!(
- "if all the returned values were of the same type you could use `impl {}` as the \
- return type",
- trait_obj,
- ));
- err.note(impl_trait_msg);
- err.note("you can create a new `enum` with a variant for each returned type");
- }
+ }));
+
+ err.multipart_suggestion(
+ "box the return type, and wrap all of the returned values in `Box::new`",
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+
true
}
@@ -2014,7 +1883,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
} else {
err.span_label(
expr.span,
- &format!("this returned value is of type `{}`", ty),
+ format!("this returned value is of type `{}`", ty),
);
}
}
@@ -2164,7 +2033,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
) {
if let Some(assoc_item) = self.tcx.opt_associated_item(item_def_id) {
if let ty::AssocKind::Const | ty::AssocKind::Type = assoc_item.kind {
- err.note(&format!(
+ err.note(format!(
"{}s cannot be accessed directly on a `trait`, they can only be \
accessed through a specific `impl`",
self.tcx.def_kind_descr(assoc_item.kind.as_def_kind(), item_def_id)
@@ -2428,10 +2297,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& generator_did.is_local()
// Try to avoid cycles.
&& !generator_within_in_progress_typeck
+ && let Some(generator_info) = self.tcx.mir_generator_witnesses(generator_did)
{
- let generator_info = &self.tcx.mir_generator_witnesses(generator_did);
debug!(?generator_info);
-
'find_source: for (variant, source_info) in
generator_info.variant_fields.iter().zip(&generator_info.variant_source_info)
{
@@ -2594,7 +2462,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
err.span_note(
span,
- &format!(
+ format!(
"{} {} as this value is used across {}",
future_or_generator, trait_explanation, an_await_or_yield
),
@@ -2615,7 +2483,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
err.span_note(
span,
- &format!(
+ format!(
"future {not_trait} as it awaits another future which {not_trait}",
not_trait = trait_explanation
),
@@ -2717,7 +2585,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut span = MultiSpan::from_span(upvar_span);
span.push_span_label(upvar_span, span_label);
- err.span_note(span, &span_note);
+ err.span_note(span, span_note);
}
}
@@ -2725,6 +2593,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// bound that introduced the obligation (e.g. `T: Send`).
debug!(?next_code);
self.note_obligation_cause_code(
+ obligation.cause.body_id,
err,
obligation.predicate,
obligation.param_env,
@@ -2736,6 +2605,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn note_obligation_cause_code<T>(
&self,
+ body_id: LocalDefId,
err: &mut Diagnostic,
predicate: T,
param_env: ty::ParamEnv<'tcx>,
@@ -2773,7 +2643,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::LetElse
| ObligationCauseCode::BinOp { .. }
| ObligationCauseCode::AscribeUserTypeProvePredicate(..)
- | ObligationCauseCode::RustCall => {}
+ | ObligationCauseCode::RustCall
+ | ObligationCauseCode::DropImpl => {}
ObligationCauseCode::SliceOrArrayElem => {
err.note("slice and array elements must have `Sized` type");
}
@@ -2781,15 +2652,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.note("only the last element of a tuple may have a dynamically sized type");
}
ObligationCauseCode::ProjectionWf(data) => {
- err.note(&format!("required so that the projection `{data}` is well-formed"));
+ err.note(format!("required so that the projection `{data}` is well-formed"));
}
ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => {
- err.note(&format!(
+ err.note(format!(
"required so that reference `{ref_ty}` does not outlive its referent"
));
}
ObligationCauseCode::ObjectTypeBound(object_ty, region) => {
- err.note(&format!(
+ err.note(format!(
"required so that the lifetime bound of `{}` for `{}` is satisfied",
region, object_ty,
));
@@ -2825,35 +2696,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if span.is_visible(sm) {
let msg = format!("required by this bound in `{short_item_name}`");
multispan.push_span_label(span, msg);
- err.span_note(multispan, &descr);
+ err.span_note(multispan, descr);
} else {
- err.span_note(tcx.def_span(item_def_id), &descr);
+ err.span_note(tcx.def_span(item_def_id), descr);
}
}
- ObligationCauseCode::ObjectCastObligation(concrete_ty, object_ty) => {
- let (concrete_ty, concrete_file) =
- self.tcx.short_ty_string(self.resolve_vars_if_possible(concrete_ty));
- let (object_ty, object_file) =
- self.tcx.short_ty_string(self.resolve_vars_if_possible(object_ty));
- err.note(&with_forced_trimmed_paths!(format!(
- "required for the cast from `{concrete_ty}` to the object type `{object_ty}`",
+ ObligationCauseCode::Coercion { source, target } => {
+ let (source, source_file) =
+ self.tcx.short_ty_string(self.resolve_vars_if_possible(source));
+ let (target, target_file) =
+ self.tcx.short_ty_string(self.resolve_vars_if_possible(target));
+ err.note(with_forced_trimmed_paths!(format!(
+ "required for the cast from `{source}` to `{target}`",
)));
- if let Some(file) = concrete_file {
- err.note(&format!(
- "the full name for the casted type has been written to '{}'",
+ if let Some(file) = source_file {
+ err.note(format!(
+ "the full name for the source type has been written to '{}'",
file.display(),
));
}
- if let Some(file) = object_file {
- err.note(&format!(
- "the full name for the object type has been written to '{}'",
+ if let Some(file) = target_file {
+ err.note(format!(
+ "the full name for the target type has been written to '{}'",
file.display(),
));
}
}
- ObligationCauseCode::Coercion { source: _, target } => {
- err.note(&format!("required by cast to type `{}`", self.ty_to_string(target)));
- }
ObligationCauseCode::RepeatElementCopy { is_const_fn } => {
err.note(
"the `Copy` trait is required because this value will be copied for each element of the array",
@@ -3055,8 +2923,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
));
match ty.kind() {
ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) {
- Some(ident) => err.span_note(ident.span, &msg),
- None => err.note(&msg),
+ Some(ident) => err.span_note(ident.span, msg),
+ None => err.note(msg),
},
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
// If the previous type is async fn, this is the future generated by the body of an async function.
@@ -3068,7 +2936,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
"note_obligation_cause_code: check for async fn"
);
if is_future
- && obligated_types.last().map_or(false, |ty| match ty.kind() {
+ && obligated_types.last().is_some_and(|ty| match ty.kind() {
ty::Generator(last_def_id, ..) => {
tcx.generator_is_async(*last_def_id)
}
@@ -3077,7 +2945,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
break 'print;
}
- err.span_note(self.tcx.def_span(def_id), &msg)
+ err.span_note(self.tcx.def_span(def_id), msg)
}
ty::GeneratorWitness(bound_tys) => {
use std::fmt::Write;
@@ -3113,7 +2981,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let kind = tcx.generator_kind(def_id).unwrap().descr();
err.span_note(
sp,
- with_forced_trimmed_paths!(&format!(
+ with_forced_trimmed_paths!(format!(
"required because it's used within this {kind}",
)),
)
@@ -3123,7 +2991,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
"required because it's used within this closure",
),
ty::Str => err.note("`str` is considered to contain a `[u8]` slice for auto trait purposes"),
- _ => err.note(&msg),
+ _ => err.note(msg),
};
}
}
@@ -3135,6 +3003,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// #74711: avoid a stack overflow
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
+ body_id,
err,
parent_predicate,
param_env,
@@ -3146,6 +3015,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
} else {
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
+ body_id,
err,
parent_predicate,
param_env,
@@ -3177,7 +3047,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// FIXME: we should do something else so that it works even on crate foreign
// auto traits.
is_auto_trait = matches!(is_auto, hir::IsAuto::Yes);
- err.span_note(ident.span, &msg);
+ err.span_note(ident.span, msg);
}
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
@@ -3206,15 +3076,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
"unsatisfied trait bound introduced here",
);
}
- err.span_note(spans, &msg);
+ err.span_note(spans, msg);
}
_ => {
- err.note(&msg);
+ err.note(msg);
}
};
if let Some(file) = file {
- err.note(&format!(
+ err.note(format!(
"the full type name has been written to '{}'",
file.display(),
));
@@ -3254,19 +3124,19 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
parent_trait_pred = child_trait_pred;
}
if count > 0 {
- err.note(&format!(
+ err.note(format!(
"{} redundant requirement{} hidden",
count,
pluralize!(count)
));
let (self_ty, file) =
self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty());
- err.note(&format!(
+ err.note(format!(
"required for `{self_ty}` to implement `{}`",
parent_trait_pred.print_modifiers_and_trait_path()
));
if let Some(file) = file {
- err.note(&format!(
+ err.note(format!(
"the full type name has been written to '{}'",
file.display(),
));
@@ -3275,6 +3145,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// #74711: avoid a stack overflow
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
+ body_id,
err,
parent_predicate,
param_env,
@@ -3290,6 +3161,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// #74711: avoid a stack overflow
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
+ body_id,
err,
parent_predicate,
param_env,
@@ -3306,8 +3178,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
..
} => {
self.note_function_argument_obligation(
- arg_hir_id,
+ body_id,
err,
+ arg_hir_id,
parent_code,
param_env,
predicate,
@@ -3315,6 +3188,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
+ body_id,
err,
predicate,
param_env,
@@ -3347,7 +3221,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
assoc_span.push_span_label(ident.span, "in this trait");
}
- err.span_note(assoc_span, &msg);
+ err.span_note(assoc_span, msg);
}
ObligationCauseCode::TrivialBound => {
err.help("see issue #48214");
@@ -3485,7 +3359,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
_ => None,
};
let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
- trait_ref: self.tcx.mk_trait_ref(
+ trait_ref: ty::TraitRef::new(self.tcx,
trait_pred.def_id(),
[field_ty].into_iter().chain(trait_substs),
),
@@ -3503,7 +3377,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if can_derive {
err.span_suggestion_verbose(
self.tcx.def_span(adt.did()).shrink_to_lo(),
- &format!(
+ format!(
"consider annotating `{}` with `#[derive({})]`",
trait_pred.skip_binder().self_ty(),
diagnostic_name,
@@ -3536,8 +3410,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
fn note_function_argument_obligation(
&self,
- arg_hir_id: HirId,
+ body_id: LocalDefId,
err: &mut Diagnostic,
+ arg_hir_id: HirId,
parent_code: &ObligationCauseCode<'tcx>,
param_env: ty::ParamEnv<'tcx>,
failed_pred: ty::Predicate<'tcx>,
@@ -3570,7 +3445,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// to an associated type (as seen from `trait_pred`) in the predicate. Like in
// trait_pred `S: Sum<<Self as Iterator>::Item>` and predicate `i32: Sum<&()>`
let mut type_diffs = vec![];
-
if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code.deref()
&& let Some(node_substs) = typeck_results.node_substs_opt(call_hir_id)
&& let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_substs)
@@ -3579,14 +3453,26 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let Some(where_pred) = where_pred.to_opt_poly_trait_pred()
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
{
- let mut c = CollectAllMismatches {
- infcx: self.infcx,
- param_env,
- errors: vec![],
+ let where_pred = self.instantiate_binder_with_placeholders(where_pred);
+ let failed_pred = self.instantiate_binder_with_fresh_vars(
+ expr.span,
+ LateBoundRegionConversionTime::FnCall,
+ failed_pred
+ );
+
+ let zipped =
+ iter::zip(where_pred.trait_ref.substs, failed_pred.trait_ref.substs);
+ for (expected, actual) in zipped {
+ self.probe(|_| {
+ match self
+ .at(&ObligationCause::misc(expr.span, body_id), param_env)
+ .eq(DefineOpaqueTypes::No, expected, actual)
+ {
+ Ok(_) => (), // We ignore nested obligations here for now.
+ Err(err) => type_diffs.push(err),
+ }
+ })
};
- if let Ok(_) = c.relate(where_pred, failed_pred) {
- type_diffs = c.errors;
- }
} else if let Some(where_pred) = where_pred.to_opt_poly_projection_pred()
&& let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
&& let Some(found) = failed_pred.skip_binder().term.ty()
@@ -3848,7 +3734,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn maybe_suggest_convert_to_slice(
&self,
err: &mut Diagnostic,
- trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
candidate_impls: &[ImplCandidate<'tcx>],
span: Span,
) {
@@ -3890,7 +3776,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
.map(|trait_ref| trait_ref.trait_ref.self_ty())
.find(|t| is_slice(*t))
{
- let msg = &format!("convert the array to a `{}` slice instead", slice_ty);
+ let msg = format!("convert the array to a `{}` slice instead", slice_ty);
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let mut suggestions = vec![];
@@ -4103,37 +3989,6 @@ impl NextTypeParamName for &[hir::GenericParam<'_>] {
}
}
-fn suggest_trait_object_return_type_alternatives(
- err: &mut Diagnostic,
- ret_ty: Span,
- trait_obj: &str,
- is_object_safe: bool,
-) {
- err.span_suggestion(
- ret_ty,
- &format!(
- "use `impl {}` as the return type if all return paths have the same type but you \
- want to expose only the trait in the signature",
- trait_obj,
- ),
- format!("impl {}", trait_obj),
- Applicability::MaybeIncorrect,
- );
- if is_object_safe {
- err.multipart_suggestion(
- &format!(
- "use a boxed trait object if all return paths implement trait `{}`",
- trait_obj,
- ),
- vec![
- (ret_ty.shrink_to_lo(), "Box<".to_string()),
- (ret_ty.shrink_to_hi(), ">".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
-}
-
/// Collect the spans that we see the generic param `param_did`
struct ReplaceImplTraitVisitor<'a> {
ty_spans: &'a mut Vec<Span>,
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 26cadab3e..2f85c32b5 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -540,8 +540,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
use ty::ConstKind::Unevaluated;
match (c1.kind(), c2.kind()) {
(Unevaluated(a), Unevaluated(b))
- if a.def.did == b.def.did
- && tcx.def_kind(a.def.did) == DefKind::AssocConst =>
+ if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
{
if let Ok(new_obligations) = infcx
.at(&obligation.cause, obligation.param_env)
@@ -616,7 +615,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
(Err(ErrorHandled::Reported(reported)), _)
| (_, Err(ErrorHandled::Reported(reported))) => ProcessResult::Error(
CodeSelectionError(SelectionError::NotConstEvaluatable(
- NotConstEvaluatable::Error(reported),
+ NotConstEvaluatable::Error(reported.into()),
)),
),
(Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index af567c074..2210ef975 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -2,23 +2,29 @@
use crate::traits::{self, ObligationCause, ObligationCtxt};
+use hir::LangItem;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
use rustc_infer::infer::canonical::Canonical;
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
-use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, AdtDef, GenericArg, List, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::DUMMY_SP;
use super::outlives_bounds::InferCtxtExt;
pub enum CopyImplementationError<'tcx> {
- InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
+ InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
NotAnAdt,
HasDestructor,
}
+pub enum ConstParamTyImplementationError<'tcx> {
+ InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
+ NotAnAdtOrBuiltinAllowed,
+}
+
pub enum InfringingFieldsReason<'tcx> {
Fulfill(Vec<FulfillmentError<'tcx>>),
Regions(Vec<RegionResolutionError<'tcx>>),
@@ -27,7 +33,10 @@ pub enum InfringingFieldsReason<'tcx> {
/// Checks that the fields of the type (an ADT) all implement copy.
///
/// If fields don't implement copy, return an error containing a list of
-/// those violating fields. If it's not an ADT, returns `Err(NotAnAdt)`.
+/// those violating fields.
+///
+/// If it's not an ADT, int ty, `bool`, float ty, `char`, raw pointer, `!`,
+/// a reference or an array returns `Err(NotAnAdt)`.
pub fn type_allowed_to_implement_copy<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
@@ -47,12 +56,82 @@ pub fn type_allowed_to_implement_copy<'tcx>(
| ty::Ref(_, _, hir::Mutability::Not)
| ty::Array(..) => return Ok(()),
- ty::Adt(adt, substs) => (adt, substs),
+ &ty::Adt(adt, substs) => (adt, substs),
_ => return Err(CopyImplementationError::NotAnAdt),
};
- let copy_def_id = tcx.require_lang_item(hir::LangItem::Copy, Some(parent_cause.span));
+ all_fields_implement_trait(
+ tcx,
+ param_env,
+ self_type,
+ adt,
+ substs,
+ parent_cause,
+ hir::LangItem::Copy,
+ )
+ .map_err(CopyImplementationError::InfringingFields)?;
+
+ if adt.has_dtor(tcx) {
+ return Err(CopyImplementationError::HasDestructor);
+ }
+
+ Ok(())
+}
+
+/// Checks that the fields of the type (an ADT) all implement `ConstParamTy`.
+///
+/// If fields don't implement `ConstParamTy`, return an error containing a list of
+/// those violating fields.
+///
+/// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`.
+pub fn type_allowed_to_implement_const_param_ty<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ self_type: Ty<'tcx>,
+ parent_cause: ObligationCause<'tcx>,
+) -> Result<(), ConstParamTyImplementationError<'tcx>> {
+ let (adt, substs) = match self_type.kind() {
+ // `core` provides these impls.
+ ty::Uint(_)
+ | ty::Int(_)
+ | ty::Bool
+ | ty::Char
+ | ty::Str
+ | ty::Array(..)
+ | ty::Slice(_)
+ | ty::Ref(.., hir::Mutability::Not) => return Ok(()),
+
+ &ty::Adt(adt, substs) => (adt, substs),
+
+ _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
+ };
+
+ all_fields_implement_trait(
+ tcx,
+ param_env,
+ self_type,
+ adt,
+ substs,
+ parent_cause,
+ hir::LangItem::ConstParamTy,
+ )
+ .map_err(ConstParamTyImplementationError::InfrigingFields)?;
+
+ Ok(())
+}
+
+/// Check that all fields of a given `adt` implement `lang_item` trait.
+pub fn all_fields_implement_trait<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ self_type: Ty<'tcx>,
+ adt: AdtDef<'tcx>,
+ substs: &'tcx List<GenericArg<'tcx>>,
+ parent_cause: ObligationCause<'tcx>,
+ lang_item: LangItem,
+) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>> {
+ let trait_def_id = tcx.require_lang_item(lang_item, Some(parent_cause.span));
let mut infringing = Vec::new();
for variant in adt.variants() {
@@ -93,7 +172,7 @@ pub fn type_allowed_to_implement_copy<'tcx>(
// between expected and found const-generic types. Don't report an
// additional copy error here, since it's not typically useful.
if !normalization_errors.is_empty() || ty.references_error() {
- tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking Copy implementation"));
+ tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking {tr} implementation", tr = tcx.def_path_str(trait_def_id)));
continue;
}
@@ -101,7 +180,7 @@ pub fn type_allowed_to_implement_copy<'tcx>(
ObligationCause::dummy_with_span(field_ty_span),
param_env,
ty,
- copy_def_id,
+ trait_def_id,
);
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
@@ -124,15 +203,7 @@ pub fn type_allowed_to_implement_copy<'tcx>(
}
}
- if !infringing.is_empty() {
- return Err(CopyImplementationError::InfrigingFields(infringing));
- }
-
- if adt.has_dtor(tcx) {
- return Err(CopyImplementationError::HasDestructor);
- }
-
- Ok(())
+ if infringing.is_empty() { Ok(()) } else { Err(infringing) }
}
pub fn check_tys_might_be_eq<'tcx>(
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 8a203dec8..f265230ff 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -17,6 +17,7 @@ pub mod query;
mod select;
mod specialize;
mod structural_match;
+mod structural_normalize;
mod util;
mod vtable;
pub mod wf;
@@ -26,6 +27,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt};
use crate::traits::error_reporting::TypeErrCtxtExt as _;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
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};
@@ -49,20 +51,24 @@ pub use self::object_safety::astconv_object_safety_violations;
pub use self::object_safety::is_vtable_safe_method;
pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::ObjectSafetyViolation;
-pub use self::project::{normalize_projection_type, NormalizeExt};
+pub use self::project::NormalizeExt;
+pub use self::project::{normalize_inherent_projection, normalize_projection_type};
pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
pub use self::specialize::specialization_graph::FutureCompatOverlapError;
pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
-pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
+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_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_type,
+ supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item,
SupertraitDefIds,
};
@@ -127,7 +133,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
ty: Ty<'tcx>,
def_id: DefId,
) -> bool {
- let trait_ref = ty::Binder::dummy(infcx.tcx.mk_trait_ref(def_id, [ty]));
+ let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]);
pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref.without_const())
}
@@ -139,35 +145,36 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
fn pred_known_to_hold_modulo_regions<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- pred: impl ToPredicate<'tcx> + TypeVisitable<TyCtxt<'tcx>>,
+ pred: impl ToPredicate<'tcx>,
) -> bool {
- let has_non_region_infer = pred.has_non_region_infer();
let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred);
let result = infcx.evaluate_obligation_no_overflow(&obligation);
debug!(?result);
- if result.must_apply_modulo_regions() && !has_non_region_infer {
+ if result.must_apply_modulo_regions() {
true
} else if result.may_apply() {
- // Because of inference "guessing", selection can sometimes claim
- // to succeed while the success requires a guess. To ensure
- // this function's result remains infallible, we must confirm
- // that guess. While imperfect, I believe this is sound.
-
- // The handling of regions in this area of the code is terrible,
- // see issue #29149. We should be able to improve on this with
- // NLL.
- let ocx = ObligationCtxt::new(infcx);
- ocx.register_obligation(obligation);
- let errors = ocx.select_all_or_error();
- match errors.as_slice() {
- [] => true,
- errors => {
- debug!(?errors);
- false
+ // Sometimes obligations are ambiguous because the recursive evaluator
+ // is not smart enough, so we fall back to fulfillment when we're not certain
+ // that an obligation holds or not. Even still, we must make sure that
+ // 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);
+ ocx.register_obligation(obligation);
+
+ let errors = ocx.select_all_or_error();
+ match errors.as_slice() {
+ // Only known to hold if we did no inference.
+ [] => infcx.shallow_resolve(goal) == goal,
+
+ errors => {
+ debug!(?errors);
+ false
+ }
}
- }
+ })
} else {
false
}
@@ -203,7 +210,7 @@ fn do_normalize_predicates<'tcx>(
}
};
- debug!("do_normalize_predictes: normalized predicates = {:?}", predicates);
+ debug!("do_normalize_predicates: normalized predicates = {:?}", predicates);
// We can use the `elaborated_env` here; the region code only
// cares about declarations like `'a: 'b`.
@@ -414,7 +421,7 @@ fn subst_and_check_impossible_predicates<'tcx>(
predicates.push(ty::Binder::dummy(trait_ref).to_predicate(tcx));
}
- predicates.retain(|predicate| !predicate.needs_subst());
+ predicates.retain(|predicate| !predicate.has_param());
let result = impossible_predicates(tcx, predicates);
debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result);
@@ -450,7 +457,7 @@ fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefI
{
return ControlFlow::Break(());
}
- r.super_visit_with(self)
+ ControlFlow::Continue(())
}
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::ConstKind::Param(param) = ct.kind()
@@ -495,10 +502,10 @@ fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefI
false
}
-pub fn provide(providers: &mut ty::query::Providers) {
+pub fn provide(providers: &mut Providers) {
object_safety::provide(providers);
vtable::provide(providers);
- *providers = ty::query::Providers {
+ *providers = Providers {
specialization_graph_of: specialize::specialization_graph_provider,
specializes: specialize::specializes,
subst_and_check_impossible_predicates,
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index b8ad1925e..c81bf6ebc 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -16,6 +16,7 @@ use crate::traits::{self, Obligation, ObligationCause};
use rustc_errors::{DelayDm, FatalError, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_middle::query::Providers;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
use rustc_middle::ty::{
self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
@@ -279,7 +280,7 @@ fn predicates_reference_self(
trait_def_id: DefId,
supertraits_only: bool,
) -> SmallVec<[Span; 1]> {
- let trait_ref = ty::TraitRef::identity(tcx, trait_def_id);
+ let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id));
let predicates = if supertraits_only {
tcx.super_predicates_of(trait_def_id)
} else {
@@ -297,8 +298,8 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
tcx.associated_items(trait_def_id)
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Type)
- .flat_map(|item| tcx.explicit_item_bounds(item.def_id))
- .filter_map(|pred_span| predicate_references_self(tcx, *pred_span))
+ .flat_map(|item| tcx.explicit_item_bounds(item.def_id).subst_identity_iter_copied())
+ .filter_map(|pred_span| predicate_references_self(tcx, pred_span))
.collect()
}
@@ -525,7 +526,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: {}\n while computing layout for type {:?}", err, ty),
);
None
}
@@ -541,7 +542,7 @@ fn virtual_call_violation_for_method<'tcx>(
abi => {
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
- &format!(
+ format!(
"receiver when `Self = ()` should have a Scalar ABI; found {:?}",
abi
),
@@ -560,7 +561,7 @@ fn virtual_call_violation_for_method<'tcx>(
abi => {
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
- &format!(
+ format!(
"receiver when `Self = {}` should have a ScalarPair ABI; found {:?}",
trait_object_ty, abi
),
@@ -661,9 +662,9 @@ fn object_ty_for_trait<'tcx>(
let trait_ref = ty::TraitRef::identity(tcx, trait_def_id);
debug!(?trait_ref);
- let trait_predicate = trait_ref.map_bound(|trait_ref| {
- ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref))
- });
+ let trait_predicate = ty::Binder::dummy(ty::ExistentialPredicate::Trait(
+ ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref),
+ ));
debug!(?trait_predicate);
let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx);
@@ -769,11 +770,10 @@ fn receiver_is_dispatchable<'tcx>(
let param_env = tcx.param_env(method.def_id);
// Self: Unsize<U>
- let unsize_predicate = ty::Binder::dummy(
- tcx.mk_trait_ref(unsize_did, [tcx.types.self_param, unsized_self_ty]),
- )
- .without_const()
- .to_predicate(tcx);
+ 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>
let trait_predicate = {
@@ -782,7 +782,7 @@ fn receiver_is_dispatchable<'tcx>(
if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
});
- ty::Binder::dummy(tcx.mk_trait_ref(trait_def_id, substs)).to_predicate(tcx)
+ ty::TraitRef::new(tcx, trait_def_id, substs).to_predicate(tcx)
};
let caller_bounds =
@@ -797,9 +797,8 @@ fn receiver_is_dispatchable<'tcx>(
// Receiver: DispatchFromDyn<Receiver[Self => U]>
let obligation = {
- let predicate = ty::Binder::dummy(
- tcx.mk_trait_ref(dispatch_from_dyn_did, [receiver_ty, unsized_receiver_ty]),
- );
+ let predicate =
+ ty::TraitRef::new(tcx, dispatch_from_dyn_did, [receiver_ty, unsized_receiver_ty]);
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
};
@@ -882,7 +881,8 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
// Compute supertraits of current trait lazily.
if self.supertraits.is_none() {
- let trait_ref = ty::TraitRef::identity(self.tcx, self.trait_def_id);
+ let trait_ref =
+ ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id));
self.supertraits = Some(
traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(),
);
@@ -948,7 +948,6 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>(
})
}
-pub fn provide(providers: &mut ty::query::Providers) {
- *providers =
- ty::query::Providers { object_safety_violations, check_is_object_safe, ..*providers };
+pub fn provide(providers: &mut Providers) {
+ *providers = Providers { object_safety_violations, check_is_object_safe, ..*providers };
}
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index cff3d277a..f8d056e32 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -1,9 +1,9 @@
use crate::infer::InferCtxt;
-use crate::traits::query::type_op::{self, TypeOp, TypeOpOutput};
-use crate::traits::query::NoSolution;
use crate::traits::{ObligationCause, ObligationCtxt};
use rustc_data_structures::fx::FxIndexSet;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
+use rustc_infer::infer::InferOk;
+use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints};
use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt};
use rustc_span::def_id::LocalDefId;
@@ -55,26 +55,42 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
) -> Vec<OutlivesBound<'tcx>> {
let ty = self.resolve_vars_if_possible(ty);
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
- assert!(!ty.needs_infer());
- let span = self.tcx.def_span(body_id);
- let result = param_env
- .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
- .fully_perform(self);
- let result = match result {
- Ok(r) => r,
- Err(NoSolution) => {
- self.tcx.sess.delay_span_bug(
- span,
- "implied_outlives_bounds failed to solve all obligations",
- );
- return vec![];
- }
+ // We do not expect existential variables in implied bounds.
+ // We may however encounter unconstrained lifetime variables in invalid
+ // code. See #110161 for context.
+ 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 =
+ self.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values);
+ let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else {
+ return vec![];
+ };
+
+ let mut constraints = QueryRegionConstraints::default();
+ let Ok(InferOk { value, obligations }) = self
+ .instantiate_nll_query_response_and_region_obligations(
+ &ObligationCause::dummy(),
+ param_env,
+ &canonical_var_values,
+ canonical_result,
+ &mut constraints,
+ ) else {
+ return vec![];
};
+ assert_eq!(&obligations, &[]);
- let TypeOpOutput { output, constraints, .. } = result;
+ if !constraints.is_empty() {
+ let span = self.tcx.def_span(body_id);
- if let Some(constraints) = constraints {
debug!(?constraints);
if !constraints.member_constraints.is_empty() {
span_bug!(span, "{:#?}", constraints.member_constraints);
@@ -101,7 +117,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
}
};
- output
+ value
}
fn implied_bounds_tys(
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 826fc63ca..510698971 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -16,6 +16,7 @@ use super::{
};
use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
+use crate::errors::InherentProjectionNormalizationOverflow;
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
use crate::traits::error_reporting::TypeErrCtxtExt as _;
@@ -370,10 +371,14 @@ pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
reveal: Reveal,
) -> bool {
match reveal {
- Reveal::UserFacing => value
- .has_type_flags(ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_CT_PROJECTION),
+ Reveal::UserFacing => value.has_type_flags(
+ ty::TypeFlags::HAS_TY_PROJECTION
+ | ty::TypeFlags::HAS_TY_INHERENT
+ | ty::TypeFlags::HAS_CT_PROJECTION,
+ ),
Reveal::All => value.has_type_flags(
ty::TypeFlags::HAS_TY_PROJECTION
+ | ty::TypeFlags::HAS_TY_INHERENT
| ty::TypeFlags::HAS_TY_OPAQUE
| ty::TypeFlags::HAS_CT_PROJECTION,
),
@@ -616,6 +621,51 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
);
normalized_ty
}
+
+ ty::Inherent if !data.has_escaping_bound_vars() => {
+ // This branch is *mostly* just an optimization: when we don't
+ // have escaping bound vars, we don't need to replace them with
+ // placeholders (see branch below). *Also*, we know that we can
+ // register an obligation to *later* project, since we know
+ // there won't be bound vars there.
+
+ let data = data.fold_with(self);
+
+ // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement`
+ // here like `ty::Projection`?
+ normalize_inherent_projection(
+ self.selcx,
+ self.param_env,
+ data,
+ self.cause.clone(),
+ self.depth,
+ &mut self.obligations,
+ )
+ }
+
+ ty::Inherent => {
+ let infcx = self.selcx.infcx;
+ let (data, mapped_regions, mapped_types, mapped_consts) =
+ BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+ let data = data.fold_with(self);
+ let ty = normalize_inherent_projection(
+ self.selcx,
+ self.param_env,
+ data,
+ self.cause.clone(),
+ self.depth,
+ &mut self.obligations,
+ );
+
+ PlaceholderReplacer::replace_placeholders(
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ &self.universes,
+ ty,
+ )
+ }
}
}
@@ -1204,6 +1254,115 @@ fn normalize_to_error<'a, 'tcx>(
Normalized { value: new_value, obligations: vec![trait_obligation] }
}
+/// Confirm and normalize the given inherent projection.
+#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
+pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
+ selcx: &'a mut SelectionContext<'b, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ alias_ty: ty::AliasTy<'tcx>,
+ cause: ObligationCause<'tcx>,
+ depth: usize,
+ obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> Ty<'tcx> {
+ let tcx = selcx.tcx();
+
+ if !tcx.recursion_limit().value_within_limit(depth) {
+ // Halt compilation because it is important that overflows never be masked.
+ tcx.sess.emit_fatal(InherentProjectionNormalizationOverflow {
+ span: cause.span,
+ ty: alias_ty.to_string(),
+ });
+ }
+
+ let substs = compute_inherent_assoc_ty_substs(
+ selcx,
+ param_env,
+ alias_ty,
+ cause.clone(),
+ depth,
+ obligations,
+ );
+
+ // Register the obligations arising from the impl and from the associated type itself.
+ let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, substs);
+ for (predicate, span) in predicates {
+ let predicate = normalize_with_depth_to(
+ selcx,
+ param_env,
+ cause.clone(),
+ depth + 1,
+ predicate,
+ obligations,
+ );
+
+ let nested_cause = ObligationCause::new(
+ cause.span,
+ cause.body_id,
+ // FIXME(inherent_associated_types): Since we can't pass along the self type to the
+ // cause code, inherent projections will be printed with identity substitutions in
+ // diagnostics which is not ideal.
+ // Consider creating separate cause codes for this specific situation.
+ if span.is_dummy() {
+ super::ItemObligation(alias_ty.def_id)
+ } else {
+ super::BindingObligation(alias_ty.def_id, span)
+ },
+ );
+
+ obligations.push(Obligation::with_depth(
+ tcx,
+ nested_cause,
+ depth + 1,
+ param_env,
+ predicate,
+ ));
+ }
+
+ let ty = tcx.type_of(alias_ty.def_id).subst(tcx, substs);
+
+ let mut ty = selcx.infcx.resolve_vars_if_possible(ty);
+ if ty.has_projections() {
+ ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations);
+ }
+
+ ty
+}
+
+pub fn compute_inherent_assoc_ty_substs<'a, 'b, 'tcx>(
+ selcx: &'a mut SelectionContext<'b, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ alias_ty: ty::AliasTy<'tcx>,
+ cause: ObligationCause<'tcx>,
+ depth: usize,
+ obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> ty::SubstsRef<'tcx> {
+ let tcx = selcx.tcx();
+
+ let impl_def_id = tcx.parent(alias_ty.def_id);
+ let impl_substs = selcx.infcx.fresh_substs_for_item(cause.span, impl_def_id);
+
+ let impl_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs);
+ let impl_ty =
+ normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, impl_ty, obligations);
+
+ // Infer the generic parameters of the impl by unifying the
+ // impl type with the self type of the projection.
+ let self_ty = alias_ty.self_ty();
+ match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) {
+ Ok(mut ok) => obligations.append(&mut ok.obligations),
+ Err(_) => {
+ tcx.sess.delay_span_bug(
+ cause.span,
+ format!(
+ "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not"
+ ),
+ );
+ }
+ }
+
+ alias_ty.rebase_substs_onto_impl(impl_substs, tcx)
+}
+
enum Projected<'tcx> {
Progress(Progress<'tcx>),
NoProgress(ty::Term<'tcx>),
@@ -1272,14 +1431,29 @@ fn project<'cx, 'tcx>(
ProjectionCandidateSet::Single(candidate) => {
Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate)))
}
- ProjectionCandidateSet::None => Ok(Projected::NoProgress(
- // FIXME(associated_const_generics): this may need to change in the future?
- // need to investigate whether or not this is fine.
- selcx
- .tcx()
- .mk_projection(obligation.predicate.def_id, obligation.predicate.substs)
- .into(),
- )),
+ 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(),
+ kind => {
+ bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id))
+ }
+ };
+
+ Ok(Projected::NoProgress(term))
+ }
// Error occurred while trying to processing impls.
ProjectionCandidateSet::Error(e) => Err(ProjectionError::TraitSelectionError(e)),
// Inherent ambiguity that prevents us from even enumerating the
@@ -1304,7 +1478,7 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
let trait_substs =
obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id));
// FIXME(named-returns): Binders
- let trait_predicate = ty::Binder::dummy(tcx.mk_trait_ref(trait_def_id, trait_substs));
+ let trait_predicate = ty::TraitRef::new(tcx, trait_def_id, trait_substs);
let _ = selcx.infcx.commit_if_ok(|_| {
match selcx.select(&obligation.with(tcx, trait_predicate)) {
@@ -1369,7 +1543,10 @@ 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() {
- ty::Alias(_, ref data) => tcx.item_bounds(data.def_id).subst(tcx, data.substs),
+ // Excluding IATs 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)
+ }
ty::Infer(ty::TyVar(_)) => {
// If the self-type is an inference variable, then it MAY wind up
// being a projected type, so induce an ambiguity.
@@ -1667,10 +1844,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
if selcx.infcx.predicate_must_hold_modulo_regions(
&obligation.with(
selcx.tcx(),
- ty::Binder::dummy(
- selcx.tcx().at(obligation.cause.span()).mk_trait_ref(LangItem::Sized, [self_ty]),
- )
- .without_const(),
+ ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty])
+ .without_const(),
),
) =>
{
@@ -1734,7 +1909,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// These traits have no associated types.
selcx.tcx().sess.delay_span_bug(
obligation.cause.span,
- &format!("Cannot project an associated type from `{:?}`", impl_source),
+ format!("Cannot project an associated type from `{:?}`", impl_source),
);
return Err(());
}
@@ -1933,8 +2108,11 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
)
});
if check_is_sized {
- let sized_predicate = ty::Binder::dummy(
- tcx.at(obligation.cause.span()).mk_trait_ref(LangItem::Sized, [self_ty]),
+ let sized_predicate = ty::TraitRef::from_lang_item(
+ tcx,
+ LangItem::Sized,
+ obligation.cause.span(),
+ [self_ty],
)
.without_const();
obligations.push(obligation.with(tcx, sized_predicate));
@@ -2085,7 +2263,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 = infcx.tcx.ty_error_with_message(obligation.cause.span, msg);
Progress { term: err.into(), obligations: vec![] }
}
}
@@ -2131,9 +2309,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let ty = tcx.type_of(assoc_ty.item.def_id);
let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
- let identity_substs =
- crate::traits::InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id);
- let did = ty::WithOptConstParam::unknown(assoc_ty.item.def_id);
+ 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())
} else {
@@ -2277,11 +2454,10 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
obligation.param_env,
cause.clone(),
obligation.recursion_depth + 1,
- tcx.bound_return_position_impl_trait_in_trait_tys(impl_fn_def_id)
- .map_bound(|tys| {
- tys.map_or_else(|guar| tcx.ty_error(guar), |tys| tys[&obligation.predicate.def_id])
- })
- .subst(tcx, impl_fn_substs),
+ tcx.collect_return_position_impl_trait_in_trait_tys(impl_fn_def_id).map_or_else(
+ |guar| tcx.ty_error(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 455b53bfb..4e4172e7f 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -1,6 +1,11 @@
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use crate::traits::query::normalize::QueryNormalizeExt;
+use crate::traits::query::NoSolution;
+use crate::traits::{Normalized, ObligationCause, ObligationCtxt};
-pub use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult};
+use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt};
+use rustc_span::source_map::{Span, DUMMY_SP};
/// This returns true if the type `ty` is "trivial" for
/// dropck-outlives -- that is, if it doesn't require any types to
@@ -71,3 +76,263 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
| ty::Generator(..) => false,
}
}
+
+pub fn compute_dropck_outlives_inner<'tcx>(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ goal: ParamEnvAnd<'tcx, Ty<'tcx>>,
+) -> Result<DropckOutlivesResult<'tcx>, NoSolution> {
+ let tcx = ocx.infcx.tcx;
+ let ParamEnvAnd { param_env, value: for_ty } = goal;
+
+ let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] };
+
+ // A stack of types left to process. Each round, we pop
+ // something from the stack and invoke
+ // `dtorck_constraint_for_ty_inner`. This may produce new types that
+ // have to be pushed on the stack. This continues until we have explored
+ // all the reachable types from the type `for_ty`.
+ //
+ // Example: Imagine that we have the following code:
+ //
+ // ```rust
+ // struct A {
+ // value: B,
+ // children: Vec<A>,
+ // }
+ //
+ // struct B {
+ // value: u32
+ // }
+ //
+ // fn f() {
+ // let a: A = ...;
+ // ..
+ // } // here, `a` is dropped
+ // ```
+ //
+ // at the point where `a` is dropped, we need to figure out
+ // which types inside of `a` contain region data that may be
+ // accessed by any destructors in `a`. We begin by pushing `A`
+ // onto the stack, as that is the type of `a`. We will then
+ // invoke `dtorck_constraint_for_ty_inner` which will expand `A`
+ // into the types of its fields `(B, Vec<A>)`. These will get
+ // pushed onto the stack. Eventually, expanding `Vec<A>` will
+ // lead to us trying to push `A` a second time -- to prevent
+ // infinite recursion, we notice that `A` was already pushed
+ // once and stop.
+ let mut ty_stack = vec![(for_ty, 0)];
+
+ // Set used to detect infinite recursion.
+ let mut ty_set = FxHashSet::default();
+
+ let cause = ObligationCause::dummy();
+ let mut constraints = DropckConstraint::empty();
+ while let Some((ty, depth)) = ty_stack.pop() {
+ debug!(
+ "{} kinds, {} overflows, {} ty_stack",
+ result.kinds.len(),
+ result.overflows.len(),
+ ty_stack.len()
+ );
+ dtorck_constraint_for_ty_inner(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?;
+
+ // "outlives" represent types/regions that may be touched
+ // by a destructor.
+ result.kinds.append(&mut constraints.outlives);
+ result.overflows.append(&mut constraints.overflows);
+
+ // If we have even one overflow, we should stop trying to evaluate further --
+ // chances are, the subsequent overflows for this evaluation won't provide useful
+ // information and will just decrease the speed at which we can emit these errors
+ // (since we'll be printing for just that much longer for the often enormous types
+ // that result here).
+ if !result.overflows.is_empty() {
+ break;
+ }
+
+ // dtorck types are "types that will get dropped but which
+ // do not themselves define a destructor", more or less. We have
+ // to push them onto the stack to be expanded.
+ for ty in constraints.dtorck_types.drain(..) {
+ let Normalized { value: ty, obligations } =
+ ocx.infcx.at(&cause, param_env).query_normalize(ty)?;
+ ocx.register_obligations(obligations);
+
+ debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
+
+ match ty.kind() {
+ // All parameters live for the duration of the
+ // function.
+ ty::Param(..) => {}
+
+ // A projection that we couldn't resolve - it
+ // might have a destructor.
+ ty::Alias(..) => {
+ result.kinds.push(ty.into());
+ }
+
+ _ => {
+ if ty_set.insert(ty) {
+ ty_stack.push((ty, depth + 1));
+ }
+ }
+ }
+ }
+ }
+
+ debug!("dropck_outlives: result = {:#?}", result);
+ Ok(result)
+}
+
+/// Returns a set of constraints that needs to be satisfied in
+/// order for `ty` to be valid for destruction.
+pub fn dtorck_constraint_for_ty_inner<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ span: Span,
+ for_ty: Ty<'tcx>,
+ depth: usize,
+ ty: Ty<'tcx>,
+ constraints: &mut DropckConstraint<'tcx>,
+) -> Result<(), NoSolution> {
+ debug!("dtorck_constraint_for_ty_inner({:?}, {:?}, {:?}, {:?})", span, for_ty, depth, ty);
+
+ if !tcx.recursion_limit().value_within_limit(depth) {
+ constraints.overflows.push(ty);
+ return Ok(());
+ }
+
+ if trivial_dropck_outlives(tcx, ty) {
+ return Ok(());
+ }
+
+ match ty.kind() {
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Str
+ | ty::Never
+ | ty::Foreign(..)
+ | ty::RawPtr(..)
+ | ty::Ref(..)
+ | ty::FnDef(..)
+ | ty::FnPtr(_)
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..) => {
+ // these types never have a destructor
+ }
+
+ ty::Array(ety, _) | ty::Slice(ety) => {
+ // single-element containers, behave like their element
+ rustc_data_structures::stack::ensure_sufficient_stack(|| {
+ dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, *ety, constraints)
+ })?;
+ }
+
+ ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
+ for ty in tys.iter() {
+ dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, ty, constraints)?;
+ }
+ Ok::<_, NoSolution>(())
+ })?,
+
+ ty::Closure(_, substs) => {
+ if !substs.as_closure().is_valid() {
+ // By the time this code runs, all type variables ought to
+ // be fully resolved.
+
+ tcx.sess.delay_span_bug(
+ span,
+ format!("upvar_tys for closure not found. Expected capture information for closure {ty}",),
+ );
+ return Err(NoSolution);
+ }
+
+ rustc_data_structures::stack::ensure_sufficient_stack(|| {
+ for ty in substs.as_closure().upvar_tys() {
+ dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, ty, constraints)?;
+ }
+ Ok::<_, NoSolution>(())
+ })?
+ }
+
+ ty::Generator(_, substs, _movability) => {
+ // rust-lang/rust#49918: types can be constructed, stored
+ // in the interior, and sit idle when generator yields
+ // (and is subsequently dropped).
+ //
+ // It would be nice to descend into interior of a
+ // generator to determine what effects dropping it might
+ // have (by looking at any drop effects associated with
+ // its interior).
+ //
+ // However, the interior's representation uses things like
+ // GeneratorWitness that explicitly assume they are not
+ // traversed in such a manner. So instead, we will
+ // simplify things for now by treating all generators as
+ // if they were like trait objects, where its upvars must
+ // all be alive for the generator's (potential)
+ // destructor.
+ //
+ // In particular, skipping over `_interior` is safe
+ // because any side-effects from dropping `_interior` can
+ // only take place through references with lifetimes
+ // derived from lifetimes attached to the upvars and resume
+ // argument, and we *do* incorporate those here.
+
+ if !substs.as_generator().is_valid() {
+ // By the time this code runs, all type variables ought to
+ // be fully resolved.
+ tcx.sess.delay_span_bug(
+ span,
+ format!("upvar_tys for generator not found. Expected capture information for generator {ty}",),
+ );
+ return Err(NoSolution);
+ }
+
+ constraints.outlives.extend(
+ substs
+ .as_generator()
+ .upvar_tys()
+ .map(|t| -> ty::subst::GenericArg<'tcx> { t.into() }),
+ );
+ constraints.outlives.push(substs.as_generator().resume_ty().into());
+ }
+
+ ty::Adt(def, substs) => {
+ let DropckConstraint { dtorck_types, outlives, overflows } =
+ tcx.at(span).adt_dtorck_constraint(def.did())?;
+ // FIXME: we can try to recursively `dtorck_constraint_on_ty`
+ // there, but that needs some way to handle cycles.
+ constraints
+ .dtorck_types
+ .extend(dtorck_types.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ constraints
+ .outlives
+ .extend(outlives.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ constraints
+ .overflows
+ .extend(overflows.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ }
+
+ // Objects must be alive in order for their destructor
+ // to be called.
+ ty::Dynamic(..) => {
+ constraints.outlives.push(ty.into());
+ }
+
+ // Types that can't be resolved. Pass them forward.
+ ty::Alias(..) | ty::Param(..) => {
+ constraints.dtorck_types.push(ty);
+ }
+
+ ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => {
+ // By the time this code runs, all type variables ought to
+ // be fully resolved.
+ return Err(NoSolution);
+ }
+ }
+
+ Ok(())
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index a986a9b6a..8bf934cb7 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -257,11 +257,11 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
ty::Opaque => ty.try_super_fold_with(self)?,
- ty::Projection => {
+ ty::Projection | ty::Inherent => {
// See note in `rustc_trait_selection::traits::project`
- let tcx = self.infcx.tcx;
let infcx = self.infcx;
+ let tcx = infcx.tcx;
// Just an optimization: When we don't have escaping bound vars,
// we don't need to replace them with placeholders.
let (data, maps) = if data.has_escaping_bound_vars() {
@@ -276,12 +276,15 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
let mut orig_values = OriginalQueryValues::default();
// HACK(matthewjasper) `'static` is special-cased in selection,
// so we cannot canonicalize it.
- let c_data = self
- .infcx
+ let c_data = infcx
.canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
debug!("QueryNormalizer: c_data = {:#?}", c_data);
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
- let result = tcx.normalize_projection_ty(c_data)?;
+ let result = match kind {
+ ty::Projection => tcx.normalize_projection_ty(c_data),
+ ty::Inherent => tcx.normalize_inherent_projection_ty(c_data),
+ _ => unreachable!(),
+ }?;
// We don't expect ambiguity.
if result.is_ambiguous() {
// Rustdoc normalizes possibly not well-formed types, so only
@@ -294,8 +297,8 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
}
return Err(NoSolution);
}
- let InferOk { value: result, obligations } =
- self.infcx.instantiate_query_response_and_region_obligations(
+ let InferOk { value: result, obligations } = infcx
+ .instantiate_query_response_and_region_obligations(
self.cause,
self.param_env,
&orig_values,
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 e6db96c9e..01d7a1e79 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
@@ -1,8 +1,13 @@
use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::query::Fallible;
-use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
+use crate::traits::ObligationCtxt;
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
+use rustc_infer::traits::Obligation;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
+use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserSelfTy, UserSubsts, UserType};
pub use rustc_middle::traits::query::type_op::AscribeUserType;
+use rustc_span::{Span, DUMMY_SP};
impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> {
type QueryResponse = ();
@@ -17,7 +22,119 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> {
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, ()>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
tcx.type_op_ascribe_user_type(canonicalized)
}
+
+ fn perform_locally_in_new_solver(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ key: ParamEnvAnd<'tcx, Self>,
+ ) -> Result<Self::QueryResponse, NoSolution> {
+ type_op_ascribe_user_type_with_span(ocx, key, None)
+ }
+}
+
+/// The core of the `type_op_ascribe_user_type` query: for diagnostics purposes in NLL HRTB errors,
+/// this query can be re-run to better track the span of the obligation cause, and improve the error
+/// message. Do not call directly unless you're in that very specific context.
+pub fn type_op_ascribe_user_type_with_span<'tcx>(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>,
+ span: Option<Span>,
+) -> Result<(), NoSolution> {
+ let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts();
+ debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty);
+ let span = span.unwrap_or(DUMMY_SP);
+ match user_ty {
+ UserType::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?,
+ UserType::TypeOf(def_id, user_substs) => {
+ relate_mir_and_user_substs(ocx, param_env, span, mir_ty, def_id, user_substs)?
+ }
+ };
+ Ok(())
+}
+
+#[instrument(level = "debug", skip(ocx, param_env, span))]
+fn relate_mir_and_user_ty<'tcx>(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ span: Span,
+ mir_ty: Ty<'tcx>,
+ user_ty: Ty<'tcx>,
+) -> Result<(), NoSolution> {
+ let cause = ObligationCause::dummy_with_span(span);
+ let user_ty = ocx.normalize(&cause, param_env, user_ty);
+ 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()));
+ ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
+ Ok(())
+}
+
+#[instrument(level = "debug", skip(ocx, param_env, span))]
+fn relate_mir_and_user_substs<'tcx>(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ span: Span,
+ mir_ty: Ty<'tcx>,
+ def_id: DefId,
+ user_substs: UserSubsts<'tcx>,
+) -> Result<(), NoSolution> {
+ let param_env = param_env.without_const();
+ let UserSubsts { user_self_ty, substs } = user_substs;
+ let tcx = ocx.infcx.tcx;
+ let cause = ObligationCause::dummy_with_span(span);
+
+ let ty = tcx.type_of(def_id).subst(tcx, substs);
+ let ty = ocx.normalize(&cause, param_env, ty);
+ debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
+
+ ocx.eq(&cause, param_env, mir_ty, ty)?;
+
+ // Prove the predicates coming along with `def_id`.
+ //
+ // Also, normalize the `instantiated_predicates`
+ // because otherwise we wind up with duplicate "type
+ // outlives" error messages.
+ let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
+
+ debug!(?instantiated_predicates);
+ for (instantiated_predicate, predicate_span) in instantiated_predicates {
+ let span = if span == DUMMY_SP { predicate_span } else { span };
+ let cause = ObligationCause::new(
+ span,
+ CRATE_DEF_ID,
+ ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span),
+ );
+ let instantiated_predicate =
+ ocx.normalize(&cause.clone(), param_env, instantiated_predicate);
+
+ ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate));
+ }
+
+ if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
+ let self_ty = ocx.normalize(&cause, param_env, self_ty);
+ let impl_self_ty = tcx.type_of(impl_def_id).subst(tcx, substs);
+ 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()));
+ ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
+ }
+
+ // In addition to proving the predicates, we have to
+ // prove that `ty` is well-formed -- this is because
+ // the WF of `ty` is predicated on the substs being
+ // well-formed, and we haven't proven *that*. We don't
+ // want to prove the WF of types from `substs` directly because they
+ // haven't been normalized.
+ //
+ // FIXME(nmatsakis): Well, perhaps we should normalize
+ // 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()));
+ 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 8f1b05c11..6d8d2103f 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
@@ -1,32 +1,32 @@
use crate::infer::canonical::query_response;
-use crate::infer::{InferCtxt, InferOk};
+use crate::infer::InferCtxt;
use crate::traits::query::type_op::TypeOpOutput;
-use crate::traits::query::Fallible;
use crate::traits::ObligationCtxt;
+use rustc_errors::ErrorGuaranteed;
use rustc_infer::infer::region_constraints::RegionConstraintData;
+use rustc_middle::traits::query::NoSolution;
use rustc_span::source_map::DUMMY_SP;
+use rustc_span::Span;
use std::fmt;
-pub struct CustomTypeOp<F, G> {
+pub struct CustomTypeOp<F> {
closure: F,
- description: G,
+ description: &'static str,
}
-impl<F, G> CustomTypeOp<F, G> {
- pub fn new<'tcx, R>(closure: F, description: G) -> Self
+impl<F> CustomTypeOp<F> {
+ pub fn new<'tcx, R>(closure: F, description: &'static str) -> Self
where
- F: FnOnce(&InferCtxt<'tcx>) -> Fallible<InferOk<'tcx, R>>,
- G: Fn() -> String,
+ F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
{
CustomTypeOp { closure, description }
}
}
-impl<'tcx, F, R: fmt::Debug, G> super::TypeOp<'tcx> for CustomTypeOp<F, G>
+impl<'tcx, F, R: fmt::Debug> super::TypeOp<'tcx> for CustomTypeOp<F>
where
- F: for<'a, 'cx> FnOnce(&'a InferCtxt<'tcx>) -> Fallible<InferOk<'tcx, R>>,
- G: Fn() -> String,
+ F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
{
type Output = R;
/// We can't do any custom error reporting for `CustomTypeOp`, so
@@ -36,21 +36,22 @@ where
/// Processes the operation and all resulting obligations,
/// returning the final result along with any region constraints
/// (they will be given over to the NLL region solver).
- fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
+ fn fully_perform(
+ self,
+ infcx: &InferCtxt<'tcx>,
+ span: Span,
+ ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
if cfg!(debug_assertions) {
info!("fully_perform({:?})", self);
}
- Ok(scrape_region_constraints(infcx, || (self.closure)(infcx))?.0)
+ Ok(scrape_region_constraints(infcx, self.closure, self.description, span)?.0)
}
}
-impl<F, G> fmt::Debug for CustomTypeOp<F, G>
-where
- G: Fn() -> String,
-{
+impl<F> fmt::Debug for CustomTypeOp<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", (self.description)())
+ self.description.fmt(f)
}
}
@@ -58,8 +59,10 @@ where
/// constraints that result, creating query-region-constraints.
pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
infcx: &InferCtxt<'tcx>,
- op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>,
-) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> {
+ op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
+ name: &'static str,
+ span: Span,
+) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> {
// 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
@@ -72,16 +75,21 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
pre_obligations,
);
- let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?;
- let ocx = ObligationCtxt::new(infcx);
- ocx.register_obligations(obligations);
- let errors = ocx.select_all_or_error();
- if !errors.is_empty() {
- infcx.tcx.sess.diagnostic().delay_span_bug(
- DUMMY_SP,
- &format!("errors selecting obligation during MIR typeck: {:?}", errors),
- );
- }
+ let value = infcx.commit_if_ok(|_| {
+ let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let value = op(&ocx).map_err(|_| {
+ infcx.tcx.sess.delay_span_bug(span, format!("error performing operation: {name}"))
+ })?;
+ let errors = ocx.select_all_or_error();
+ if errors.is_empty() {
+ Ok(value)
+ } else {
+ Err(infcx.tcx.sess.delay_span_bug(
+ DUMMY_SP,
+ format!("errors selecting obligation during MIR typeck: {:?}", errors),
+ ))
+ }
+ })?;
let region_obligations = infcx.take_registered_region_obligations();
let region_constraint_data = infcx.take_and_reset_region_constraints();
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs
index 8c9b9610c..f65893088 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs
@@ -1,5 +1,7 @@
use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::query::Fallible;
+use crate::traits::ObligationCtxt;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
pub use rustc_middle::traits::query::type_op::Eq;
@@ -17,7 +19,15 @@ impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> {
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, ()>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
tcx.type_op_eq(canonicalized)
}
+
+ fn perform_locally_in_new_solver(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ key: ParamEnvAnd<'tcx, Self>,
+ ) -> Result<Self::QueryResponse, NoSolution> {
+ ocx.eq(&ObligationCause::dummy(), key.param_env, key.value.a, key.value.b)?;
+ Ok(())
+ }
}
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 18d7c9b19..9989fc9c4 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,7 +1,15 @@
-use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::query::Fallible;
+use crate::traits::query::NoSolution;
+use crate::traits::wf;
+use crate::traits::ObligationCtxt;
+
+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::ty::{self, ParamEnvAnd, Ty, TyCtxt};
+use rustc_middle::infer::canonical::CanonicalQueryResponse;
+use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
+use rustc_span::def_id::CRATE_DEF_ID;
+use rustc_span::source_map::DUMMY_SP;
+use smallvec::{smallvec, SmallVec};
#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)]
pub struct ImpliedOutlivesBounds<'tcx> {
@@ -28,7 +36,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, Self::QueryResponse>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
// FIXME this `unchecked_map` is only necessary because the
// query is defined as taking a `ParamEnvAnd<Ty>`; it should
// take an `ImpliedOutlivesBounds` instead
@@ -39,4 +47,169 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
tcx.implied_outlives_bounds(canonicalized)
}
+
+ fn perform_locally_in_new_solver(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ key: ParamEnvAnd<'tcx, Self>,
+ ) -> Result<Self::QueryResponse, NoSolution> {
+ compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty)
+ }
+}
+
+pub fn compute_implied_outlives_bounds_inner<'tcx>(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
+ let tcx = ocx.infcx.tcx;
+
+ // Sometimes when we ask what it takes for T: WF, we get back that
+ // U: WF is required; in that case, we push U onto this stack and
+ // process it next. Because the resulting predicates aren't always
+ // guaranteed to be a subset of the original type, so we need to store the
+ // WF args we've computed in a set.
+ let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
+ let mut wf_args = vec![ty.into()];
+
+ let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> =
+ vec![];
+
+ while let Some(arg) = wf_args.pop() {
+ if !checked_wf_args.insert(arg) {
+ continue;
+ }
+
+ // Compute the obligations for `arg` to be well-formed. If `arg` is
+ // an unresolved inference variable, just substituted an empty set
+ // -- because the return type here is going to be things we *add*
+ // to the environment, it's always ok for this set to be smaller
+ // than the ultimate set. (Note: normally there won't be
+ // unresolved inference variables here anyway, but there might be
+ // during typeck under some circumstances.)
+ //
+ // FIXME(@lcnr): It's not really "always fine", having fewer implied
+ // bounds can be backward incompatible, e.g. #101951 was caused by
+ // us not dealing with inference vars in `TypeOutlives` predicates.
+ let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP)
+ .unwrap_or_default();
+
+ for obligation in obligations {
+ debug!(?obligation);
+ assert!(!obligation.has_escaping_bound_vars());
+
+ // While these predicates should all be implied by other parts of
+ // the program, they are still relevant as they may constrain
+ // inference variables, which is necessary to add the correct
+ // implied bounds in some cases, mostly when dealing with projections.
+ //
+ // Another important point here: we only register `Projection`
+ // predicates, since otherwise we might register outlives
+ // predicates containing inference variables, and we don't
+ // 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::AliasRelate(..) => {
+ ocx.register_obligation(obligation.clone());
+ }
+ _ => {}
+ }
+ }
+
+ let pred = match obligation.predicate.kind().no_bound_vars() {
+ None => continue,
+ Some(pred) => pred,
+ };
+ match pred {
+ ty::PredicateKind::Clause(ty::Clause::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::Subtype(..)
+ | ty::PredicateKind::Coerce(..)
+ | ty::PredicateKind::Clause(ty::Clause::Projection(..))
+ | ty::PredicateKind::ClosureKind(..)
+ | ty::PredicateKind::ObjectSafe(..)
+ | ty::PredicateKind::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) => {
+ wf_args.push(arg);
+ }
+
+ // We need to register region relationships
+ ty::PredicateKind::Clause(ty::Clause::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_a,
+ r_b,
+ ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)),
+ }
+ }
+ }
+
+ // This call to `select_all_or_error` is necessary to constrain inference variables, which we
+ // use further down when computing the implied bounds.
+ match ocx.select_all_or_error().as_slice() {
+ [] => (),
+ _ => return Err(NoSolution),
+ }
+
+ // 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)],
+ ty::GenericArgKind::Type(ty_a) => {
+ let ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
+ let mut components = smallvec![];
+ push_outlives_components(tcx, ty_a, &mut components);
+ implied_bounds_from_components(r_b, components)
+ }
+ ty::GenericArgKind::Const(_) => unreachable!(),
+ })
+ .collect();
+
+ Ok(implied_bounds)
+}
+
+/// When we have an implied bound that `T: 'a`, we can further break
+/// this down to determine what relationships would have to hold for
+/// `T: 'a` to hold. We get to assume that the caller has validated
+/// those relationships.
+fn implied_bounds_from_components<'tcx>(
+ sub_region: ty::Region<'tcx>,
+ sup_components: SmallVec<[Component<'tcx>; 4]>,
+) -> Vec<OutlivesBound<'tcx>> {
+ sup_components
+ .into_iter()
+ .filter_map(|component| {
+ match component {
+ Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
+ Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
+ Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
+ Component::EscapingAlias(_) =>
+ // If the projection has escaping regions, don't
+ // try to infer any implied bounds even for its
+ // free components. This is conservative, because
+ // the caller will still have to prove that those
+ // free components outlive `sub_region`. But the
+ // idea is that the WAY that the caller proves
+ // that may change in the future and we want to
+ // give ourselves room to get smarter here.
+ {
+ None
+ }
+ Component::UnresolvedInferenceVariable(..) => None,
+ }
+ })
+ .collect()
}
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 9e8bc8bce..642fdec2d 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
@@ -2,13 +2,14 @@ use crate::infer::canonical::{
Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints,
};
use crate::infer::{InferCtxt, InferOk};
-use crate::traits::query::Fallible;
-use crate::traits::ObligationCause;
+use crate::traits::{ObligationCause, ObligationCtxt};
+use rustc_errors::ErrorGuaranteed;
use rustc_infer::infer::canonical::Certainty;
-use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::PredicateObligations;
+use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
+use rustc_span::Span;
use std::fmt;
pub mod ascribe_user_type;
@@ -22,6 +23,8 @@ pub mod subtype;
pub use rustc_middle::traits::query::type_op::*;
+use self::custom::scrape_region_constraints;
+
/// "Type ops" are used in NLL to perform some particular action and
/// extract out the resulting region constraints (or an error if it
/// cannot be completed).
@@ -32,7 +35,11 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug {
/// Processes the operation and all resulting obligations,
/// returning the final result along with any region constraints
/// (they will be given over to the NLL region solver).
- fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>>;
+ fn fully_perform(
+ self,
+ infcx: &InferCtxt<'tcx>,
+ span: Span,
+ ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>;
}
/// The output from performing a type op
@@ -74,18 +81,32 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 't
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, Self::QueryResponse>>;
+ ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution>;
+
+ /// In the new trait solver, we already do caching in the solver itself,
+ /// so there's no need to canonicalize and cache via the query system.
+ /// Additionally, even if we were to canonicalize, we'd still need to
+ /// make sure to feed it predefined opaque types and the defining anchor
+ /// and that would require duplicating all of the tcx queries. Instead,
+ /// just perform these ops locally.
+ fn perform_locally_in_new_solver(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ key: ParamEnvAnd<'tcx, Self>,
+ ) -> Result<Self::QueryResponse, NoSolution>;
fn fully_perform_into(
query_key: ParamEnvAnd<'tcx, Self>,
infcx: &InferCtxt<'tcx>,
output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
- ) -> Fallible<(
- Self::QueryResponse,
- Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>,
- PredicateObligations<'tcx>,
- Certainty,
- )> {
+ ) -> Result<
+ (
+ Self::QueryResponse,
+ Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>,
+ PredicateObligations<'tcx>,
+ Certainty,
+ ),
+ NoSolution,
+ > {
if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) {
return Ok((result, None, vec![], Certainty::Proven));
}
@@ -120,10 +141,26 @@ where
type Output = Q::QueryResponse;
type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>;
- fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
+ fn fully_perform(
+ self,
+ infcx: &InferCtxt<'tcx>,
+ span: Span,
+ ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
+ if infcx.tcx.trait_solver_next() {
+ return Ok(scrape_region_constraints(
+ infcx,
+ |ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self),
+ "query type op",
+ span,
+ )?
+ .0);
+ }
+
let mut region_constraints = QueryRegionConstraints::default();
let (output, error_info, mut obligations, _) =
- Q::fully_perform_into(self, infcx, &mut region_constraints)?;
+ Q::fully_perform_into(self, infcx, &mut region_constraints).map_err(|_| {
+ infcx.tcx.sess.delay_span_bug(span, format!("error performing {self:?}"))
+ })?;
// Typically, instantiating NLL query results does not
// create obligations. However, in some cases there
@@ -151,7 +188,10 @@ where
}
}
if !progress {
- return Err(NoSolution);
+ return Err(infcx.tcx.sess.delay_span_bug(
+ span,
+ format!("ambiguity processing {obligations:?} from {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 5b216c076..57ca14aa4 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
@@ -1,5 +1,7 @@
use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::query::Fallible;
+use crate::traits::ObligationCtxt;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
use std::fmt;
@@ -19,23 +21,31 @@ where
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, Self::QueryResponse>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
T::type_op_method(tcx, canonicalized)
}
+
+ fn perform_locally_in_new_solver(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ key: ParamEnvAnd<'tcx, Self>,
+ ) -> Result<Self::QueryResponse, NoSolution> {
+ // FIXME(-Ztrait-solver=next): shouldn't be using old normalizer
+ Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value))
+ }
}
pub trait Normalizable<'tcx>: fmt::Debug + TypeFoldable<TyCtxt<'tcx>> + Lift<'tcx> + Copy {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, Self>>;
+ ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution>;
}
impl<'tcx> Normalizable<'tcx> for Ty<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, Self>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
tcx.type_op_normalize_ty(canonicalized)
}
}
@@ -44,7 +54,7 @@ impl<'tcx> Normalizable<'tcx> for ty::Predicate<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, Self>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
tcx.type_op_normalize_predicate(canonicalized)
}
}
@@ -53,7 +63,7 @@ impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, Self>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
tcx.type_op_normalize_poly_fn_sig(canonicalized)
}
}
@@ -62,7 +72,7 @@ impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, Self>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
tcx.type_op_normalize_fn_sig(canonicalized)
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
index 21ef4e24f..988942633 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
@@ -1,6 +1,9 @@
use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::query::dropck_outlives::{trivial_dropck_outlives, DropckOutlivesResult};
-use crate::traits::query::Fallible;
+use crate::traits::query::dropck_outlives::{
+ compute_dropck_outlives_inner, trivial_dropck_outlives,
+};
+use crate::traits::ObligationCtxt;
+use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution};
use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)]
@@ -27,7 +30,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> {
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, Self::QueryResponse>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
// Subtle: note that we are not invoking
// `infcx.at(...).dropck_outlives(...)` here, but rather the
// underlying `dropck_outlives` query. This same underlying
@@ -48,4 +51,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> {
tcx.dropck_outlives(canonicalized)
}
+
+ fn perform_locally_in_new_solver(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ key: ParamEnvAnd<'tcx, Self>,
+ ) -> Result<Self::QueryResponse, NoSolution> {
+ compute_dropck_outlives_inner(ocx, key.param_env.and(key.value.dropped_ty))
+ }
}
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 b63da28e2..47850bc33 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
@@ -1,5 +1,8 @@
use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::query::Fallible;
+use crate::traits::ObligationCtxt;
+use rustc_infer::traits::Obligation;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt};
pub use rustc_middle::traits::query::type_op::ProvePredicate;
@@ -32,14 +35,21 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
fn perform_query(
tcx: TyCtxt<'tcx>,
- mut canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, ()>> {
- match canonicalized.value.value.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
- canonicalized.value.param_env.remap_constness_with(pred.constness);
- }
- _ => canonicalized.value.param_env = canonicalized.value.param_env.without_const(),
- }
+ canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
+ ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
tcx.type_op_prove_predicate(canonicalized)
}
+
+ fn perform_locally_in_new_solver(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ key: ParamEnvAnd<'tcx, Self>,
+ ) -> Result<Self::QueryResponse, NoSolution> {
+ ocx.register_obligation(Obligation::new(
+ ocx.infcx.tcx,
+ ObligationCause::dummy(),
+ key.param_env,
+ key.value.predicate,
+ ));
+ Ok(())
+ }
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs
index c51292eba..10976d5cd 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs
@@ -1,5 +1,7 @@
use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::query::Fallible;
+use crate::traits::ObligationCtxt;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
pub use rustc_middle::traits::query::type_op::Subtype;
@@ -14,7 +16,15 @@ impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> {
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
- ) -> Fallible<CanonicalQueryResponse<'tcx, ()>> {
+ ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
tcx.type_op_subtype(canonicalized)
}
+
+ fn perform_locally_in_new_solver(
+ ocx: &ObligationCtxt<'_, 'tcx>,
+ key: ParamEnvAnd<'tcx, Self>,
+ ) -> Result<Self::QueryResponse, NoSolution> {
+ ocx.sub(&ObligationCause::dummy(), key.param_env, key.value.sub, key.value.sup)?;
+ Ok(())
+ }
}
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 1f5bbc178..8bc82b9f5 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -11,7 +11,7 @@ use hir::LangItem;
use rustc_hir as hir;
use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
-use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
use crate::traits;
@@ -57,6 +57,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if obligation.polarity() == ty::ImplPolarity::Negative {
self.assemble_candidates_for_trait_alias(obligation, &mut candidates);
self.assemble_candidates_from_impls(obligation, &mut candidates);
+ self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?;
} else {
self.assemble_candidates_for_trait_alias(obligation, &mut candidates);
@@ -142,7 +143,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, '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() {
- ty::Alias(..) => {}
+ // Excluding IATs here as they don't have meaningful item bounds.
+ ty::Alias(ty::Projection | ty::Opaque, _) => {}
ty::Infer(ty::TyVar(_)) => {
span_bug!(
obligation.cause.span,
@@ -187,6 +189,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Keep only those bounds which may apply, and propagate overflow if it occurs.
for bound in matching_bounds {
+ if bound.skip_binder().polarity != stack.obligation.predicate.skip_binder().polarity {
+ continue;
+ }
+
// FIXME(oli-obk): it is suspicious that we are dropping the constness and
// polarity here.
let wc = self.where_clause_may_apply(stack, bound.map_bound(|t| t.trait_ref))?;
@@ -454,7 +460,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.param_env,
self.tcx().mk_predicate(obligation.predicate.map_bound(|mut pred| {
pred.trait_ref =
- self.tcx().mk_trait_ref(fn_ptr_trait, [pred.trait_ref.self_ty()]);
+ ty::TraitRef::new(self.tcx(), fn_ptr_trait, [pred.trait_ref.self_ty()]);
ty::PredicateKind::Clause(ty::Clause::Trait(pred))
})),
);
@@ -493,7 +499,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// this trait and type.
}
ty::Param(..)
- | ty::Alias(ty::Projection, ..)
+ | ty::Alias(ty::Projection | ty::Inherent, ..)
| ty::Placeholder(..)
| ty::Bound(..) => {
// In these cases, we don't know what the actual
@@ -629,7 +635,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
// <ty as Deref>
- let trait_ref = tcx.mk_trait_ref(tcx.lang_items().deref_trait()?, [ty]);
+ let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]);
let obligation =
traits::Obligation::new(tcx, cause.clone(), param_env, ty::Binder::dummy(trait_ref));
@@ -775,7 +781,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
- if obligation.has_non_region_param() {
+ if obligation.predicate.has_non_region_param() {
return;
}
@@ -875,12 +881,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
ty::Adt(..) => {
- // Find a custom `impl Drop` impl, if it exists
- let relevant_impl = self.tcx().find_map_relevant_impl(
+ let mut relevant_impl = None;
+ self.tcx().for_each_relevant_impl(
self.tcx().require_lang_item(LangItem::Drop, None),
obligation.predicate.skip_binder().trait_ref.self_ty(),
- TreatProjections::ForLookup,
- Some,
+ |impl_def_id| {
+ if let Some(old_impl_def_id) = relevant_impl {
+ self.tcx()
+ .sess
+ .struct_span_err(
+ self.tcx().def_span(impl_def_id),
+ "multiple drop impls found",
+ )
+ .span_note(self.tcx().def_span(old_impl_def_id), "other impl here")
+ .delay_as_bug();
+ }
+
+ relevant_impl = Some(impl_def_id);
+ },
);
if let Some(impl_def_id) = relevant_impl {
@@ -949,16 +967,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) {
// The regions of a type don't affect the size of the type
let tcx = self.tcx();
- let self_ty =
- tcx.erase_regions(tcx.erase_late_bound_regions(obligation.predicate.self_ty()));
-
+ let self_ty = tcx.erase_late_bound_regions(obligation.predicate.self_ty());
+ // We should erase regions from both the param-env and type, since both
+ // may have infer regions. Specifically, after canonicalizing and instantiating,
+ // early bound regions turn into region vars in both the new and old solver.
+ let key = tcx.erase_regions(obligation.param_env.and(self_ty));
// But if there are inference variables, we have to wait until it's resolved.
- if self_ty.has_non_region_infer() {
+ if key.has_non_region_infer() {
candidates.ambiguous = true;
return;
}
- if let Ok(layout) = tcx.layout_of(obligation.param_env.and(self_ty))
+ if let Ok(layout) = tcx.layout_of(key)
&& layout.layout.is_pointer_like(&tcx.data_layout)
{
candidates.vec.push(BuiltinCandidate { has_nested: false });
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 88121f865..0d9f55d4c 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -10,6 +10,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
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,
@@ -28,9 +29,9 @@ use crate::traits::{
ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
ImplSourceConstDestructData, ImplSourceFnPointerData, ImplSourceFutureData,
ImplSourceGeneratorData, ImplSourceObjectData, ImplSourceTraitAliasData,
- ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, ObjectCastObligation,
- Obligation, ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection,
- SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented,
+ ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, Obligation,
+ ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection, SelectionError,
+ TraitNotObjectSafe, TraitObligation, Unimplemented,
};
use super::BuiltinImplConditions;
@@ -161,7 +162,10 @@ 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() {
- ty::Alias(_, ty::AliasTy { def_id, substs, .. }) => (def_id, substs),
+ // Excluding IATs here as they don't have meaningful item bounds.
+ ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ (def_id, substs)
+ }
_ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty),
};
@@ -281,33 +285,35 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
debug!(?obligation, "confirm_transmutability_candidate");
- let predicate = obligation.predicate;
-
- let type_at = |i| predicate.map_bound(|p| p.trait_ref.substs.type_at(i));
- let const_at = |i| predicate.skip_binder().trait_ref.substs.const_at(i);
-
- let src_and_dst = predicate.map_bound(|p| rustc_transmute::Types {
- dst: p.trait_ref.substs.type_at(0),
- src: p.trait_ref.substs.type_at(1),
- });
-
- let scope = type_at(2).skip_binder();
-
- let Some(assume) =
- rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, const_at(3)) else {
- return Err(Unimplemented);
- };
+ // We erase regions here because transmutability calls layout queries,
+ // which does not handle inference regions and doesn't particularly
+ // care about other regions. Erasing late-bound regions is equivalent
+ // to instantiating the binder with placeholders then erasing those
+ // placeholder regions.
+ let predicate =
+ self.tcx().erase_regions(self.tcx().erase_late_bound_regions(obligation.predicate));
- let cause = obligation.cause.clone();
+ let Some(assume) = rustc_transmute::Assume::from_const(
+ self.infcx.tcx,
+ obligation.param_env,
+ predicate.trait_ref.substs.const_at(3)
+ ) else {
+ return Err(Unimplemented);
+ };
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
-
- let maybe_transmutable = transmute_env.is_transmutable(cause, src_and_dst, scope, assume);
-
- use rustc_transmute::Answer;
+ 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),
+ },
+ predicate.trait_ref.substs.type_at(2),
+ assume,
+ );
match maybe_transmutable {
- Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
+ rustc_transmute::Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
_ => Err(Unimplemented),
}
}
@@ -650,8 +656,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
output_ty,
&mut nested,
);
- let tr =
- ty::Binder::dummy(self.tcx().at(cause.span).mk_trait_ref(LangItem::Sized, [output_ty]));
+ 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 })
@@ -816,7 +821,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_poly_trait_refs(
&mut self,
obligation: &TraitObligation<'tcx>,
- expected_trait_ref: ty::PolyTraitRef<'tcx>,
+ self_ty_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let obligation_trait_ref = obligation.predicate.to_poly_trait_ref();
// Normalize the obligation and expected trait refs together, because why not
@@ -827,7 +832,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
- (obligation_trait_ref, expected_trait_ref),
+ (obligation_trait_ref, self_ty_trait_ref),
)
});
@@ -839,7 +844,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligations.extend(nested);
obligations
})
- .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e))
+ .map_err(|terr| {
+ OutputTypeParameterMismatch(Box::new(SelectionOutputTypeParameterMismatch {
+ expected_trait_ref: obligation_trait_ref,
+ found_trait_ref: expected_trait_ref,
+ terr,
+ }))
+ })
}
fn confirm_trait_upcasting_unsize_candidate(
@@ -903,16 +914,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.map_err(|_| Unimplemented)?;
nested.extend(obligations);
- // Register one obligation for 'a: 'b.
- let cause = ObligationCause::new(
- obligation.cause.span,
- obligation.cause.body_id,
- ObjectCastObligation(source, target),
- );
let outlives = ty::OutlivesPredicate(r_a, r_b);
nested.push(Obligation::with_depth(
tcx,
- cause,
+ obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
obligation.predicate.rebind(outlives),
@@ -1003,15 +1008,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
nested.extend(obligations);
// Register one obligation for 'a: 'b.
- let cause = ObligationCause::new(
- obligation.cause.span,
- obligation.cause.body_id,
- ObjectCastObligation(source, target),
- );
let outlives = ty::OutlivesPredicate(r_a, r_b);
nested.push(Obligation::with_depth(
tcx,
- cause,
+ obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
obligation.predicate.rebind(outlives),
@@ -1025,16 +1025,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Err(TraitNotObjectSafe(did));
}
- let cause = ObligationCause::new(
- obligation.cause.span,
- obligation.cause.body_id,
- ObjectCastObligation(source, target),
- );
-
let predicate_to_obligation = |predicate| {
Obligation::with_depth(
tcx,
- cause.clone(),
+ obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
predicate,
@@ -1054,8 +1048,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
// We can only make objects from sized types.
- let tr =
- ty::Binder::dummy(tcx.at(cause.span).mk_trait_ref(LangItem::Sized, [source]));
+ let tr = ty::TraitRef::from_lang_item(
+ tcx,
+ LangItem::Sized,
+ obligation.cause.span,
+ [source],
+ );
nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx)));
// If the type is `Foo + 'a`, ensure that the type
@@ -1125,7 +1123,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Construct the nested `TailField<T>: Unsize<TailField<U>>` predicate.
let tail_unsize_obligation = obligation.with(
tcx,
- tcx.mk_trait_ref(obligation.predicate.def_id(), [source_tail, target_tail]),
+ ty::TraitRef::new(
+ tcx,
+ obligation.predicate.def_id(),
+ [source_tail, target_tail],
+ ),
);
nested.push(tail_unsize_obligation);
}
@@ -1150,8 +1152,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
nested.extend(obligations);
// Add a nested `T: Unsize<U>` predicate.
- let last_unsize_obligation = obligation
- .with(tcx, tcx.mk_trait_ref(obligation.predicate.def_id(), [a_last, b_last]));
+ let last_unsize_obligation = obligation.with(
+ tcx,
+ ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]),
+ );
nested.push(last_unsize_obligation);
}
@@ -1268,17 +1272,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// If we have a projection type, make sure to normalize it so we replace it
// with a fresh infer variable
- ty::Alias(ty::Projection, ..) => {
+ ty::Alias(ty::Projection | ty::Inherent, ..) => {
let predicate = normalize_with_depth_to(
self,
obligation.param_env,
cause.clone(),
obligation.recursion_depth + 1,
self_ty.rebind(ty::TraitPredicate {
- trait_ref: self
- .tcx()
- .at(cause.span)
- .mk_trait_ref(LangItem::Destruct, [nested_ty]),
+ trait_ref: ty::TraitRef::from_lang_item(
+ self.tcx(),
+ LangItem::Destruct,
+ cause.span,
+ [nested_ty],
+ ),
constness: ty::BoundConstness::ConstIfConst,
polarity: ty::ImplPolarity::Positive,
}),
@@ -1299,10 +1305,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// or it's an ADT (and we need to check for a custom impl during selection)
_ => {
let predicate = self_ty.rebind(ty::TraitPredicate {
- trait_ref: self
- .tcx()
- .at(cause.span)
- .mk_trait_ref(LangItem::Destruct, [nested_ty]),
+ trait_ref: ty::TraitRef::from_lang_item(
+ self.tcx(),
+ LangItem::Destruct,
+ cause.span,
+ [nested_ty],
+ ),
constness: ty::BoundConstness::ConstIfConst,
polarity: ty::ImplPolarity::Positive,
});
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 6bb53418b..3baf1c97c 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -67,7 +67,7 @@ impl IntercrateAmbiguityCause {
/// Emits notes when the overlap is caused by complex intercrate ambiguities.
/// See #23980 for details.
pub fn add_intercrate_ambiguity_hint(&self, err: &mut Diagnostic) {
- err.note(&self.intercrate_ambiguity_hint());
+ err.note(self.intercrate_ambiguity_hint());
}
pub fn intercrate_ambiguity_hint(&self) -> String {
@@ -449,7 +449,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len());
- let needs_infer = stack.obligation.predicate.has_non_region_infer();
+ let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
// If there are STILL multiple candidates, we can further
// reduce the list by dropping duplicates -- including
@@ -461,7 +461,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.candidate_should_be_dropped_in_favor_of(
&candidates[i],
&candidates[j],
- needs_infer,
+ has_non_region_infer,
) == DropVictim::Yes
});
if should_drop_i {
@@ -537,14 +537,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
self.evaluation_probe(|this| {
- if this.tcx().trait_solver_next() {
- this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])
+ 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(),
- )
+ )?
+ };
+ // If the predicate has done any inference, then downgrade the
+ // result to ambiguous.
+ if this.infcx.shallow_resolve(goal) != goal {
+ result = result.max(EvaluatedToAmbig);
}
+ Ok(result)
})
}
@@ -888,7 +896,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let c1 = tcx.expand_abstract_consts(c1);
let c2 = tcx.expand_abstract_consts(c2);
debug!(
- "evalaute_predicate_recursively: equating consts:\nc1= {:?}\nc2= {:?}",
+ "evaluate_predicate_recursively: equating consts:\nc1= {:?}\nc2= {:?}",
c1, c2
);
@@ -896,8 +904,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
use ty::ConstKind::Unevaluated;
match (c1.kind(), c2.kind()) {
(Unevaluated(a), Unevaluated(b))
- if a.def.did == b.def.did
- && tcx.def_kind(a.def.did) == DefKind::AssocConst =>
+ if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
{
if let Ok(InferOk { obligations, value: () }) = self
.infcx
@@ -1001,7 +1008,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) -> Result<EvaluationResult, OverflowError> {
if !self.is_intercrate()
&& obligation.is_global()
- && obligation.param_env.caller_bounds().iter().all(|bound| bound.needs_subst())
+ && obligation.param_env.caller_bounds().iter().all(|bound| bound.has_param())
{
// If a param env has no global bounds, global obligations do not
// depend on its particular value in order to work, so we can clear
@@ -1331,7 +1338,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
if self.can_use_global_caches(param_env) {
- if !trait_pred.needs_infer() {
+ if !trait_pred.has_infer() {
debug!(?trait_pred, ?result, "insert_evaluation_cache global");
// This may overwrite the cache with the same value
// FIXME: Due to #50507 this overwrites the different values
@@ -1517,7 +1524,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// If there are any inference variables in the `ParamEnv`, then we
// always use a cache local to this particular scope. Otherwise, we
// switch to a global cache.
- if param_env.needs_infer() {
+ if param_env.has_infer() {
return false;
}
@@ -1588,7 +1595,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return false;
}
match result {
- Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.needs_infer(),
+ Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.has_infer(),
_ => true,
}
}
@@ -1614,8 +1621,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if self.can_use_global_caches(param_env) {
if let Err(Overflow(OverflowError::Canonical)) = candidate {
// Don't cache overflow globally; we only produce this in certain modes.
- } else if !pred.needs_infer() {
- if !candidate.needs_infer() {
+ } else if !pred.has_infer() {
+ if !candidate.has_infer() {
debug!(?pred, ?candidate, "insert_candidate_cache global");
// This may overwrite the cache with the same value.
tcx.selection_cache.insert((param_env, pred), dep_node, candidate);
@@ -1646,7 +1653,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let tcx = self.infcx.tcx;
let (def_id, substs) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
- ty::Alias(_, ty::AliasTy { def_id, substs, .. }) => (def_id, substs),
+ ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ (def_id, substs)
+ }
_ => {
span_bug!(
obligation.cause.span,
@@ -1725,7 +1734,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.map(|InferOk { obligations: _, value: () }| {
// This method is called within a probe, so we can't have
// inference variables and placeholders escape.
- if !trait_bound.needs_infer() && !trait_bound.has_placeholders() {
+ if !trait_bound.has_infer() && !trait_bound.has_placeholders() {
Some(trait_bound)
} else {
None
@@ -1784,12 +1793,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.infcx
.at(&obligation.cause, obligation.param_env)
.sup(DefineOpaqueTypes::No, obligation.predicate, infer_projection)
- .map_or(false, |InferOk { obligations, value: () }| {
+ .is_ok_and(|InferOk { obligations, value: () }| {
self.evaluate_predicates_recursively(
TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
nested_obligations.into_iter().chain(obligations),
)
- .map_or(false, |res| res.may_apply())
+ .is_ok_and(|res| res.may_apply())
});
if is_match {
@@ -1841,7 +1850,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
&mut self,
victim: &EvaluatedCandidate<'tcx>,
other: &EvaluatedCandidate<'tcx>,
- needs_infer: bool,
+ has_non_region_infer: bool,
) -> DropVictim {
if victim.candidate == other.candidate {
return DropVictim::Yes;
@@ -1957,7 +1966,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| (ObjectCandidate(i), ObjectCandidate(j)) => {
// Arbitrarily pick the lower numbered candidate for backwards
// compatibility reasons. Don't let this affect inference.
- DropVictim::drop_if(i < j && !needs_infer)
+ DropVictim::drop_if(i < j && !has_non_region_infer)
}
(ObjectCandidate(_), ProjectionCandidate(..))
| (ProjectionCandidate(..), ObjectCandidate(_)) => {
@@ -2063,7 +2072,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// existence of multiple marker trait impls tells us nothing
// about which one should actually apply.
DropVictim::drop_if(
- !needs_infer && other.evaluation.must_apply_considering_regions(),
+ !has_non_region_infer
+ && other.evaluation.must_apply_considering_regions(),
)
}
None => DropVictim::No,
@@ -2315,7 +2325,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
- | ty::Alias(ty::Projection, ..)
+ | ty::Alias(ty::Projection | ty::Inherent, ..)
| ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("asked to assemble constituent types of unexpected type: {:?}", t);
@@ -2413,7 +2423,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
self.tcx(),
cause.clone(),
param_env,
- self.tcx().mk_trait_ref(trait_def_id, [normalized_ty]),
+ ty::TraitRef::new(self.tcx(), trait_def_id, [normalized_ty]),
);
obligations.push(obligation);
obligations
@@ -2449,7 +2459,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// for a variable being generalized...
let guar = self.infcx.tcx.sess.delay_span_bug(
obligation.cause.span,
- &format!(
+ format!(
"Impl {:?} was matchable against {:?} but now is not",
impl_def_id, obligation
),
@@ -2647,14 +2657,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
let predicates = predicates.instantiate_own(tcx, substs);
let mut obligations = Vec::with_capacity(predicates.len());
for (index, (predicate, span)) in predicates.into_iter().enumerate() {
- let cause = cause.clone().derived_cause(parent_trait_pred, |derived| {
- ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
- derived,
- impl_or_alias_def_id: def_id,
- impl_def_predicate_index: Some(index),
- span,
- }))
- });
+ let cause =
+ if Some(parent_trait_pred.def_id()) == tcx.lang_items().coerce_unsized_trait() {
+ cause.clone()
+ } else {
+ cause.clone().derived_cause(parent_trait_pred, |derived| {
+ ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
+ derived,
+ impl_or_alias_def_id: def_id,
+ impl_def_predicate_index: Some(index),
+ span,
+ }))
+ })
+ };
let predicate = normalize_with_depth_to(
self,
param_env,
@@ -3007,16 +3022,16 @@ fn bind_generator_hidden_types_above<'tcx>(
// Only remap erased regions if we use them.
if considering_regions {
- ty = tcx.fold_regions(ty, |mut r, current_depth| {
- if let ty::ReErased = r.kind() {
+ ty = tcx.fold_regions(ty, |r, current_depth| match r.kind() {
+ ty::ReErased => {
let br = ty::BoundRegion {
var: ty::BoundVar::from_u32(counter),
kind: ty::BrAnon(None),
};
counter += 1;
- r = tcx.mk_re_late_bound(current_depth, br);
+ tcx.mk_re_late_bound(current_depth, br)
}
- r
+ 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 8546bbe52..9a4b72013 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -83,6 +83,30 @@ pub fn translate_substs<'tcx>(
source_substs: SubstsRef<'tcx>,
target_node: specialization_graph::Node,
) -> SubstsRef<'tcx> {
+ translate_substs_with_cause(
+ infcx,
+ param_env,
+ source_impl,
+ source_substs,
+ target_node,
+ |_, _| ObligationCause::dummy(),
+ )
+}
+
+/// Like [translate_substs], but obligations from the parent implementation
+/// are registered with the provided `ObligationCause`.
+///
+/// This is for reporting *region* errors from those bounds. Type errors should
+/// not happen because the specialization graph already checks for those, and
+/// will result in an ICE.
+pub fn translate_substs_with_cause<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ source_impl: DefId,
+ source_substs: SubstsRef<'tcx>,
+ target_node: specialization_graph::Node,
+ cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
+) -> SubstsRef<'tcx> {
debug!(
"translate_substs({:?}, {:?}, {:?}, {:?})",
param_env, source_impl, source_substs, target_node
@@ -99,14 +123,13 @@ pub fn translate_substs<'tcx>(
return source_substs;
}
- fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else(
- |()| {
+ fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl, cause)
+ .unwrap_or_else(|()| {
bug!(
"When translating substitutions from {source_impl:?} to {target_impl:?}, \
the expected specialization failed to hold"
)
- },
- )
+ })
}
specialization_graph::Node::Trait(..) => source_trait_ref.substs,
};
@@ -153,20 +176,12 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
// Create an infcx, taking the predicates of impl1 as assumptions:
let infcx = tcx.infer_ctxt().build();
- let impl1_trait_ref =
- match traits::fully_normalize(&infcx, ObligationCause::dummy(), penv, impl1_trait_ref) {
- Ok(impl1_trait_ref) => impl1_trait_ref,
- Err(_errors) => {
- tcx.sess.delay_span_bug(
- tcx.def_span(impl1_def_id),
- format!("failed to fully normalize {impl1_trait_ref}"),
- );
- impl1_trait_ref
- }
- };
// Attempt to prove that impl2 applies, given all of the above.
- fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok()
+ fulfill_implication(&infcx, penv, impl1_trait_ref, impl1_def_id, impl2_def_id, |_, _| {
+ ObligationCause::dummy()
+ })
+ .is_ok()
}
/// Attempt to fulfill all obligations of `target_impl` after unification with
@@ -178,23 +193,41 @@ fn fulfill_implication<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
source_trait_ref: ty::TraitRef<'tcx>,
+ source_impl: DefId,
target_impl: DefId,
+ error_cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
) -> Result<SubstsRef<'tcx>, ()> {
debug!(
"fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
param_env, source_trait_ref, target_impl
);
+ let source_trait_ref = match traits::fully_normalize(
+ &infcx,
+ ObligationCause::dummy(),
+ param_env,
+ source_trait_ref,
+ ) {
+ Ok(source_trait_ref) => source_trait_ref,
+ Err(_errors) => {
+ infcx.tcx.sess.delay_span_bug(
+ infcx.tcx.def_span(source_impl),
+ format!("failed to fully normalize {source_trait_ref}"),
+ );
+ source_trait_ref
+ }
+ };
+
let source_trait = ImplSubject::Trait(source_trait_ref);
let selcx = &mut SelectionContext::new(&infcx);
let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl);
let (target_trait, obligations) =
- util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs);
+ util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs, error_cause);
// do the impls unify? If not, no specialization.
let Ok(InferOk { obligations: more_obligations, .. }) =
- infcx.at(&ObligationCause::dummy(), param_env, ).eq(DefineOpaqueTypes::No,source_trait, target_trait)
+ infcx.at(&ObligationCause::dummy(), param_env).eq(DefineOpaqueTypes::No, source_trait, target_trait)
else {
debug!(
"fulfill_implication: {:?} does not unify with {:?}",
@@ -373,7 +406,7 @@ fn report_conflicting_impls<'tcx>(
}
None => format!("conflicting implementation in crate `{}`", cname),
};
- err.note(&msg);
+ err.note(msg);
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
new file mode 100644
index 000000000..af8dd0da5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
@@ -0,0 +1,55 @@
+use rustc_infer::infer::at::At;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::traits::{FulfillmentError, TraitEngine};
+use rustc_middle::ty::{self, Ty};
+
+use crate::traits::{query::evaluate_obligation::InferCtxtExt, NormalizeExt, Obligation};
+
+pub trait StructurallyNormalizeExt<'tcx> {
+ fn structurally_normalize(
+ &self,
+ ty: Ty<'tcx>,
+ fulfill_cx: &mut dyn TraitEngine<'tcx>,
+ ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>>;
+}
+
+impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
+ fn structurally_normalize(
+ &self,
+ mut ty: Ty<'tcx>,
+ fulfill_cx: &mut dyn TraitEngine<'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() {
+ while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() {
+ let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::NormalizeProjectionType,
+ span: self.cause.span,
+ });
+ let obligation = Obligation::new(
+ self.infcx.tcx,
+ self.cause.clone(),
+ self.param_env,
+ ty::Binder::dummy(ty::ProjectionPredicate {
+ projection_ty,
+ term: new_infer_ty.into(),
+ }),
+ );
+ if self.infcx.predicate_may_hold(&obligation) {
+ fulfill_cx.register_predicate_obligation(self.infcx, obligation);
+ let errors = fulfill_cx.select_where_possible(self.infcx);
+ if !errors.is_empty() {
+ return Err(errors);
+ }
+ ty = self.infcx.resolve_vars_if_possible(new_infer_ty);
+ } else {
+ break;
+ }
+ }
+ Ok(ty)
+ } else {
+ Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx))
+ }
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 20357d4d2..82f3df401 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -197,6 +197,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>(
param_env: ty::ParamEnv<'tcx>,
impl_def_id: DefId,
impl_substs: SubstsRef<'tcx>,
+ cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
let subject = selcx.tcx().impl_subject(impl_def_id);
let subject = subject.subst(selcx.tcx(), impl_substs);
@@ -208,8 +209,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>(
let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
let InferOk { value: predicates, obligations: normalization_obligations2 } =
selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates);
- let impl_obligations =
- super::predicates_for_generics(|_, _| ObligationCause::dummy(), param_env, predicates);
+ let impl_obligations = super::predicates_for_generics(cause, param_env, predicates);
let impl_obligations = impl_obligations
.chain(normalization_obligations1.into_iter())
@@ -243,16 +243,11 @@ pub fn get_vtable_index_of_object_method<'tcx, N>(
) -> Option<usize> {
// Count number of methods preceding the one we are selecting and
// add them to the total offset.
- if let Some(index) = tcx
- .own_existential_vtable_entries(object.upcast_trait_ref.def_id())
+ tcx.own_existential_vtable_entries(object.upcast_trait_ref.def_id())
.iter()
.copied()
.position(|def_id| def_id == method_def_id)
- {
- Some(object.vtable_base + index)
- } else {
- None
- }
+ .map(|index| object.vtable_base + index)
}
pub fn closure_trait_ref_and_return_type<'tcx>(
@@ -267,7 +262,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>(
TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
TupleArgumentsFlag::Yes => tcx.mk_tup(sig.skip_binder().inputs()),
};
- let trait_ref = tcx.mk_trait_ref(fn_trait_def_id, [self_ty, arguments_tuple]);
+ let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]);
sig.map_bound(|sig| (trait_ref, sig.output()))
}
@@ -278,7 +273,7 @@ pub fn generator_trait_ref_and_outputs<'tcx>(
sig: ty::PolyGenSig<'tcx>,
) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> {
assert!(!self_ty.has_escaping_bound_vars());
- let trait_ref = tcx.mk_trait_ref(fn_trait_def_id, [self_ty, sig.skip_binder().resume_ty]);
+ let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, sig.skip_binder().resume_ty]);
sig.map_bound(|sig| (trait_ref, sig.yield_ty, sig.return_ty))
}
@@ -289,7 +284,7 @@ pub fn future_trait_ref_and_outputs<'tcx>(
sig: ty::PolyGenSig<'tcx>,
) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> {
assert!(!self_ty.has_escaping_bound_vars());
- let trait_ref = tcx.mk_trait_ref(fn_trait_def_id, [self_ty]);
+ let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty]);
sig.map_bound(|sig| (trait_ref, sig.return_ty))
}
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index a4e9928f8..cc674ceee 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -4,6 +4,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::traits::util::PredicateSet;
use rustc_infer::traits::ImplSource;
+use rustc_middle::query::Providers;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::InternalSubsts;
use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry};
@@ -353,13 +354,13 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
),
) -> Option<usize> {
let (source, target) = key;
- assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer());
- assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer());
+ assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.has_infer());
+ assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.has_infer());
// this has been typecked-before, so diagnostics is not really needed.
let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);
- let trait_ref = tcx.mk_trait_ref(unsize_trait_did, [source, target]);
+ 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))) {
Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => {
@@ -379,8 +380,8 @@ pub(crate) fn count_own_vtable_entries<'tcx>(
tcx.own_existential_vtable_entries(trait_ref.def_id()).len()
}
-pub(super) fn provide(providers: &mut ty::query::Providers) {
- *providers = ty::query::Providers {
+pub(super) fn provide(providers: &mut Providers) {
+ *providers = Providers {
own_existential_vtable_entries,
vtable_entries,
vtable_trait_upcasting_coercion_new_vptr_slot,
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 3d026506a..086ab32b5 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -58,15 +58,8 @@ pub fn obligations<'tcx>(
GenericArgKind::Lifetime(..) => return Some(Vec::new()),
};
- let mut wf = WfPredicates {
- tcx: infcx.tcx,
- param_env,
- body_id,
- span,
- out: vec![],
- recursion_depth,
- item: None,
- };
+ let mut wf =
+ WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None };
wf.compute(arg);
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out);
@@ -91,7 +84,7 @@ pub fn unnormalized_obligations<'tcx>(
debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
let mut wf = WfPredicates {
- tcx: infcx.tcx,
+ infcx,
param_env,
body_id: CRATE_DEF_ID,
span: DUMMY_SP,
@@ -116,7 +109,7 @@ pub fn trait_obligations<'tcx>(
item: &'tcx hir::Item<'tcx>,
) -> Vec<traits::PredicateObligation<'tcx>> {
let mut wf = WfPredicates {
- tcx: infcx.tcx,
+ infcx,
param_env,
body_id,
span,
@@ -138,7 +131,7 @@ pub fn predicate_obligations<'tcx>(
span: Span,
) -> Vec<traits::PredicateObligation<'tcx>> {
let mut wf = WfPredicates {
- tcx: infcx.tcx,
+ infcx,
param_env,
body_id,
span,
@@ -170,37 +163,28 @@ pub fn predicate_obligations<'tcx>(
ty::PredicateKind::WellFormed(arg) => {
wf.compute(arg);
}
- ty::PredicateKind::ObjectSafe(_) => {}
- ty::PredicateKind::ClosureKind(..) => {}
- ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => {
- wf.compute(a.into());
- wf.compute(b.into());
- }
- ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
- wf.compute(a.into());
- wf.compute(b.into());
- }
+
ty::PredicateKind::ConstEvaluatable(ct) => {
wf.compute(ct.into());
}
- ty::PredicateKind::ConstEquate(c1, c2) => {
- wf.compute(c1.into());
- wf.compute(c2.into());
- }
- ty::PredicateKind::Ambiguous => {}
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("TypeWellFormedFromEnv is only used for Chalk")
- }
- ty::PredicateKind::AliasRelate(..) => {
- bug!("We should only wf check where clauses and `AliasRelate` is not a `Clause`")
+
+ ty::PredicateKind::ObjectSafe(_)
+ | ty::PredicateKind::ClosureKind(..)
+ | ty::PredicateKind::Subtype(..)
+ | ty::PredicateKind::Coerce(..)
+ | ty::PredicateKind::ConstEquate(..)
+ | ty::PredicateKind::Ambiguous
+ | ty::PredicateKind::AliasRelate(..)
+ | ty::PredicateKind::TypeWellFormedFromEnv(..) => {
+ bug!("We should only wf check where clauses, unexpected predicate: {predicate:?}")
}
}
wf.normalize(infcx)
}
-struct WfPredicates<'tcx> {
- tcx: TyCtxt<'tcx>,
+struct WfPredicates<'a, 'tcx> {
+ infcx: &'a InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
span: Span,
@@ -299,9 +283,9 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
}
}
-impl<'tcx> WfPredicates<'tcx> {
+impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
- self.tcx
+ self.infcx.tcx
}
fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
@@ -334,9 +318,16 @@ impl<'tcx> WfPredicates<'tcx> {
/// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
fn compute_trait_pred(&mut self, trait_pred: &ty::TraitPredicate<'tcx>, elaborate: Elaborate) {
- let tcx = self.tcx;
+ let tcx = self.tcx();
let trait_ref = &trait_pred.trait_ref;
+ // Negative trait predicates don't require supertraits to hold, just
+ // that their substs are WF.
+ if trait_pred.polarity == ty::ImplPolarity::Negative {
+ self.compute_negative_trait_pred(trait_ref);
+ return;
+ }
+
// if the trait predicate is not const, the wf obligations should not be const as well.
let obligations = if trait_pred.constness == ty::BoundConstness::NotConst {
self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs)
@@ -371,7 +362,6 @@ impl<'tcx> WfPredicates<'tcx> {
self.out.extend(obligations);
}
- let tcx = self.tcx();
self.out.extend(
trait_ref
.substs
@@ -402,6 +392,14 @@ impl<'tcx> WfPredicates<'tcx> {
);
}
+ // Compute the obligations that are required for `trait_ref` to be WF,
+ // given that it is a *negative* trait predicate.
+ fn compute_negative_trait_pred(&mut self, trait_ref: &ty::TraitRef<'tcx>) {
+ for arg in trait_ref.substs {
+ self.compute(arg);
+ }
+ }
+
/// Pushes the obligations required for `trait_ref::Item` to be WF
/// into `self.out`.
fn compute_projection(&mut self, data: ty::AliasTy<'tcx>) {
@@ -430,13 +428,45 @@ impl<'tcx> WfPredicates<'tcx> {
let obligations = self.nominal_obligations_without_const(data.def_id, data.substs);
self.out.extend(obligations);
+ self.compute_projection_substs(data.substs);
+ }
+
+ fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) {
+ // An inherent projection is well-formed if
+ //
+ // (a) its predicates hold (*)
+ // (b) its substs are wf
+ //
+ // (*) The predicates of an inherent associated type include the
+ // predicates of the impl that it's contained in.
+
+ if !data.self_ty().has_escaping_bound_vars() {
+ // FIXME(inherent_associated_types): Should this happen inside of a snapshot?
+ // FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm!
+ let substs = traits::project::compute_inherent_assoc_ty_substs(
+ &mut traits::SelectionContext::new(self.infcx),
+ self.param_env,
+ data,
+ self.cause(traits::WellFormed(None)),
+ self.recursion_depth,
+ &mut self.out,
+ );
+ // Inherent projection types do not require const predicates.
+ let obligations = self.nominal_obligations_without_const(data.def_id, substs);
+ self.out.extend(obligations);
+ }
+
+ self.compute_projection_substs(data.substs);
+ }
+
+ fn compute_projection_substs(&mut self, substs: SubstsRef<'tcx>) {
let tcx = self.tcx();
let cause = self.cause(traits::WellFormed(None));
let param_env = self.param_env;
let depth = self.recursion_depth;
self.out.extend(
- data.substs
+ substs
.iter()
.filter(|arg| {
matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
@@ -457,9 +487,10 @@ impl<'tcx> WfPredicates<'tcx> {
fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) {
if !subty.has_escaping_bound_vars() {
let cause = self.cause(cause);
- let trait_ref = self.tcx.at(cause.span).mk_trait_ref(LangItem::Sized, [subty]);
+ let trait_ref =
+ ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [subty]);
self.out.push(traits::Obligation::with_depth(
- self.tcx,
+ self.tcx(),
cause,
self.recursion_depth,
self.param_env,
@@ -487,7 +518,7 @@ impl<'tcx> WfPredicates<'tcx> {
match ct.kind() {
ty::ConstKind::Unevaluated(uv) => {
if !ct.has_escaping_bound_vars() {
- let obligations = self.nominal_obligations(uv.def.did, uv.substs);
+ let obligations = self.nominal_obligations(uv.def, uv.substs);
self.out.extend(obligations);
let predicate =
@@ -598,6 +629,10 @@ impl<'tcx> WfPredicates<'tcx> {
walker.skip_current_subtree(); // Subtree handled by compute_projection.
self.compute_projection(data);
}
+ ty::Alias(ty::Inherent, data) => {
+ walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection.
+ self.compute_inherent_projection(data);
+ }
ty::Adt(def, substs) => {
// WfNominalType
@@ -690,7 +725,7 @@ impl<'tcx> WfPredicates<'tcx> {
// All of the requirements on type parameters
// have already been checked for `impl Trait` in
// return position. We do need to check type-alias-impl-trait though.
- if self.tcx.is_type_alias_impl_trait(def_id) {
+ if self.tcx().is_type_alias_impl_trait(def_id) {
let obligations = self.nominal_obligations(def_id, substs);
self.out.extend(obligations);
}
@@ -760,15 +795,15 @@ impl<'tcx> WfPredicates<'tcx> {
substs: SubstsRef<'tcx>,
remap_constness: bool,
) -> Vec<traits::PredicateObligation<'tcx>> {
- let predicates = self.tcx.predicates_of(def_id);
+ let predicates = self.tcx().predicates_of(def_id);
let mut origins = vec![def_id; predicates.predicates.len()];
let mut head = predicates;
while let Some(parent) = head.parent {
- head = self.tcx.predicates_of(parent);
+ head = self.tcx().predicates_of(parent);
origins.extend(iter::repeat(parent).take(head.predicates.len()));
}
- let predicates = predicates.instantiate(self.tcx, substs);
+ let predicates = predicates.instantiate(self.tcx(), substs);
trace!("{:#?}", predicates);
debug_assert_eq!(predicates.predicates.len(), origins.len());
@@ -781,10 +816,10 @@ impl<'tcx> WfPredicates<'tcx> {
};
let cause = self.cause(code);
if remap_constness {
- pred = pred.without_const(self.tcx);
+ pred = pred.without_const(self.tcx());
}
traits::Obligation::with_depth(
- self.tcx,
+ self.tcx(),
cause,
self.recursion_depth,
self.param_env,
@@ -849,7 +884,7 @@ impl<'tcx> WfPredicates<'tcx> {
// Note: in fact we only permit builtin traits, not `Bar<'d>`, I
// am looking forward to the future here.
if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
- let implicit_bounds = object_region_bounds(self.tcx, data);
+ let implicit_bounds = object_region_bounds(self.tcx(), data);
let explicit_bound = region;
@@ -859,7 +894,7 @@ impl<'tcx> WfPredicates<'tcx> {
let outlives =
ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound));
self.out.push(traits::Obligation::with_depth(
- self.tcx,
+ self.tcx(),
cause,
self.recursion_depth,
self.param_env,