summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:24 +0000
commit023939b627b7dc93b01471f7d41fb8553ddb4ffa (patch)
tree60fc59477c605c72b0a1051409062ddecc43f877 /compiler/rustc_trait_selection
parentAdding debian version 1.72.1+dfsg1-1. (diff)
downloadrustc-023939b627b7dc93b01471f7d41fb8553ddb4ffa.tar.xz
rustc-023939b627b7dc93b01471f7d41fb8553ddb4ffa.zip
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_trait_selection')
-rw-r--r--compiler/rustc_trait_selection/messages.ftl19
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs72
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs48
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs563
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs90
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonicalize.rs119
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs410
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs202
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs319
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/inherent_projection.rs50
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs37
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs82
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs32
-rw-r--r--compiler/rustc_trait_selection/src/solve/opaques.rs15
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs252
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/cache.rs32
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs445
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs120
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs635
-rw-r--r--compiler/rustc_trait_selection/src/solve/weak_types.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs41
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs240
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs250
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs62
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs528
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs73
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs64
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs35
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs319
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs33
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs53
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs134
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs452
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs360
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs90
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_normalize.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs48
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs184
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs95
53 files changed, 3747 insertions, 2982 deletions
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 217ba71b6..f4c9dfa34 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -1,3 +1,22 @@
+trait_selection_adjust_signature_borrow = consider adjusting the signature so it borrows its {$len ->
+ [one] argument
+ *[other] arguments
+ }
+
+trait_selection_adjust_signature_remove_borrow = consider adjusting the signature so it does not borrow its {$len ->
+ [one] argument
+ *[other] arguments
+ }
+
+trait_selection_closure_fn_mut_label = closure is `FnMut` because it mutates the variable `{$place}` here
+
+trait_selection_closure_fn_once_label = closure is `FnOnce` because it moves the variable `{$place}` out of its environment
+
+trait_selection_closure_kind_mismatch = expected a closure that implements the `{$expected}` trait, but this closure only implements `{$found}`
+ .label = this closure implements `{$found}`, not `{$expected}`
+
+trait_selection_closure_kind_requirement = the requirement to implement `{$expected}` derives from here
+
trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 54e22cc3d..c1fb287d6 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1,7 +1,10 @@
use crate::fluent_generated as fluent;
-use rustc_errors::{ErrorGuaranteed, Handler, IntoDiagnostic};
+use rustc_errors::{
+ AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic,
+ SubdiagnosticMessage,
+};
use rustc_macros::Diagnostic;
-use rustc_middle::ty::{self, PolyTraitRef, Ty};
+use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty};
use rustc_span::{Span, Symbol};
#[derive(Diagnostic)]
@@ -97,3 +100,68 @@ pub struct InherentProjectionNormalizationOverflow {
pub span: Span,
pub ty: String,
}
+
+pub enum AdjustSignatureBorrow {
+ Borrow { to_borrow: Vec<(Span, String)> },
+ RemoveBorrow { remove_borrow: Vec<(Span, String)> },
+}
+
+impl AddToDiagnostic for AdjustSignatureBorrow {
+ fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+ where
+ F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+ {
+ match self {
+ AdjustSignatureBorrow::Borrow { to_borrow } => {
+ diag.set_arg("len", to_borrow.len());
+ diag.multipart_suggestion_verbose(
+ fluent::trait_selection_adjust_signature_borrow,
+ to_borrow,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => {
+ diag.set_arg("len", remove_borrow.len());
+ diag.multipart_suggestion_verbose(
+ fluent::trait_selection_adjust_signature_remove_borrow,
+ remove_borrow,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_closure_kind_mismatch, code = "E0525")]
+pub struct ClosureKindMismatch {
+ #[primary_span]
+ #[label]
+ pub closure_span: Span,
+ pub expected: ClosureKind,
+ pub found: ClosureKind,
+ #[label(trait_selection_closure_kind_requirement)]
+ pub cause_span: Span,
+
+ #[subdiagnostic]
+ pub fn_once_label: Option<ClosureFnOnceLabel>,
+
+ #[subdiagnostic]
+ pub fn_mut_label: Option<ClosureFnMutLabel>,
+}
+
+#[derive(Subdiagnostic)]
+#[label(trait_selection_closure_fn_once_label)]
+pub struct ClosureFnOnceLabel {
+ #[primary_span]
+ pub span: Span,
+ pub place: String,
+}
+
+#[derive(Subdiagnostic)]
+#[label(trait_selection_closure_fn_mut_label)]
+pub struct ClosureFnMutLabel {
+ #[primary_span]
+ pub span: Span,
+ pub place: String,
+}
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 6efc1e730..38153cccf 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -72,7 +72,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
cause: traits::ObligationCause::dummy(),
param_env,
recursion_depth: 0,
- predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx),
+ predicate: ty::Binder::dummy(trait_ref).to_predicate(self.tcx),
};
self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr)
}
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index 422a6ee34..6b839d64b 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -1,3 +1,16 @@
+//! Implements the `AliasRelate` goal, which is used when unifying aliases.
+//! Doing this via a separate goal is called "deferred alias relation" and part
+//! of our more general approach to "lazy normalization".
+//!
+//! This goal, e.g. `A alias-relate B`, may be satisfied by one of three branches:
+//! * normalizes-to: If `A` is a projection, we can prove the equivalent
+//! projection predicate with B as the right-hand side of the projection.
+//! This goal is computed in both directions, if both are aliases.
+//! * subst-relate: Equate `A` and `B` by their substs, if they're both
+//! aliases with the same def-id.
+//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both
+//! may apply, then we can compute the "intersection" of both normalizes-to by
+//! performing them together. This is used specifically to resolve ambiguities.
use super::{EvalCtxt, SolverMode};
use rustc_infer::traits::query::NoSolution;
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
@@ -65,25 +78,28 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
direction,
Invert::Yes,
));
- // Relate via substs
- let subst_relate_response = self
- .assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction);
- candidates.extend(subst_relate_response);
+ // Relate via args
+ candidates.extend(
+ self.assemble_subst_relate_candidate(
+ param_env, alias_lhs, alias_rhs, direction,
+ ),
+ );
debug!(?candidates);
if let Some(merged) = self.try_merge_responses(&candidates) {
Ok(merged)
} else {
- // When relating two aliases and we have ambiguity, we prefer
- // relating the generic arguments of the aliases over normalizing
- // them. This is necessary for inference during typeck.
+ // When relating two aliases and we have ambiguity, if both
+ // aliases can be normalized to something, we prefer
+ // "bidirectionally normalizing" both of them within the same
+ // candidate.
+ //
+ // See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/25>.
//
// As this is incomplete, we must not do so during coherence.
match self.solver_mode() {
SolverMode::Normal => {
- if let Ok(subst_relate_response) = subst_relate_response {
- Ok(subst_relate_response)
- } else if let Ok(bidirectional_normalizes_to_response) = self
+ if let Ok(bidirectional_normalizes_to_response) = self
.assemble_bidirectional_normalizes_to_candidate(
param_env, lhs, rhs, direction,
)
@@ -115,6 +131,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
})
}
+ // Computes the normalizes-to branch, with side-effects. This must be performed
+ // in a probe in order to not taint the evaluation context.
fn normalizes_to_inner(
&mut self,
param_env: ty::ParamEnv<'tcx>,
@@ -124,9 +142,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
invert: Invert,
) -> Result<(), NoSolution> {
let other = match direction {
- // This is purely an optimization.
+ // This is purely an optimization. No need to instantiate a new
+ // infer var and equate the RHS to it.
ty::AliasRelationDirection::Equate => other,
+ // Instantiate an infer var and subtype our RHS to it, so that we
+ // properly represent a subtype relation between the LHS and RHS
+ // of the goal.
ty::AliasRelationDirection::Subtype => {
let fresh = self.next_term_infer_of_kind(other);
let (sub, sup) = match invert {
@@ -140,7 +162,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
self.add_goal(Goal::new(
self.tcx(),
param_env,
- ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }),
+ ty::ProjectionPredicate { projection_ty: alias, term: other },
));
Ok(())
@@ -153,7 +175,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
alias_rhs: ty::AliasTy<'tcx>,
direction: ty::AliasRelationDirection,
) -> QueryResult<'tcx> {
- self.probe_candidate("substs relate").enter(|ecx| {
+ self.probe_candidate("args relate").enter(|ecx| {
match direction {
ty::AliasRelationDirection::Equate => {
ecx.eq(param_env, alias_lhs, alias_rhs)?;
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 28138054a..36194f973 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -1,18 +1,18 @@
//! Code shared by trait and projection goals for candidate assembly.
-use super::search_graph::OverflowHandler;
use super::{EvalCtxt, SolverMode};
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::inspect::CandidateKind;
-use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
-use rustc_middle::ty::fast_reject::TreatProjections;
-use rustc_middle::ty::TypeFoldable;
+use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
+use rustc_middle::traits::BuiltinImplSource;
+use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{fast_reject, TypeFoldable};
+use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
+use rustc_span::ErrorGuaranteed;
use std::fmt::Debug;
pub(super) mod structural_traits;
@@ -87,16 +87,6 @@ pub(super) enum CandidateSource {
AliasBound,
}
-/// Records additional information about what kind of built-in impl this is.
-/// This should only be used by selection.
-#[derive(Debug, Clone, Copy)]
-pub(super) enum BuiltinImplSource {
- TraitUpcasting,
- Object,
- Misc,
- Ambiguity,
-}
-
/// Methods used to assemble candidates for either trait or projection goals.
pub(super) trait GoalKind<'tcx>:
TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
@@ -109,10 +99,10 @@ pub(super) trait GoalKind<'tcx>:
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`]).
+ /// 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>,
@@ -120,9 +110,9 @@ pub(super) trait GoalKind<'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.
+ /// 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.
fn consider_implied_clause(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
@@ -149,9 +139,9 @@ pub(super) trait GoalKind<'tcx>:
})
}
- // Consider a clause specifically for a `dyn Trait` self type. This requires
- // additionally checking all of the supertraits and object bounds to hold,
- // since they're not implied by the well-formedness of the object type.
+ /// Consider a clause specifically for a `dyn Trait` self type. This requires
+ /// additionally checking all of the supertraits and object bounds to hold,
+ /// since they're not implied by the well-formedness of the object type.
fn consider_object_bound_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
@@ -160,18 +150,14 @@ pub(super) trait GoalKind<'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)),
- );
+ 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,
+ ));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
@@ -182,112 +168,137 @@ pub(super) trait GoalKind<'tcx>:
impl_def_id: DefId,
) -> QueryResult<'tcx>;
- // A type implements an `auto trait` if its components do as well. These components
- // are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`].
+ /// If the predicate contained an error, we want to avoid emitting unnecessary trait
+ /// errors but still want to emit errors for other trait goals. We have some special
+ /// handling for this case.
+ ///
+ /// Trait goals always hold while projection goals never do. This is a bit arbitrary
+ /// but prevents incorrect normalization while hiding any trait errors.
+ fn consider_error_guaranteed_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ guar: ErrorGuaranteed,
+ ) -> QueryResult<'tcx>;
+
+ /// A type implements an `auto trait` if its components do as well.
+ ///
+ /// These components are given by built-in rules from
+ /// [`structural_traits::instantiate_constituent_tys_for_auto_trait`].
fn consider_auto_trait_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // A trait alias holds if the RHS traits and `where` clauses hold.
+ /// A trait alias holds if the RHS traits and `where` clauses hold.
fn consider_trait_alias_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // A type is `Copy` or `Clone` if its components are `Sized`. These components
- // are given by built-in rules from [`instantiate_constituent_tys_for_sized_trait`].
+ /// A type is `Copy` or `Clone` if its components are `Sized`.
+ ///
+ /// These components are given by built-in rules from
+ /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`].
fn consider_builtin_sized_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. These
- // components are given by built-in rules from [`instantiate_constituent_tys_for_copy_clone_trait`].
+ /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`.
+ ///
+ /// These components are given by built-in rules from
+ /// [`structural_traits::instantiate_constituent_tys_for_copy_clone_trait`].
fn consider_builtin_copy_clone_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // A type is `PointerLike` if we can compute its layout, and that layout
- // matches the layout of `usize`.
+ /// A type is `PointerLike` if we can compute its layout, and that layout
+ /// matches the layout of `usize`.
fn consider_builtin_pointer_like_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // A type is a `FnPtr` if it is of `FnPtr` type.
+ /// A type is a `FnPtr` if it is of `FnPtr` type.
fn consider_builtin_fn_ptr_trait_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
- // family of traits where `A` is given by the signature of the type.
+ /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
+ /// family of traits where `A` is given by the signature of the type.
fn consider_builtin_fn_trait_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
kind: ty::ClosureKind,
) -> QueryResult<'tcx>;
- // `Tuple` is implemented if the `Self` type is a tuple.
+ /// `Tuple` is implemented if the `Self` type is a tuple.
fn consider_builtin_tuple_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // `Pointee` is always implemented.
- //
- // See the projection implementation for the `Metadata` types for all of
- // the built-in types. For structs, the metadata type is given by the struct
- // tail.
+ /// `Pointee` is always implemented.
+ ///
+ /// See the projection implementation for the `Metadata` types for all of
+ /// the built-in types. For structs, the metadata type is given by the struct
+ /// tail.
fn consider_builtin_pointee_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // A generator (that comes from an `async` desugaring) is known to implement
- // `Future<Output = O>`, where `O` is given by the generator's return type
- // that was computed during type-checking.
+ /// A generator (that comes from an `async` desugaring) is known to implement
+ /// `Future<Output = O>`, where `O` is given by the generator's return type
+ /// that was computed during type-checking.
fn consider_builtin_future_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // A generator (that doesn't come from an `async` desugaring) is known to
- // implement `Generator<R, Yield = Y, Return = O>`, given the resume, yield,
- // and return types of the generator computed during type-checking.
+ /// A generator (that doesn't come from an `async` desugaring) is known to
+ /// implement `Generator<R, Yield = Y, Return = O>`, given the resume, yield,
+ /// and return types of the generator computed during type-checking.
fn consider_builtin_generator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // The most common forms of unsizing are array to slice, and concrete (Sized)
- // type into a `dyn Trait`. ADTs and Tuples can also have their final field
- // unsized if it's generic.
- fn consider_builtin_unsize_candidate(
+ fn consider_builtin_discriminant_kind_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- // `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
- // if `Trait2` is a (transitive) supertrait of `Trait2`.
- fn consider_builtin_dyn_upcast_candidates(
+ fn consider_builtin_destruct_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- ) -> Vec<CanonicalResponse<'tcx>>;
+ ) -> QueryResult<'tcx>;
- fn consider_builtin_discriminant_kind_candidate(
+ fn consider_builtin_transmute_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
- fn consider_builtin_destruct_candidate(
+ /// Consider (possibly several) candidates to upcast or unsize a type to another
+ /// type, excluding the coercion of a sized type into a `dyn Trait`.
+ ///
+ /// We return the `BuiltinImplSource` for each candidate as it is needed
+ /// for unsize coercion in hir typeck and because it is difficult to
+ /// otherwise recompute this for codegen. This is a bit of a mess but the
+ /// easiest way to maintain the existing behavior for now.
+ fn consider_structural_builtin_unsize_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- ) -> QueryResult<'tcx>;
+ ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
- fn consider_builtin_transmute_candidate(
+ /// Consider the `Unsize` candidate corresponding to coercing a sized type
+ /// into a `dyn Trait`.
+ ///
+ /// This is computed separately from the rest of the `Unsize` candidates
+ /// since it is only done once per self type, and not once per
+ /// *normalization step* (in `assemble_candidates_via_self_ty`).
+ fn consider_unsize_to_dyn_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
@@ -299,35 +310,68 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
goal: Goal<'tcx, G>,
) -> Vec<Candidate<'tcx>> {
debug_assert_eq!(goal, self.resolve_vars_if_possible(goal));
+ if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) {
+ return ambig;
+ }
+
+ let mut candidates = self.assemble_candidates_via_self_ty(goal, 0);
+
+ self.assemble_unsize_to_dyn_candidate(goal, &mut candidates);
+
+ self.assemble_blanket_impl_candidates(goal, &mut candidates);
+
+ self.assemble_param_env_candidates(goal, &mut candidates);
+
+ self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
- // 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 structurally resolve the type one layer.
- if goal.predicate.self_ty().is_ty_var() {
- return vec![Candidate {
- source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
+ candidates
+ }
+
+ /// `?0: 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 structurally resolve the type one layer.
+ ///
+ /// It would also require us to consider all impls of the trait, which is both pretty
+ /// bad for perf and would also constrain the self type if there is just a single impl.
+ fn assemble_self_ty_infer_ambiguity_response<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ ) -> Option<Vec<Candidate<'tcx>>> {
+ goal.predicate.self_ty().is_ty_var().then(|| {
+ vec![Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result: self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
.unwrap(),
- }];
+ }]
+ })
+ }
+
+ /// Assemble candidates which apply to the self type. This only looks at candidate which
+ /// apply to the specific self type and ignores all others.
+ ///
+ /// Returns `None` if the self type is still ambiguous.
+ fn assemble_candidates_via_self_ty<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ num_steps: usize,
+ ) -> Vec<Candidate<'tcx>> {
+ debug_assert_eq!(goal, self.resolve_vars_if_possible(goal));
+ if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) {
+ return ambig;
}
let mut candidates = Vec::new();
- self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);
-
- self.assemble_impl_candidates(goal, &mut candidates);
+ self.assemble_non_blanket_impl_candidates(goal, &mut candidates);
self.assemble_builtin_impl_candidates(goal, &mut candidates);
- self.assemble_param_env_candidates(goal, &mut candidates);
-
self.assemble_alias_bound_candidates(goal, &mut candidates);
self.assemble_object_bound_candidates(goal, &mut candidates);
- self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
-
+ self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates, num_steps);
candidates
}
@@ -350,70 +394,179 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
&mut self,
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
+ num_steps: usize,
+ ) {
+ let tcx = self.tcx();
+ let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return };
+
+ candidates.extend(self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| {
+ if num_steps < ecx.local_overflow_limit() {
+ let normalized_ty = ecx.next_ty_infer();
+ let normalizes_to_goal = goal.with(
+ tcx,
+ ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
+ );
+ ecx.add_goal(normalizes_to_goal);
+ if let Err(NoSolution) = ecx.try_evaluate_added_goals() {
+ debug!("self type normalization failed");
+ return vec![];
+ }
+ let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
+ debug!(?normalized_ty, "self type normalized");
+ // NOTE: Alternatively we could call `evaluate_goal` here and only
+ // have a `Normalized` candidate. This doesn't work as long as we
+ // use `CandidateSource` in winnowing.
+ let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
+ ecx.assemble_candidates_via_self_ty(goal, num_steps + 1)
+ } else {
+ match ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) {
+ Ok(result) => vec![Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+ result,
+ }],
+ Err(NoSolution) => vec![],
+ }
+ }
+ }));
+ }
+
+ #[instrument(level = "debug", skip_all)]
+ fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
) {
let tcx = self.tcx();
- let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else {
- return
+ let self_ty = goal.predicate.self_ty();
+ let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
+ let mut consider_impls_for_simplified_type = |simp| {
+ if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) {
+ for &impl_def_id in impls_for_type {
+ match G::consider_impl_candidate(self, goal, impl_def_id) {
+ Ok(result) => candidates
+ .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }),
+ Err(NoSolution) => (),
+ }
+ }
+ }
};
- let normalized_self_candidates: Result<_, NoSolution> =
- self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| {
- ecx.with_incremented_depth(
- |ecx| {
- let result = ecx.evaluate_added_goals_and_make_canonical_response(
- Certainty::Maybe(MaybeCause::Overflow),
- )?;
- Ok(vec![Candidate {
- source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
- result,
- }])
- },
- |ecx| {
- let normalized_ty = ecx.next_ty_infer();
- let normalizes_to_goal = goal.with(
- tcx,
- ty::Binder::dummy(ty::ProjectionPredicate {
- projection_ty,
- term: normalized_ty.into(),
- }),
- );
- ecx.add_goal(normalizes_to_goal);
- let _ = ecx.try_evaluate_added_goals().inspect_err(|_| {
- debug!("self type normalization failed");
- })?;
- let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
- debug!(?normalized_ty, "self type normalized");
- // NOTE: Alternatively we could call `evaluate_goal` here and only
- // have a `Normalized` candidate. This doesn't work as long as we
- // use `CandidateSource` in winnowing.
- let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
- Ok(ecx.assemble_and_evaluate_candidates(goal))
- },
- )
- });
+ match self_ty.kind() {
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Adt(_, _)
+ | ty::Foreign(_)
+ | ty::Str
+ | ty::Array(_, _)
+ | ty::Slice(_)
+ | ty::RawPtr(_)
+ | ty::Ref(_, _, _)
+ | ty::FnDef(_, _)
+ | ty::FnPtr(_)
+ | ty::Dynamic(_, _, _)
+ | ty::Closure(_, _)
+ | ty::Generator(_, _, _)
+ | ty::Never
+ | ty::Tuple(_) => {
+ let simp =
+ fast_reject::simplify_type(tcx, self_ty, TreatParams::ForLookup).unwrap();
+ consider_impls_for_simplified_type(simp);
+ }
+
+ // HACK: For integer and float variables we have to manually look at all impls
+ // which have some integer or float as a self type.
+ ty::Infer(ty::IntVar(_)) => {
+ use ty::IntTy::*;
+ use ty::UintTy::*;
+ // This causes a compiler error if any new integer kinds are added.
+ let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy;
+ let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy;
+ let possible_integers = [
+ // signed integers
+ SimplifiedType::Int(I8),
+ SimplifiedType::Int(I16),
+ SimplifiedType::Int(I32),
+ SimplifiedType::Int(I64),
+ SimplifiedType::Int(I128),
+ SimplifiedType::Int(Isize),
+ // unsigned integers
+ SimplifiedType::Uint(U8),
+ SimplifiedType::Uint(U16),
+ SimplifiedType::Uint(U32),
+ SimplifiedType::Uint(U64),
+ SimplifiedType::Uint(U128),
+ SimplifiedType::Uint(Usize),
+ ];
+ for simp in possible_integers {
+ consider_impls_for_simplified_type(simp);
+ }
+ }
+
+ ty::Infer(ty::FloatVar(_)) => {
+ // This causes a compiler error if any new float kinds are added.
+ let (ty::FloatTy::F32 | ty::FloatTy::F64);
+ let possible_floats = [
+ SimplifiedType::Float(ty::FloatTy::F32),
+ SimplifiedType::Float(ty::FloatTy::F64),
+ ];
+
+ for simp in possible_floats {
+ consider_impls_for_simplified_type(simp);
+ }
+ }
+
+ // The only traits applying to aliases and placeholders are blanket impls.
+ //
+ // Impls which apply to an alias after normalization are handled by
+ // `assemble_candidates_after_normalizing_self_ty`.
+ ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (),
+
+ // FIXME: These should ideally not exist as a self type. It would be nice for
+ // the builtin auto trait impls of generators to instead directly recurse
+ // into the witness.
+ ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(_, _) => (),
+
+ // These variants should not exist as a self type.
+ ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+ | ty::Param(_)
+ | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"),
+ }
+ }
- if let Ok(normalized_self_candidates) = normalized_self_candidates {
- candidates.extend(normalized_self_candidates);
+ fn assemble_unsize_to_dyn_candidate<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
+ ) {
+ let tcx = self.tcx();
+ if tcx.lang_items().unsize_trait() == Some(goal.predicate.trait_def_id(tcx)) {
+ match G::consider_unsize_to_dyn_candidate(self, goal) {
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+ result,
+ }),
+ Err(NoSolution) => (),
+ }
}
}
- #[instrument(level = "debug", skip_all)]
- fn assemble_impl_candidates<G: GoalKind<'tcx>>(
+ fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
&mut self,
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
) {
let tcx = self.tcx();
- tcx.for_each_relevant_impl_treating_projections(
- goal.predicate.trait_def_id(tcx),
- goal.predicate.self_ty(),
- TreatProjections::NextSolverLookup,
- |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) {
+ let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
+ for &impl_def_id in trait_impls.blanket_impls() {
+ match G::consider_impl_candidate(self, goal, impl_def_id) {
Ok(result) => candidates
.push(Candidate { source: CandidateSource::Impl(impl_def_id), result }),
Err(NoSolution) => (),
- },
- );
+ }
+ }
}
#[instrument(level = "debug", skip_all)]
@@ -422,8 +575,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
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());
+ let tcx = self.tcx();
+ let lang_items = tcx.lang_items();
+ let trait_def_id = goal.predicate.trait_def_id(tcx);
// N.B. When assembling built-in candidates for lang items that are also
// `auto` traits, then the auto trait candidate that is assembled in
@@ -432,9 +586,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// Instead of adding the logic here, it's a better idea to add it in
// `EvalCtxt::disqualify_auto_trait_candidate_due_to_possible_impl` in
// `solve::trait_goals` instead.
- let result = if self.tcx().trait_is_auto(trait_def_id) {
+ let result = if let Err(guar) = goal.predicate.error_reported() {
+ G::consider_error_guaranteed_candidate(self, guar)
+ } else if tcx.trait_is_auto(trait_def_id) {
G::consider_auto_trait_candidate(self, goal)
- } else if self.tcx().trait_is_alias(trait_def_id) {
+ } else if tcx.trait_is_alias(trait_def_id) {
G::consider_trait_alias_candidate(self, goal)
} else if lang_items.sized_trait() == Some(trait_def_id) {
G::consider_builtin_sized_candidate(self, goal)
@@ -456,8 +612,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
G::consider_builtin_future_candidate(self, goal)
} else if lang_items.gen_trait() == Some(trait_def_id) {
G::consider_builtin_generator_candidate(self, goal)
- } else if lang_items.unsize_trait() == Some(trait_def_id) {
- G::consider_builtin_unsize_candidate(self, goal)
} else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
G::consider_builtin_discriminant_kind_candidate(self, goal)
} else if lang_items.destruct_trait() == Some(trait_def_id) {
@@ -479,11 +633,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// There may be multiple unsize candidates for a trait with several supertraits:
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
if lang_items.unsize_trait() == Some(trait_def_id) {
- for result in G::consider_builtin_dyn_upcast_candidates(self, goal) {
- candidates.push(Candidate {
- source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
- result,
- });
+ for (result, source) in G::consider_structural_builtin_unsize_candidates(self, goal) {
+ candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
}
}
}
@@ -544,7 +695,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
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)
+ for assumption in
+ self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args)
{
match G::consider_alias_bound_candidate(self, goal, assumption) {
Ok(result) => {
@@ -584,7 +736,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
self.tcx(),
ty::TraitPredicate {
trait_ref: self_trait_ref,
- constness: ty::BoundConstness::NotConst,
polarity: ty::ImplPolarity::Positive,
},
);
@@ -698,30 +849,53 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
ty::Dynamic(bounds, ..) => bounds,
};
- let own_bounds: FxIndexSet<_> =
- bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect();
- for assumption in elaborate(tcx, own_bounds.iter().copied())
- // we only care about bounds that match the `Self` type
- .filter_only_self()
- {
- // FIXME: Predicates are fully elaborated in the object type's existential bounds
- // list. We want to only consider these pre-elaborated projections, and not other
- // projection predicates that we reach by elaborating the principal trait ref,
- // since that'll cause ambiguity.
- //
- // We can remove this when we have implemented lifetime intersections in responses.
- if assumption.as_projection_clause().is_some() && !own_bounds.contains(&assumption) {
- continue;
- }
+ // Do not consider built-in object impls for non-object-safe types.
+ if bounds.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
+ return;
+ }
- match G::consider_object_bound_candidate(self, goal, assumption) {
- Ok(result) => candidates.push(Candidate {
- source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
- result,
- }),
- Err(NoSolution) => (),
+ // Consider all of the auto-trait and projection bounds, which don't
+ // need to be recorded as a `BuiltinImplSource::Object` since they don't
+ // really have a vtable base...
+ for bound in bounds {
+ match bound.skip_binder() {
+ ty::ExistentialPredicate::Trait(_) => {
+ // Skip principal
+ }
+ ty::ExistentialPredicate::Projection(_)
+ | ty::ExistentialPredicate::AutoTrait(_) => {
+ match G::consider_object_bound_candidate(
+ self,
+ goal,
+ bound.with_self_ty(tcx, self_ty),
+ ) {
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+ result,
+ }),
+ Err(NoSolution) => (),
+ }
+ }
}
}
+
+ // FIXME: We only need to do *any* of this if we're considering a trait goal,
+ // since we don't need to look at any supertrait or anything if we are doing
+ // a projection goal.
+ if let Some(principal) = bounds.principal() {
+ let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
+ self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| {
+ match G::consider_object_bound_candidate(ecx, goal, assumption.to_predicate(tcx)) {
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object {
+ vtable_base,
+ }),
+ result,
+ }),
+ Err(NoSolution) => (),
+ }
+ });
+ }
}
#[instrument(level = "debug", skip_all)]
@@ -730,26 +904,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
) {
+ let tcx = self.tcx();
match self.solver_mode() {
SolverMode::Normal => return,
- SolverMode::Coherence => {
- let trait_ref = goal.predicate.trait_ref(self.tcx());
- match coherence::trait_ref_is_knowable(self.tcx(), trait_ref) {
- Ok(()) => {}
- Err(_) => match self
- .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
- {
- Ok(result) => candidates.push(Candidate {
- source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
- result,
- }),
- // FIXME: This will be reachable at some point if we're in
- // `assemble_candidates_after_normalizing_self_ty` and we get a
- // universe error. We'll deal with it at this point.
- Err(NoSolution) => bug!("coherence candidate resulted in NoSolution"),
- },
+ SolverMode::Coherence => {}
+ };
+
+ let result = self.probe_candidate("coherence unknowable").enter(|ecx| {
+ let trait_ref = goal.predicate.trait_ref(tcx);
+
+ #[derive(Debug)]
+ enum FailureKind {
+ Overflow,
+ NoSolution(NoSolution),
+ }
+ let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) {
+ Ok(Some(ty)) => Ok(ty),
+ Ok(None) => Err(FailureKind::Overflow),
+ Err(e) => Err(FailureKind::NoSolution(e)),
+ };
+
+ match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) {
+ Err(FailureKind::Overflow) => {
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
+ }
+ Err(FailureKind::NoSolution(NoSolution)) | Ok(Ok(())) => Err(NoSolution),
+ Ok(Err(_)) => {
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
+ });
+
+ match result {
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+ result,
+ }),
+ Err(NoSolution) => {}
}
}
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 3bb8cad15..c47767101 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -1,6 +1,9 @@
+//! Code which is used by built-in goals that match "structurally", such a auto
+//! traits, `Copy`/`Clone`.
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{def_id::DefId, Movability, Mutability};
use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::Goal;
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
};
@@ -51,36 +54,36 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
Ok(tys.iter().collect())
}
- ty::Closure(_, ref substs) => Ok(vec![substs.as_closure().tupled_upvars_ty()]),
+ ty::Closure(_, ref args) => Ok(vec![args.as_closure().tupled_upvars_ty()]),
- ty::Generator(_, ref substs, _) => {
- let generator_substs = substs.as_generator();
- Ok(vec![generator_substs.tupled_upvars_ty(), generator_substs.witness()])
+ ty::Generator(_, ref args, _) => {
+ let generator_args = args.as_generator();
+ Ok(vec![generator_args.tupled_upvars_ty(), generator_args.witness()])
}
ty::GeneratorWitness(types) => Ok(ecx.instantiate_binder_with_placeholders(types).to_vec()),
- ty::GeneratorWitnessMIR(def_id, substs) => Ok(ecx
+ ty::GeneratorWitnessMIR(def_id, args) => Ok(ecx
.tcx()
.generator_hidden_types(def_id)
.map(|bty| {
ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars(
tcx,
- bty.subst(tcx, substs),
+ bty.instantiate(tcx, args),
))
})
.collect()),
// For `PhantomData<T>`, we pass `T`.
- ty::Adt(def, substs) if def.is_phantom_data() => Ok(vec![substs.type_at(0)]),
+ ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![args.type_at(0)]),
- ty::Adt(def, substs) => Ok(def.all_fields().map(|f| f.ty(tcx, substs)).collect()),
+ ty::Adt(def, args) => Ok(def.all_fields().map(|f| f.ty(tcx, args)).collect()),
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
- Ok(vec![tcx.type_of(def_id).subst(tcx, substs)])
+ Ok(vec![tcx.type_of(def_id).instantiate(tcx, args)])
}
}
}
@@ -146,9 +149,9 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
ty::Tuple(tys) => Ok(tys.to_vec()),
- ty::Adt(def, substs) => {
+ ty::Adt(def, args) => {
let sized_crit = def.sized_constraint(ecx.tcx());
- Ok(sized_crit.subst_iter_copied(ecx.tcx(), substs).collect())
+ Ok(sized_crit.iter_instantiated(ecx.tcx(), args).collect())
}
}
}
@@ -158,14 +161,12 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
ty: Ty<'tcx>,
) -> Result<Vec<Ty<'tcx>>, NoSolution> {
match *ty.kind() {
- ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
- | ty::FnDef(..)
- | ty::FnPtr(_)
- | ty::Error(_) => Ok(vec![]),
+ ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]),
// Implementations are provided in core
ty::Uint(_)
| ty::Int(_)
+ | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Bool
| ty::Float(_)
| ty::Char
@@ -192,11 +193,11 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
ty::Tuple(tys) => Ok(tys.to_vec()),
- ty::Closure(_, substs) => Ok(vec![substs.as_closure().tupled_upvars_ty()]),
+ ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]),
- ty::Generator(_, substs, Movability::Movable) => {
+ ty::Generator(_, args, Movability::Movable) => {
if ecx.tcx().features().generator_clone {
- let generator = substs.as_generator();
+ let generator = args.as_generator();
Ok(vec![generator.tupled_upvars_ty(), generator.witness()])
} else {
Err(NoSolution)
@@ -205,13 +206,13 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
ty::GeneratorWitness(types) => Ok(ecx.instantiate_binder_with_placeholders(types).to_vec()),
- ty::GeneratorWitnessMIR(def_id, substs) => Ok(ecx
+ ty::GeneratorWitnessMIR(def_id, args) => Ok(ecx
.tcx()
.generator_hidden_types(def_id)
.map(|bty| {
ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars(
ecx.tcx(),
- bty.subst(ecx.tcx(), substs),
+ bty.instantiate(ecx.tcx(), args),
))
})
.collect()),
@@ -226,13 +227,13 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> {
match *self_ty.kind() {
// keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
- ty::FnDef(def_id, substs) => {
+ ty::FnDef(def_id, args) => {
let sig = tcx.fn_sig(def_id);
if sig.skip_binder().is_fn_trait_compatible()
&& tcx.codegen_fn_attrs(def_id).target_features.is_empty()
{
Ok(Some(
- sig.subst(tcx, substs)
+ sig.instantiate(tcx, args)
.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())),
))
} else {
@@ -247,9 +248,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
Err(NoSolution)
}
}
- ty::Closure(_, substs) => {
- let closure_substs = substs.as_closure();
- match closure_substs.kind_ty().to_opt_closure_kind() {
+ ty::Closure(_, args) => {
+ let closure_args = args.as_closure();
+ match closure_args.kind_ty().to_opt_closure_kind() {
// If the closure's kind doesn't extend the goal kind,
// then the closure doesn't implement the trait.
Some(closure_kind) => {
@@ -265,7 +266,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
}
}
}
- Ok(Some(closure_substs.sig().map_bound(|sig| (sig.inputs()[0], sig.output()))))
+ Ok(Some(closure_args.sig().map_bound(|sig| (sig.inputs()[0], sig.output()))))
}
ty::Bool
| ty::Char
@@ -343,17 +344,18 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-) -> Vec<ty::Clause<'tcx>> {
+) -> Vec<Goal<'tcx, ty::Predicate<'tcx>>> {
let tcx = ecx.tcx();
let mut requirements = vec![];
requirements.extend(
- tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.substs).predicates,
+ tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.args).predicates,
);
for item in tcx.associated_items(trait_ref.def_id).in_definition_order() {
// FIXME(associated_const_equality): Also add associated consts to
// the requirements here.
if item.kind == ty::AssocKind::Type {
- requirements.extend(tcx.item_bounds(item.def_id).subst_iter(tcx, trait_ref.substs));
+ requirements
+ .extend(tcx.item_bounds(item.def_id).iter_instantiated(tcx, trait_ref.args));
}
}
@@ -373,17 +375,22 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
}
}
- requirements.fold_with(&mut ReplaceProjectionWith {
- ecx,
- param_env,
- mapping: replace_projection_with,
- })
+ let mut folder =
+ ReplaceProjectionWith { ecx, param_env, mapping: replace_projection_with, nested: vec![] };
+ let folded_requirements = requirements.fold_with(&mut folder);
+
+ folder
+ .nested
+ .into_iter()
+ .chain(folded_requirements.into_iter().map(|clause| Goal::new(tcx, param_env, clause)))
+ .collect()
}
struct ReplaceProjectionWith<'a, 'tcx> {
ecx: &'a EvalCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
mapping: FxHashMap<DefId, ty::PolyProjectionPredicate<'tcx>>,
+ nested: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
@@ -399,13 +406,12 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
// but the where clauses we instantiated are not. We can solve this by instantiating
// the binder at the usage site.
let proj = self.ecx.instantiate_binder_with_infer(*replacement);
- // FIXME: Technically this folder could be fallible?
- let nested = self
- .ecx
- .eq_and_get_goals(self.param_env, alias_ty, proj.projection_ty)
- .expect("expected to be able to unify goal projection with dyn's projection");
- // FIXME: Technically we could register these too..
- assert!(nested.is_empty(), "did not expect unification to have any nested goals");
+ // FIXME: Technically this equate could be fallible...
+ self.nested.extend(
+ self.ecx
+ .eq_and_get_goals(self.param_env, alias_ty, proj.projection_ty)
+ .expect("expected to be able to unify goal projection with dyn's projection"),
+ );
proj.term.ty().unwrap()
} else {
ty.super_fold_with(self)
diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
index 255620489..a9d182abf 100644
--- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
@@ -125,9 +125,8 @@ impl<'a, 'tcx> Canonicalizer<'a, 'tcx> {
// - var_infos: [E0, U1, E1, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 6
// - var_infos: [E0, U1, E1, U1, E1, E2, U2], curr_compressed_uv: 2, next_orig_uv: -
//
- // This algorithm runs in `O(nm)` where `n` is the number of different universe
- // indices in the input and `m` is the number of canonical variables.
- // This should be fine as both `n` and `m` are expected to be small.
+ // This algorithm runs in `O(n²)` where `n` is the number of different universe
+ // indices in the input. This should be fine as `n` is expected to be small.
let mut curr_compressed_uv = ty::UniverseIndex::ROOT;
let mut existential_in_new_uv = false;
let mut next_orig_uv = Some(ty::UniverseIndex::ROOT);
@@ -208,23 +207,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
t
}
- fn fold_region(&mut self, mut r: ty::Region<'tcx>) -> ty::Region<'tcx> {
- match self.canonicalize_mode {
- CanonicalizeMode::Input => {
- // Don't resolve infer vars in input, since it affects
- // caching and may cause trait selection bugs which rely
- // on regions to be equal.
- }
- CanonicalizeMode::Response { .. } => {
- if let ty::ReVar(vid) = *r {
- r = self
- .infcx
- .inner
- .borrow_mut()
- .unwrap_region_constraints()
- .opportunistic_resolve_var(self.infcx.tcx, vid);
- }
- }
+ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ if let ty::ReVar(vid) = *r {
+ let resolved_region = self
+ .infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .opportunistic_resolve_var(self.infcx.tcx, vid);
+ assert_eq!(
+ r, resolved_region,
+ "region var should have been resolved, {r} -> {resolved_region}"
+ );
}
let kind = match *r {
@@ -263,50 +257,38 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
ty::ReError(_) => return r,
};
- let var = ty::BoundVar::from(
- self.variables.iter().position(|&v| v == r.into()).unwrap_or_else(|| {
- let var = self.variables.len();
- self.variables.push(r.into());
- self.primitive_var_infos.push(CanonicalVarInfo { kind });
- var
- }),
- );
+ let existing_bound_var = match self.canonicalize_mode {
+ CanonicalizeMode::Input => None,
+ CanonicalizeMode::Response { .. } => {
+ self.variables.iter().position(|&v| v == r.into()).map(ty::BoundVar::from)
+ }
+ };
+ let var = existing_bound_var.unwrap_or_else(|| {
+ let var = ty::BoundVar::from(self.variables.len());
+ self.variables.push(r.into());
+ self.primitive_var_infos.push(CanonicalVarInfo { kind });
+ var
+ });
let br = ty::BoundRegion { var, kind: BrAnon(None) };
ty::Region::new_late_bound(self.interner(), self.binder_index, br)
}
- fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
+ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
let kind = match *t.kind() {
- ty::Infer(ty::TyVar(mut vid)) => {
- // We need to canonicalize the *root* of our ty var.
- // This is so that our canonical response correctly reflects
- // any equated inference vars correctly!
- let root_vid = self.infcx.root_var(vid);
- if root_vid != vid {
- t = Ty::new_var(self.infcx.tcx, root_vid);
- vid = root_vid;
- }
-
- match self.infcx.probe_ty_var(vid) {
- Ok(t) => return self.fold_ty(t),
- Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
- }
+ ty::Infer(ty::TyVar(vid)) => {
+ assert_eq!(self.infcx.root_var(vid), vid, "ty vid should have been resolved");
+ let Err(ui) = self.infcx.probe_ty_var(vid) else {
+ bug!("ty var should have been resolved: {t}");
+ };
+ CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
}
ty::Infer(ty::IntVar(vid)) => {
- let nt = self.infcx.opportunistic_resolve_int_var(vid);
- if nt != t {
- return self.fold_ty(nt);
- } else {
- CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
- }
+ assert_eq!(self.infcx.opportunistic_resolve_int_var(vid), t);
+ CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
}
ty::Infer(ty::FloatVar(vid)) => {
- let nt = self.infcx.opportunistic_resolve_float_var(vid);
- if nt != t {
- return self.fold_ty(nt);
- } else {
- CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
- }
+ assert_eq!(self.infcx.opportunistic_resolve_float_var(vid), t);
+ CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
}
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("fresh var during canonicalization: {t:?}")
@@ -369,22 +351,19 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
Ty::new_bound(self.infcx.tcx, self.binder_index, bt)
}
- fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
let kind = match c.kind() {
- ty::ConstKind::Infer(ty::InferConst::Var(mut vid)) => {
- // We need to canonicalize the *root* of our const var.
- // This is so that our canonical response correctly reflects
- // any equated inference vars correctly!
- let root_vid = self.infcx.root_const_var(vid);
- if root_vid != vid {
- c = ty::Const::new_var(self.infcx.tcx, root_vid, c.ty());
- vid = root_vid;
- }
-
- match self.infcx.probe_const_var(vid) {
- Ok(c) => return self.fold_const(c),
- Err(universe) => CanonicalVarKind::Const(universe, c.ty()),
- }
+ ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
+ assert_eq!(
+ self.infcx.root_const_var(vid),
+ vid,
+ "const var should have been resolved"
+ );
+ let Err(ui) = self.infcx.probe_const_var(vid) else {
+ bug!("const var should have been resolved");
+ };
+ // FIXME: we should fold this ty eventually
+ CanonicalVarKind::Const(ui, c.ty())
}
ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => {
bug!("fresh var during canonicalization: {c:?}")
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 74dfbdddb..5c2cbe399 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -1,20 +1,21 @@
+use rustc_data_structures::stack::ensure_sufficient_stack;
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, RegionVariableOrigin,
- TyCtxtInferExt,
+ DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt,
};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::ObligationCause;
+use rustc_middle::infer::canonical::CanonicalVarInfos;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::traits::solve::inspect;
use rustc_middle::traits::solve::{
- CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, MaybeCause,
- PredefinedOpaques, PredefinedOpaquesData, QueryResult,
+ CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques,
+ PredefinedOpaquesData, QueryResult,
};
-use rustc_middle::traits::DefiningAnchor;
+use rustc_middle::traits::{specialization_graph, DefiningAnchor};
use rustc_middle::ty::{
self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor,
@@ -24,10 +25,10 @@ use rustc_span::DUMMY_SP;
use std::io::Write;
use std::ops::ControlFlow;
-use crate::traits::specialization_graph;
+use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
use super::inspect::ProofTreeBuilder;
-use super::search_graph::{self, OverflowHandler};
+use super::search_graph;
use super::SolverMode;
use super::{search_graph::SearchGraph, Goal};
pub use select::InferCtxtSelectExt;
@@ -54,6 +55,9 @@ pub struct EvalCtxt<'a, 'tcx> {
/// the job already.
infcx: &'a InferCtxt<'tcx>,
+ /// The variable info for the `var_values`, only used to make an ambiguous response
+ /// with no constraints.
+ variables: CanonicalVarInfos<'tcx>,
pub(super) var_values: CanonicalVarValues<'tcx>,
predefined_opaques_in_body: PredefinedOpaques<'tcx>,
@@ -116,7 +120,8 @@ impl NestedGoals<'_> {
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
pub enum GenerateProofTree {
Yes(UseGlobalCache),
- No,
+ IfEnabled,
+ Never,
}
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
@@ -169,6 +174,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
self.search_graph.solver_mode()
}
+ pub(super) fn local_overflow_limit(&self) -> usize {
+ self.search_graph.local_overflow_limit()
+ }
+
/// Creates a root evaluation context and search graph. This should only be
/// used from outside of any evaluation, and other methods should be preferred
/// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
@@ -182,18 +191,19 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let mut ecx = EvalCtxt {
search_graph: &mut search_graph,
- infcx: infcx,
+ infcx,
+ nested_goals: NestedGoals::new(),
+ inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
+
// Only relevant when canonicalizing the response,
// which we don't do within this evaluation context.
predefined_opaques_in_body: infcx
.tcx
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
- // Only relevant when canonicalizing the response.
max_input_universe: ty::UniverseIndex::ROOT,
+ variables: ty::List::empty(),
var_values: CanonicalVarValues::dummy(),
- nested_goals: NestedGoals::new(),
tainted: Ok(()),
- inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
};
let result = f(&mut ecx);
@@ -202,7 +212,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
(&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree)
{
let mut lock = std::io::stdout().lock();
- let _ = lock.write_fmt(format_args!("{tree:?}"));
+ let _ = lock.write_fmt(format_args!("{tree:?}\n"));
let _ = lock.flush();
}
@@ -243,6 +253,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let mut ecx = EvalCtxt {
infcx,
+ variables: canonical_input.variables,
var_values,
predefined_opaques_in_body: input.predefined_opaques_in_body,
max_input_universe: canonical_input.max_universe,
@@ -270,6 +281,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
// 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 {
+ // This seems ok, but fragile.
let _ = infcx.take_opaque_types();
}
@@ -297,24 +309,26 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
// Deal with overflow, caching, and coinduction.
//
// The actual solver logic happens in `ecx.compute_goal`.
- search_graph.with_new_goal(
- tcx,
- canonical_input,
- goal_evaluation,
- |search_graph, goal_evaluation| {
- EvalCtxt::enter_canonical(
- tcx,
- search_graph,
- canonical_input,
- goal_evaluation,
- |ecx, goal| {
- let result = ecx.compute_goal(goal);
- ecx.inspect.query_result(result);
- result
- },
- )
- },
- )
+ ensure_sufficient_stack(|| {
+ search_graph.with_new_goal(
+ tcx,
+ canonical_input,
+ goal_evaluation,
+ |search_graph, goal_evaluation| {
+ EvalCtxt::enter_canonical(
+ tcx,
+ search_graph,
+ canonical_input,
+ goal_evaluation,
+ |ecx, goal| {
+ let result = ecx.compute_goal(goal);
+ ecx.inspect.query_result(result);
+ result
+ },
+ )
+ },
+ )
+ })
}
/// Recursively evaluates `goal`, returning whether any inference vars have
@@ -326,6 +340,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack);
+ let encountered_overflow = self.search_graph.encountered_overflow();
let canonical_response = EvalCtxt::evaluate_canonical_goal(
self.tcx(),
self.search_graph,
@@ -341,7 +356,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
Ok(response) => response,
};
- let has_changed = !canonical_response.value.var_values.is_identity()
+ let has_changed = !canonical_response.value.var_values.is_identity_modulo_regions()
|| !canonical_response.value.external_constraints.opaque_types.is_empty();
let (certainty, nested_goals) = match self.instantiate_and_apply_query_response(
goal.param_env,
@@ -373,37 +388,60 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
&& is_normalizes_to_hack == IsNormalizesToHack::No
&& !self.search_graph.in_cycle()
{
- debug!("rerunning goal to check result is stable");
- let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
- let new_canonical_response = EvalCtxt::evaluate_canonical_goal(
- self.tcx(),
- self.search_graph,
- canonical_goal,
- // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal`
- &mut ProofTreeBuilder::new_noop(),
- )?;
- // We only check for modulo regions as we convert all regions in
- // the input to new existentials, even if they're expected to be
- // `'static` or a placeholder region.
- if !new_canonical_response.value.var_values.is_identity_modulo_regions() {
- bug!(
- "unstable result: re-canonicalized goal={canonical_goal:#?} \
- first_response={canonical_response:#?} \
- second_response={new_canonical_response:#?}"
- );
- }
- if certainty != new_canonical_response.value.certainty {
- bug!(
- "unstable certainty: {certainty:#?} re-canonicalized goal={canonical_goal:#?} \
- first_response={canonical_response:#?} \
- second_response={new_canonical_response:#?}"
- );
- }
+ // The nested evaluation has to happen with the original state
+ // of `encountered_overflow`.
+ let from_original_evaluation =
+ self.search_graph.reset_encountered_overflow(encountered_overflow);
+ self.check_evaluate_goal_stable_result(goal, canonical_goal, canonical_response);
+ // In case the evaluation was unstable, we manually make sure that this
+ // debug check does not influence the result of the parent goal.
+ self.search_graph.reset_encountered_overflow(from_original_evaluation);
}
Ok((has_changed, certainty, nested_goals))
}
+ fn check_evaluate_goal_stable_result(
+ &mut self,
+ goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ original_input: CanonicalInput<'tcx>,
+ original_result: CanonicalResponse<'tcx>,
+ ) {
+ let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
+ let result = EvalCtxt::evaluate_canonical_goal(
+ self.tcx(),
+ self.search_graph,
+ canonical_goal,
+ // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal`
+ &mut ProofTreeBuilder::new_noop(),
+ );
+
+ macro_rules! fail {
+ ($msg:expr) => {{
+ let msg = $msg;
+ warn!(
+ "unstable result: {msg}\n\
+ original goal: {original_input:?},\n\
+ original result: {original_result:?}\n\
+ re-canonicalized goal: {canonical_goal:?}\n\
+ second response: {result:?}"
+ );
+ return;
+ }};
+ }
+
+ let Ok(new_canonical_response) = result else { fail!("second response was error") };
+ // We only check for modulo regions as we convert all regions in
+ // the input to new existentials, even if they're expected to be
+ // `'static` or a placeholder region.
+ if !new_canonical_response.value.var_values.is_identity_modulo_regions() {
+ fail!("additional constraints from second response")
+ }
+ if original_result.value.certainty != new_canonical_response.value.certainty {
+ fail!("unstable certainty")
+ }
+ }
+
fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
let Goal { param_env, predicate } = goal;
let kind = predicate.kind();
@@ -430,11 +468,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
ty::PredicateKind::Coerce(predicate) => {
self.compute_coerce_goal(Goal { param_env, predicate })
}
- ty::PredicateKind::ClosureKind(def_id, substs, kind) => self
- .compute_closure_kind_goal(Goal {
- param_env,
- predicate: (def_id, substs, kind),
- }),
+ ty::PredicateKind::ClosureKind(def_id, args, kind) => self
+ .compute_closure_kind_goal(Goal { param_env, predicate: (def_id, args, kind) }),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
self.compute_object_safe_goal(trait_def_id)
}
@@ -471,101 +506,22 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let inspect = self.inspect.new_evaluate_added_goals();
let inspect = core::mem::replace(&mut self.inspect, inspect);
- let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
- let mut new_goals = NestedGoals::new();
-
- let response = self.repeat_while_none(
- |_| Ok(Certainty::Maybe(MaybeCause::Overflow)),
- |this| {
- this.inspect.evaluate_added_goals_loop_start();
-
- let mut has_changed = Err(Certainty::Yes);
-
- if let Some(goal) = goals.normalizes_to_hack_goal.take() {
- // Replace the goal with an unconstrained infer var, so the
- // RHS does not affect projection candidate assembly.
- let unconstrained_rhs = this.next_term_infer_of_kind(goal.predicate.term);
- let unconstrained_goal = goal.with(
- this.tcx(),
- ty::Binder::dummy(ty::ProjectionPredicate {
- projection_ty: goal.predicate.projection_ty,
- term: unconstrained_rhs,
- }),
- );
-
- let (_, certainty, instantiate_goals) =
- match this.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal) {
- Ok(r) => r,
- Err(NoSolution) => return Some(Err(NoSolution)),
- };
- new_goals.goals.extend(instantiate_goals);
-
- // Finally, equate the goal's RHS with the unconstrained var.
- // We put the nested goals from this into goals instead of
- // next_goals to avoid needing to process the loop one extra
- // time if this goal returns something -- I don't think this
- // matters in practice, though.
- match this.eq_and_get_goals(
- goal.param_env,
- goal.predicate.term,
- unconstrained_rhs,
- ) {
- Ok(eq_goals) => {
- goals.goals.extend(eq_goals);
- }
- Err(NoSolution) => return Some(Err(NoSolution)),
- };
-
- // We only look at the `projection_ty` part here rather than
- // looking at the "has changed" return from evaluate_goal,
- // because we expect the `unconstrained_rhs` part of the predicate
- // to have changed -- that means we actually normalized successfully!
- if goal.predicate.projection_ty
- != this.resolve_vars_if_possible(goal.predicate.projection_ty)
- {
- has_changed = Ok(())
- }
-
- match certainty {
- Certainty::Yes => {}
- Certainty::Maybe(_) => {
- // We need to resolve vars here so that we correctly
- // deal with `has_changed` in the next iteration.
- new_goals.normalizes_to_hack_goal =
- Some(this.resolve_vars_if_possible(goal));
- has_changed = has_changed.map_err(|c| c.unify_with(certainty));
- }
- }
+ let mut response = Ok(Certainty::OVERFLOW);
+ for _ in 0..self.local_overflow_limit() {
+ // FIXME: This match is a bit ugly, it might be nice to change the inspect
+ // stuff to use a closure instead. which should hopefully simplify this a bit.
+ match self.evaluate_added_goals_step() {
+ Ok(Some(cert)) => {
+ response = Ok(cert);
+ break;
}
-
- for goal in goals.goals.drain(..) {
- let (changed, certainty, instantiate_goals) =
- match this.evaluate_goal(IsNormalizesToHack::No, goal) {
- Ok(result) => result,
- Err(NoSolution) => return Some(Err(NoSolution)),
- };
- new_goals.goals.extend(instantiate_goals);
-
- if changed {
- has_changed = Ok(());
- }
-
- match certainty {
- Certainty::Yes => {}
- Certainty::Maybe(_) => {
- new_goals.goals.push(goal);
- has_changed = has_changed.map_err(|c| c.unify_with(certainty));
- }
- }
- }
-
- core::mem::swap(&mut new_goals, &mut goals);
- match has_changed {
- Ok(()) => None,
- Err(certainty) => Some(Ok(certainty)),
+ Ok(None) => {}
+ Err(NoSolution) => {
+ response = Err(NoSolution);
+ break;
}
- },
- );
+ }
+ }
self.inspect.eval_added_goals_result(response);
@@ -576,9 +532,84 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let goal_evaluations = std::mem::replace(&mut self.inspect, inspect);
self.inspect.added_goals_evaluation(goal_evaluations);
- self.nested_goals = goals;
response
}
+
+ /// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning.
+ ///
+ /// Goals for the next step get directly added the the nested goals of the `EvalCtxt`.
+ fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
+ let tcx = self.tcx();
+ let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
+
+ self.inspect.evaluate_added_goals_loop_start();
+ // If this loop did not result in any progress, what's our final certainty.
+ let mut unchanged_certainty = Some(Certainty::Yes);
+ if let Some(goal) = goals.normalizes_to_hack_goal.take() {
+ // Replace the goal with an unconstrained infer var, so the
+ // RHS does not affect projection candidate assembly.
+ let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
+ let unconstrained_goal = goal.with(
+ tcx,
+ ty::ProjectionPredicate {
+ projection_ty: goal.predicate.projection_ty,
+ term: unconstrained_rhs,
+ },
+ );
+
+ let (_, certainty, instantiate_goals) =
+ self.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal)?;
+ self.add_goals(instantiate_goals);
+
+ // Finally, equate the goal's RHS with the unconstrained var.
+ // We put the nested goals from this into goals instead of
+ // next_goals to avoid needing to process the loop one extra
+ // time if this goal returns something -- I don't think this
+ // matters in practice, though.
+ let eq_goals =
+ self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?;
+ goals.goals.extend(eq_goals);
+
+ // We only look at the `projection_ty` part here rather than
+ // looking at the "has changed" return from evaluate_goal,
+ // because we expect the `unconstrained_rhs` part of the predicate
+ // to have changed -- that means we actually normalized successfully!
+ if goal.predicate.projection_ty
+ != self.resolve_vars_if_possible(goal.predicate.projection_ty)
+ {
+ unchanged_certainty = None;
+ }
+
+ match certainty {
+ Certainty::Yes => {}
+ Certainty::Maybe(_) => {
+ // We need to resolve vars here so that we correctly
+ // deal with `has_changed` in the next iteration.
+ self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal));
+ unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
+ }
+ }
+ }
+
+ for goal in goals.goals.drain(..) {
+ let (has_changed, certainty, instantiate_goals) =
+ self.evaluate_goal(IsNormalizesToHack::No, goal)?;
+ self.add_goals(instantiate_goals);
+ if has_changed {
+ unchanged_certainty = None;
+ }
+
+ match certainty {
+ Certainty::Yes => {}
+ Certainty::Maybe(_) => {
+ self.add_goal(goal);
+ unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
+ }
+ }
+ }
+
+ Ok(unchanged_certainty)
+ }
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -593,10 +624,6 @@ 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,
@@ -774,24 +801,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
self.infcx.resolve_vars_if_possible(value)
}
- pub(super) fn fresh_substs_for_item(&self, def_id: DefId) -> ty::SubstsRef<'tcx> {
- self.infcx.fresh_substs_for_item(DUMMY_SP, def_id)
+ pub(super) fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> {
+ self.infcx.fresh_args_for_item(DUMMY_SP, def_id)
}
- pub(super) fn translate_substs(
+ pub(super) fn translate_args(
&self,
param_env: ty::ParamEnv<'tcx>,
source_impl: DefId,
- source_substs: ty::SubstsRef<'tcx>,
+ source_args: ty::GenericArgsRef<'tcx>,
target_node: specialization_graph::Node,
- ) -> ty::SubstsRef<'tcx> {
- crate::traits::translate_substs(
- self.infcx,
- param_env,
- source_impl,
- source_substs,
- target_node,
- )
+ ) -> ty::GenericArgsRef<'tcx> {
+ crate::traits::translate_args(self.infcx, param_env, source_impl, source_args, target_node)
}
pub(super) fn register_ty_outlives(&self, ty: Ty<'tcx>, lt: ty::Region<'tcx>) {
@@ -863,14 +884,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn add_item_bounds_for_hidden_type(
&mut self,
opaque_def_id: DefId,
- opaque_substs: ty::SubstsRef<'tcx>,
+ opaque_args: ty::GenericArgsRef<'tcx>,
param_env: ty::ParamEnv<'tcx>,
hidden_ty: Ty<'tcx>,
) {
let mut obligations = Vec::new();
self.infcx.add_item_bounds_for_hidden_type(
opaque_def_id,
- opaque_substs,
+ opaque_args,
ObligationCause::dummy(),
param_env,
hidden_ty,
@@ -896,13 +917,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
continue;
}
values.extend(self.probe_candidate("opaque type storage").enter(|ecx| {
- for (a, b) in std::iter::zip(candidate_key.substs, key.substs) {
+ for (a, b) in std::iter::zip(candidate_key.args, key.args) {
ecx.eq(param_env, a, b)?;
}
ecx.eq(param_env, candidate_ty, ty)?;
ecx.add_item_bounds_for_hidden_type(
candidate_key.def_id.to_def_id(),
- candidate_key.substs,
+ candidate_key.args,
param_env,
candidate_ty,
);
@@ -928,4 +949,39 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Err(ErrorHandled::TooGeneric) => None,
}
}
+
+ /// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor`
+ /// for every trait ref encountered (including the principal). Passes both the vtable
+ /// base and the (optional) vptr slot.
+ pub(super) fn walk_vtable(
+ &mut self,
+ principal: ty::PolyTraitRef<'tcx>,
+ mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option<usize>),
+ ) {
+ let tcx = self.tcx();
+ let mut offset = 0;
+ prepare_vtable_segments::<()>(tcx, principal, |segment| {
+ match segment {
+ VtblSegment::MetadataDSA => {
+ offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
+ }
+ VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+ let own_vtable_entries = count_own_vtable_entries(tcx, trait_ref);
+
+ supertrait_visitor(
+ self,
+ trait_ref,
+ offset,
+ emit_vptr.then(|| offset + own_vtable_entries),
+ );
+
+ offset += own_vtable_entries;
+ if emit_vptr {
+ offset += 1;
+ }
+ }
+ }
+ ControlFlow::Continue(())
+ });
+ }
}
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 637d45888..523841951 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -1,26 +1,30 @@
-/// Canonicalization is used to separate some goal from its context,
-/// throwing away unnecessary information in the process.
-///
-/// This is necessary to cache goals containing inference variables
-/// and placeholders without restricting them to the current `InferCtxt`.
-///
-/// Canonicalization is fairly involved, for more details see the relevant
-/// section of the [rustc-dev-guide][c].
-///
-/// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
+//! Canonicalization is used to separate some goal from its context,
+//! throwing away unnecessary information in the process.
+//!
+//! This is necessary to cache goals containing inference variables
+//! and placeholders without restricting them to the current `InferCtxt`.
+//!
+//! Canonicalization is fairly involved, for more details see the relevant
+//! section of the [rustc-dev-guide][c].
+//!
+//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer};
-use crate::solve::{CanonicalResponse, QueryResult, Response};
+use crate::solve::{response_no_constraints_raw, CanonicalResponse, QueryResult, Response};
use rustc_data_structures::fx::FxHashSet;
use rustc_index::IndexVec;
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
+use rustc_infer::infer::InferCtxt;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{
- ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
+ ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
+};
+use rustc_middle::ty::{
+ self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
+ TypeVisitableExt,
};
-use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
use rustc_span::DUMMY_SP;
use std::iter;
use std::ops::Deref;
@@ -32,6 +36,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
&self,
goal: Goal<'tcx, T>,
) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
+ let opaque_types = self.infcx.clone_opaque_types_for_query_response();
+ let (goal, opaque_types) =
+ (goal, opaque_types).fold_with(&mut EagerResolver { infcx: self.infcx });
+
let mut orig_values = Default::default();
let canonical_goal = Canonicalizer::canonicalize(
self.infcx,
@@ -40,11 +48,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
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(),
- },
- ),
+ predefined_opaques_in_body: self
+ .tcx()
+ .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }),
},
);
(orig_values, canonical_goal)
@@ -70,34 +76,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
);
let certainty = certainty.unify_with(goals_certainty);
+ if let Certainty::OVERFLOW = certainty {
+ // 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 = 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 var_values = self.var_values;
+ let external_constraints = self.compute_external_query_constraints()?;
+
+ let (var_values, mut external_constraints) =
+ (var_values, external_constraints).fold_with(&mut EagerResolver { infcx: self.infcx });
+ // Remove any trivial region constraints once we've resolved regions
+ external_constraints
+ .region_constraints
+ .outlives
+ .retain(|(outlives, _)| outlives.0.as_region().map_or(true, |re| re != outlives.1));
let canonical = Canonicalizer::canonicalize(
self.infcx,
CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
&mut Default::default(),
- response,
+ Response {
+ var_values,
+ certainty,
+ external_constraints: self.tcx().mk_external_constraints(external_constraints),
+ },
);
+
Ok(canonical)
}
@@ -109,34 +124,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
&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,
+ response_no_constraints_raw(
+ self.tcx(),
+ self.max_input_universe,
+ self.variables,
+ Certainty::Maybe(maybe_cause),
)
}
+ /// Computes the region constraints and *new* opaque types registered when
+ /// proving a goal.
+ ///
+ /// If an opaque was already constrained before proving this goal, then the
+ /// external constraints do not need to record that opaque, since if it is
+ /// further constrained by inference, that will be passed back in the var
+ /// values.
#[instrument(level = "debug", skip(self), ret)]
- fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
+ fn compute_external_query_constraints(
+ &self,
+ ) -> Result<ExternalConstraintsData<'tcx>, NoSolution> {
// We only check for leaks from universes which were entered inside
// of the query.
self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
@@ -166,9 +172,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
});
- Ok(self
- .tcx()
- .mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types }))
+ Ok(ExternalConstraintsData { region_constraints, opaque_types })
}
/// After calling a canonical query, we apply the constraints returned
@@ -211,7 +215,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// created inside of the query directly instead of returning them to the
// caller.
let prev_universe = self.infcx.universe();
- let universes_created_in_query = response.max_universe.index() + 1;
+ let universes_created_in_query = response.max_universe.index();
for _ in 0..universes_created_in_query {
self.infcx.create_next_universe();
}
@@ -250,7 +254,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}
- let var_values = self.tcx().mk_substs_from_iter(response.variables.iter().enumerate().map(
+ let var_values = self.tcx().mk_args_from_iter(response.variables.iter().enumerate().map(
|(index, info)| {
if info.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
@@ -326,3 +330,65 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Ok(())
}
}
+
+/// Resolves ty, region, and const vars to their inferred values or their root vars.
+struct EagerResolver<'a, 'tcx> {
+ infcx: &'a InferCtxt<'tcx>,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerResolver<'_, 'tcx> {
+ fn interner(&self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
+ }
+
+ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+ match *t.kind() {
+ ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) {
+ Ok(t) => t.fold_with(self),
+ Err(_) => Ty::new_var(self.infcx.tcx, self.infcx.root_var(vid)),
+ },
+ ty::Infer(ty::IntVar(vid)) => self.infcx.opportunistic_resolve_int_var(vid),
+ ty::Infer(ty::FloatVar(vid)) => self.infcx.opportunistic_resolve_float_var(vid),
+ _ => {
+ if t.has_infer() {
+ t.super_fold_with(self)
+ } else {
+ t
+ }
+ }
+ }
+ }
+
+ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ match *r {
+ ty::ReVar(vid) => self
+ .infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .opportunistic_resolve_var(self.infcx.tcx, vid),
+ _ => r,
+ }
+ }
+
+ fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ match c.kind() {
+ ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
+ // FIXME: we need to fold the ty too, I think.
+ match self.infcx.probe_const_var(vid) {
+ Ok(c) => c.fold_with(self),
+ Err(_) => {
+ ty::Const::new_var(self.infcx.tcx, self.infcx.root_const_var(vid), c.ty())
+ }
+ }
+ }
+ _ => {
+ if c.has_infer() {
+ c.super_fold_with(self)
+ } else {
+ c
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
index 4477ea7d5..317c43baf 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -17,6 +17,7 @@ where
let mut nested_ecx = EvalCtxt {
infcx: outer_ecx.infcx,
+ variables: outer_ecx.variables,
var_values: outer_ecx.var_values,
predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
max_input_universe: outer_ecx.max_input_universe,
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
index bf6cbef8c..42d7a587c 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -1,24 +1,21 @@
-use std::ops::ControlFlow;
-
+use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
-use rustc_infer::traits::util::supertraits;
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt};
use rustc_infer::traits::{
- Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult,
+ Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine,
};
use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
use rustc_middle::traits::{
- ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
- ObligationCause, SelectionError,
+ BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError,
};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::DUMMY_SP;
-use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
+use crate::solve::assembly::{Candidate, CandidateSource};
use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
use crate::solve::inspect::ProofTreeBuilder;
-use crate::solve::search_graph::OverflowHandler;
-use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
+use crate::traits::StructurallyNormalizeExt;
+use crate::traits::TraitEngineExt;
pub trait InferCtxtSelectExt<'tcx> {
fn select_in_new_trait_solver(
@@ -40,7 +37,7 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
self.instantiate_binder_with_placeholders(obligation.predicate),
);
- let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::No, |ecx| {
+ let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::Never, |ecx| {
let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
@@ -102,32 +99,34 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
rematch_impl(self, goal, def_id, nested_obligations)
}
- // Rematching the dyn upcast or object goal will instantiate the same nested
- // goals that would have caused the ambiguity, so we can still make progress here
- // regardless.
- // FIXME: This doesn't actually check the object bounds hold here.
- (
- _,
- CandidateSource::BuiltinImpl(
- BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting,
- ),
- ) => rematch_object(self, goal, nested_obligations),
+ // If an unsize goal is ambiguous, then we can manually rematch it to make
+ // selection progress for coercion during HIR typeck. If it is *not* ambiguous,
+ // but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals,
+ // and we need to rematch those to detect tuple unsizing and trait upcasting.
+ // FIXME: This will be wrong if we have param-env or where-clause bounds
+ // with the unsize goal -- we may need to mark those with different impl
+ // sources.
+ (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
+ | (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc))
+ if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
+ {
+ rematch_unsize(self, goal, nested_obligations, src, certainty)
+ }
// Technically some builtin impls have nested obligations, but if
// `Certainty::Yes`, then they should've all been verified and don't
// need re-checking.
- (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
- Ok(Some(ImplSource::Builtin(nested_obligations)))
+ (Certainty::Yes, CandidateSource::BuiltinImpl(src)) => {
+ Ok(Some(ImplSource::Builtin(src, nested_obligations)))
}
// It's fine not to do anything to rematch these, since there are no
// nested obligations.
(Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
- Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst)))
+ Ok(Some(ImplSource::Param(nested_obligations)))
}
- (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity))
- | (Certainty::Maybe(_), _) => Ok(None),
+ (Certainty::Maybe(_), _) => Ok(None),
}
}
}
@@ -143,7 +142,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// the cycle anyways one step later.
EvalCtxt::enter_canonical(
self.tcx(),
- self.search_graph(),
+ self.search_graph,
canonical_input,
// FIXME: This is wrong, idk if we even want to track stuff here.
&mut ProofTreeBuilder::new_noop(),
@@ -174,11 +173,12 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>(
}
(_, CandidateSource::ParamEnv(_)) => true,
+ // FIXME: we could prefer earlier vtable bases perhaps...
(
- CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
- CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+ CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
+ CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
) => false,
- (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true,
+ (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true,
(CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
tcx.specializes((other_def_id, victim_def_id))
@@ -195,8 +195,9 @@ fn rematch_impl<'tcx>(
impl_def_id: DefId,
mut nested: Vec<PredicateObligation<'tcx>>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
- let substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
- let impl_trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap().subst(infcx.tcx, substs);
+ let args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
+ let impl_trait_ref =
+ infcx.tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(infcx.tcx, args);
nested.extend(
infcx
@@ -207,101 +208,191 @@ fn rematch_impl<'tcx>(
);
nested.extend(
- infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, substs).into_iter().map(
+ infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, args).into_iter().map(
|(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred),
),
);
- Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, substs, nested })))
+ Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested })))
}
-fn rematch_object<'tcx>(
+/// The `Unsize` trait is particularly important to coercion, so we try rematch it.
+/// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait
+/// goal assembly in the solver, both for soundness and in order to avoid ICEs.
+fn rematch_unsize<'tcx>(
infcx: &InferCtxt<'tcx>,
goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
mut nested: Vec<PredicateObligation<'tcx>>,
+ source: BuiltinImplSource,
+ certainty: Certainty,
) -> SelectionResult<'tcx, Selection<'tcx>> {
- let self_ty = goal.predicate.self_ty();
- let ty::Dynamic(data, _, source_kind) = *self_ty.kind()
- else {
- bug!()
- };
- let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, self_ty);
-
- let (is_upcasting, target_trait_ref_unnormalized) = if Some(goal.predicate.def_id())
- == infcx.tcx.lang_items().unsize_trait()
- {
- assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*");
- if let ty::Dynamic(data, _, ty::Dyn) = goal.predicate.trait_ref.substs.type_at(1).kind() {
- (true, data.principal().unwrap().with_self_ty(infcx.tcx, self_ty))
- } else {
- bug!()
- }
- } else {
- (false, ty::Binder::dummy(goal.predicate.trait_ref))
- };
-
- let mut target_trait_ref = None;
- for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) {
- let result = infcx.commit_if_ok(|_| {
- infcx.at(&ObligationCause::dummy(), goal.param_env).eq(
- DefineOpaqueTypes::No,
- target_trait_ref_unnormalized,
- candidate_trait_ref,
- )
-
- // FIXME: We probably should at least shallowly verify these...
- });
+ let tcx = infcx.tcx;
+ let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
+ let b_ty = structurally_normalize(
+ goal.predicate.trait_ref.args.type_at(1),
+ infcx,
+ goal.param_env,
+ &mut nested,
+ );
- match result {
- Ok(InferOk { value: (), obligations }) => {
- target_trait_ref = Some(candidate_trait_ref);
- nested.extend(obligations);
- break;
+ match (a_ty.kind(), b_ty.kind()) {
+ // Don't try to coerce `?0` to `dyn Trait`
+ (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => Ok(None),
+ // Stall any ambiguous upcasting goals, since we can't rematch those
+ (ty::Dynamic(_, _, ty::Dyn), ty::Dynamic(_, _, ty::Dyn)) => match certainty {
+ Certainty::Yes => Ok(Some(ImplSource::Builtin(source, nested))),
+ _ => Ok(None),
+ },
+ // `T` -> `dyn Trait` upcasting
+ (_, &ty::Dynamic(data, region, ty::Dyn)) => {
+ // Check that the type implements all of the predicates of the def-id.
+ // (i.e. the principal, all of the associated types match, and any auto traits)
+ nested.extend(data.iter().map(|pred| {
+ Obligation::new(
+ infcx.tcx,
+ ObligationCause::dummy(),
+ goal.param_env,
+ pred.with_self_ty(tcx, a_ty),
+ )
+ }));
+ // The type must be Sized to be unsized.
+ let sized_def_id = tcx.require_lang_item(hir::LangItem::Sized, None);
+ nested.push(Obligation::new(
+ infcx.tcx,
+ ObligationCause::dummy(),
+ goal.param_env,
+ ty::TraitRef::new(tcx, sized_def_id, [a_ty]),
+ ));
+ // The type must outlive the lifetime of the `dyn` we're unsizing into.
+ nested.push(Obligation::new(
+ infcx.tcx,
+ ObligationCause::dummy(),
+ goal.param_env,
+ ty::OutlivesPredicate(a_ty, region),
+ ));
+
+ Ok(Some(ImplSource::Builtin(source, nested)))
+ }
+ // `[T; n]` -> `[T]` unsizing
+ (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
+ nested.extend(
+ infcx
+ .at(&ObligationCause::dummy(), goal.param_env)
+ .eq(DefineOpaqueTypes::No, a_elem_ty, b_elem_ty)
+ .expect("expected rematch to succeed")
+ .into_obligations(),
+ );
+
+ Ok(Some(ImplSource::Builtin(source, nested)))
+ }
+ // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
+ (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
+ if a_def.is_struct() && a_def.did() == b_def.did() =>
+ {
+ let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
+ // We must be unsizing some type parameters. This also implies
+ // that the struct has a tail field.
+ if unsizing_params.is_empty() {
+ bug!("expected rematch to succeed")
}
- Err(_) => continue,
+
+ let tail_field = a_def
+ .non_enum_variant()
+ .fields
+ .raw
+ .last()
+ .expect("expected unsized ADT to have a tail field");
+ let tail_field_ty = tcx.type_of(tail_field.did);
+
+ let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
+ let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
+
+ // Substitute just the unsizing params from B into A. The type after
+ // this substitution must be equal to B. This is so we don't unsize
+ // unrelated type parameters.
+ let new_a_args = tcx.mk_args_from_iter(
+ a_args
+ .iter()
+ .enumerate()
+ .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }),
+ );
+ let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args);
+
+ nested.extend(
+ infcx
+ .at(&ObligationCause::dummy(), goal.param_env)
+ .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty)
+ .expect("expected rematch to succeed")
+ .into_obligations(),
+ );
+
+ // Finally, we require that `TailA: Unsize<TailB>` for the tail field
+ // types.
+ nested.push(Obligation::new(
+ tcx,
+ ObligationCause::dummy(),
+ goal.param_env,
+ ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
+ ));
+
+ Ok(Some(ImplSource::Builtin(source, nested)))
+ }
+ // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
+ (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
+ if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
+ {
+ let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
+ let b_last_ty = b_tys.last().unwrap();
+
+ // Substitute just the tail field of B., and require that they're equal.
+ let unsized_a_ty =
+ Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
+ nested.extend(
+ infcx
+ .at(&ObligationCause::dummy(), goal.param_env)
+ .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty)
+ .expect("expected rematch to succeed")
+ .into_obligations(),
+ );
+
+ // Similar to ADTs, require that we can unsize the tail.
+ nested.push(Obligation::new(
+ tcx,
+ ObligationCause::dummy(),
+ goal.param_env,
+ ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
+ ));
+
+ // We need to be able to detect tuple unsizing to require its feature gate.
+ assert_eq!(
+ source,
+ BuiltinImplSource::TupleUnsizing,
+ "compiler-errors wants to know if this can ever be triggered..."
+ );
+ Ok(Some(ImplSource::Builtin(source, nested)))
+ }
+ _ => {
+ assert_ne!(certainty, Certainty::Yes);
+ Ok(None)
}
}
+}
- let target_trait_ref = target_trait_ref.unwrap();
-
- let mut offset = 0;
- let Some((vtable_base, vtable_vptr_slot)) =
- prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| {
- match segment {
- VtblSegment::MetadataDSA => {
- offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
- }
- VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
- let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref);
-
- if trait_ref == target_trait_ref {
- if emit_vptr {
- return ControlFlow::Break((
- offset,
- Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)),
- ));
- } else {
- return ControlFlow::Break((offset, None));
- }
- }
-
- offset += own_vtable_entries;
- if emit_vptr {
- offset += 1;
- }
- }
- }
- ControlFlow::Continue(())
- })
- else {
- bug!();
- };
-
- // If we're upcasting, get the offset of the vtable pointer, otherwise get
- // the base of the vtable.
- Ok(Some(if is_upcasting {
- ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
+fn structurally_normalize<'tcx>(
+ ty: Ty<'tcx>,
+ infcx: &InferCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ nested: &mut Vec<PredicateObligation<'tcx>>,
+) -> Ty<'tcx> {
+ if matches!(ty.kind(), ty::Alias(..)) {
+ let mut engine = <dyn TraitEngine<'tcx>>::new(infcx);
+ let normalized_ty = infcx
+ .at(&ObligationCause::dummy(), param_env)
+ .structurally_normalize(ty, &mut *engine)
+ .expect("normalization shouldn't fail if we got to here");
+ nested.extend(engine.pending_obligations());
+ normalized_ty
} else {
- ImplSource::Object(ImplSourceObjectData { vtable_base, nested })
- }))
+ ty
+ }
}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 88ee14c4d..f1d309122 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -57,7 +57,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
.map(|obligation| {
let code = infcx.probe(|_| {
match infcx
- .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::No)
+ .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::IfEnabled)
.0
{
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => {
@@ -96,7 +96,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
for obligation in mem::take(&mut self.obligations) {
let goal = obligation.clone().into();
let (changed, certainty, nested_goals) =
- match infcx.evaluate_root_goal(goal, GenerateProofTree::No).0 {
+ match infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0 {
Ok(result) => result,
Err(NoSolution) => {
errors.push(FulfillmentError {
diff --git a/compiler/rustc_trait_selection/src/solve/inherent_projection.rs b/compiler/rustc_trait_selection/src/solve/inherent_projection.rs
new file mode 100644
index 000000000..28fe59b7f
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inherent_projection.rs
@@ -0,0 +1,50 @@
+//! Computes a normalizes-to (projection) goal for inherent associated types,
+//! `#![feature(inherent_associated_type)]`. Since astconv already determines
+//! which impl the IAT is being projected from, we just:
+//! 1. instantiate substs,
+//! 2. equate the self type, and
+//! 3. instantiate and register where clauses.
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+use super::EvalCtxt;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ pub(super) fn normalize_inherent_associated_type(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let inherent = goal.predicate.projection_ty;
+ let expected = goal.predicate.term.ty().expect("inherent consts are treated separately");
+
+ let impl_def_id = tcx.parent(inherent.def_id);
+ let impl_substs = self.fresh_args_for_item(impl_def_id);
+
+ // Equate impl header and add impl where clauses
+ self.eq(
+ goal.param_env,
+ inherent.self_ty(),
+ tcx.type_of(impl_def_id).instantiate(tcx, impl_substs),
+ )?;
+
+ // Equate IAT with the RHS of the project goal
+ let inherent_substs = inherent.rebase_inherent_args_onto_impl(impl_substs, tcx);
+ self.eq(
+ goal.param_env,
+ expected,
+ tcx.type_of(inherent.def_id).instantiate(tcx, inherent_substs),
+ )
+ .expect("expected goal term to be fully unconstrained");
+
+ // Check both where clauses on the impl and IAT
+ self.add_goals(
+ tcx.predicates_of(inherent.def_id)
+ .instantiate(tcx, inherent_substs)
+ .into_iter()
+ .map(|(pred, _)| goal.with(tcx, pred)),
+ );
+
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
index 2d6717fda..cda683963 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -200,30 +200,23 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
generate_proof_tree: GenerateProofTree,
) -> ProofTreeBuilder<'tcx> {
- let generate_proof_tree = match (
- tcx.sess.opts.unstable_opts.dump_solver_proof_tree,
- tcx.sess.opts.unstable_opts.dump_solver_proof_tree_use_cache,
- generate_proof_tree,
- ) {
- (_, Some(use_cache), GenerateProofTree::Yes(_)) => {
- GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
- }
-
- (DumpSolverProofTree::Always, use_cache, GenerateProofTree::No) => {
- let use_cache = use_cache.unwrap_or(true);
- GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
- }
-
- (_, None, GenerateProofTree::Yes(_)) => generate_proof_tree,
- (DumpSolverProofTree::Never, _, _) => generate_proof_tree,
- (DumpSolverProofTree::OnError, _, _) => generate_proof_tree,
- };
-
match generate_proof_tree {
- GenerateProofTree::No => ProofTreeBuilder::new_noop(),
- GenerateProofTree::Yes(global_cache_disabled) => {
- ProofTreeBuilder::new_root(global_cache_disabled)
+ GenerateProofTree::Never => ProofTreeBuilder::new_noop(),
+ GenerateProofTree::IfEnabled => {
+ let opts = &tcx.sess.opts.unstable_opts;
+ match opts.dump_solver_proof_tree {
+ DumpSolverProofTree::Always => {
+ let use_cache = opts.dump_solver_proof_tree_use_cache.unwrap_or(true);
+ ProofTreeBuilder::new_root(UseGlobalCache::from_bool(use_cache))
+ }
+ // `OnError` is handled by reevaluating goals in error
+ // reporting with `GenerateProofTree::Yes`.
+ DumpSolverProofTree::OnError | DumpSolverProofTree::Never => {
+ ProofTreeBuilder::new_noop()
+ }
+ }
}
+ GenerateProofTree::Yes(use_cache) => ProofTreeBuilder::new_root(use_cache),
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 77809d8d2..75a99f799 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -1,21 +1,27 @@
-//! The new trait solver, currently still WIP.
+//! The next-generation trait solver, currently still WIP.
//!
-//! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to
-//! interact with this solver.
+//! As a user of rust, you can use `-Ztrait-solver=next` or `next-coherence`
+//! to enable the new trait solver always, or just within coherence, respectively.
+//!
+//! As a developer of rustc, you shouldn't be using the new trait
+//! solver without asking the trait-system-refactor-initiative, but it can
+//! be enabled with `InferCtxtBuilder::with_next_trait_solver`. This will
+//! ensure that trait solving using that inference context will be routed
+//! to the new trait solver.
//!
//! For a high-level overview of how this solver works, check out the relevant
//! section of the rustc-dev-guide.
//!
//! FIXME(@lcnr): Write that section. If you read this before then ask me
//! about it on zulip.
-
use rustc_hir::def_id::DefId;
use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
use rustc_infer::traits::query::NoSolution;
+use rustc_middle::infer::canonical::CanonicalVarInfos;
use rustc_middle::traits::solve::{
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult, Response,
};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex};
use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
};
@@ -25,6 +31,7 @@ mod assembly;
mod canonicalize;
mod eval_ctxt;
mod fulfill;
+mod inherent_projection;
pub mod inspect;
mod normalize;
mod opaques;
@@ -37,7 +44,7 @@ pub use eval_ctxt::{
EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
};
pub use fulfill::FulfillmentCtxt;
-pub(crate) use normalize::deeply_normalize;
+pub(crate) use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
#[derive(Debug, Clone, Copy)]
enum SolverMode {
@@ -123,10 +130,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
#[instrument(level = "debug", skip(self))]
fn compute_closure_kind_goal(
&mut self,
- goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>,
+ goal: Goal<'tcx, (DefId, ty::GenericArgsRef<'tcx>, ty::ClosureKind)>,
) -> QueryResult<'tcx> {
- let (_, substs, expected_kind) = goal.predicate;
- let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind();
+ let (_, args, expected_kind) = goal.predicate;
+ let found_kind = args.as_closure().kind_ty().to_opt_closure_kind();
let Some(found_kind) = found_kind else {
return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
@@ -266,33 +273,64 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
return Err(NoSolution);
}
- let Certainty::Maybe(maybe_cause) = responses.iter().fold(
- Certainty::AMBIGUOUS,
- |certainty, response| {
+ let Certainty::Maybe(maybe_cause) =
+ responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
certainty.unify_with(response.value.certainty)
- },
- ) else {
+ })
+ else {
bug!("expected flounder response to be ambiguous")
};
Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
}
+
+ /// Normalize a type when it is structually matched on.
+ ///
+ /// For self types this is generally already handled through
+ /// `assemble_candidates_after_normalizing_self_ty`, so anything happening
+ /// in [`EvalCtxt::assemble_candidates_via_self_ty`] does not have to normalize
+ /// the self type. It is required when structurally matching on any other
+ /// arguments of a trait goal, e.g. when assembling builtin unsize candidates.
+ fn try_normalize_ty(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ mut ty: Ty<'tcx>,
+ ) -> Result<Option<Ty<'tcx>>, NoSolution> {
+ for _ in 0..self.local_overflow_limit() {
+ let ty::Alias(_, projection_ty) = *ty.kind() else {
+ return Ok(Some(ty));
+ };
+
+ let normalized_ty = self.next_ty_infer();
+ let normalizes_to_goal = Goal::new(
+ self.tcx(),
+ param_env,
+ ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
+ );
+ self.add_goal(normalizes_to_goal);
+ self.try_evaluate_added_goals()?;
+ ty = self.resolve_vars_if_possible(normalized_ty);
+ }
+
+ Ok(None)
+ }
}
-pub(super) fn response_no_constraints<'tcx>(
+fn response_no_constraints_raw<'tcx>(
tcx: TyCtxt<'tcx>,
- goal: Canonical<'tcx, impl Sized>,
+ max_universe: UniverseIndex,
+ variables: CanonicalVarInfos<'tcx>,
certainty: Certainty,
-) -> QueryResult<'tcx> {
- Ok(Canonical {
- max_universe: goal.max_universe,
- variables: goal.variables,
+) -> CanonicalResponse<'tcx> {
+ Canonical {
+ max_universe,
+ variables,
value: Response {
- var_values: CanonicalVarValues::make_identity(tcx, goal.variables),
+ var_values: CanonicalVarValues::make_identity(tcx, variables),
// FIXME: maybe we should store the "no response" version in tcx, like
// we do for tcx.types and stuff.
external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
certainty,
},
- })
+ }
}
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index c388850d8..872f0c879 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -20,8 +20,23 @@ pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
at: At<'_, 'tcx>,
value: T,
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+ assert!(!value.has_escaping_bound_vars());
+ deeply_normalize_with_skipped_universes(at, value, vec![])
+}
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+///
+/// Additionally takes a list of universes which represents the binders which have been
+/// entered before passing `value` to the function. This is currently needed for
+/// `normalize_erasing_regions`, which skips binders as it walks through a type.
+pub(crate) fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
+ at: At<'_, 'tcx>,
+ value: T,
+ universes: Vec<Option<UniverseIndex>>,
+) -> Result<T, Vec<FulfillmentError<'tcx>>> {
let fulfill_cx = FulfillmentCtxt::new(at.infcx);
- let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes: Vec::new() };
+ let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes };
value.try_fold_with(&mut folder)
}
@@ -60,10 +75,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
tcx,
self.at.cause.clone(),
self.at.param_env,
- ty::Binder::dummy(ty::ProjectionPredicate {
- projection_ty: alias,
- term: new_infer_ty.into(),
- }),
+ ty::ProjectionPredicate { projection_ty: alias, term: new_infer_ty.into() },
);
// Do not emit an error if normalization is known to fail but instead
@@ -116,10 +128,10 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
tcx,
self.at.cause.clone(),
self.at.param_env,
- ty::Binder::dummy(ty::ProjectionPredicate {
- projection_ty: tcx.mk_alias_ty(uv.def, uv.substs),
+ ty::ProjectionPredicate {
+ projection_ty: tcx.mk_alias_ty(uv.def, uv.args),
term: new_infer_ct.into(),
- }),
+ },
);
let result = if infcx.predicate_may_hold(&obligation) {
@@ -180,7 +192,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
mapped_regions,
mapped_types,
mapped_consts,
- &mut self.universes,
+ &self.universes,
result,
))
} else {
@@ -210,7 +222,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
mapped_regions,
mapped_types,
mapped_consts,
- &mut self.universes,
+ &self.universes,
result,
))
} else {
diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs
index 16194f5ad..f08adc020 100644
--- a/compiler/rustc_trait_selection/src/solve/opaques.rs
+++ b/compiler/rustc_trait_selection/src/solve/opaques.rs
@@ -1,3 +1,6 @@
+//! Computes a normalizes-to (projection) goal for opaque types. This goal
+//! behaves differently depending on the param-env's reveal mode and whether
+//! the opaque is in a defining scope.
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
use rustc_middle::traits::Reveal;
@@ -26,8 +29,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
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) {
+ // FIXME: This may have issues when the args contain aliases...
+ match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.args) {
Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
return self.evaluate_added_goals_and_make_canonical_response(
Certainty::AMBIGUOUS,
@@ -40,7 +43,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
// Prefer opaques registered already.
let opaque_type_key =
- ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs };
+ ty::OpaqueTypeKey { def_id: opaque_ty_def_id, args: opaque_ty.args };
let matches =
self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected);
if !matches.is_empty() {
@@ -54,7 +57,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
self.add_item_bounds_for_hidden_type(
opaque_ty.def_id,
- opaque_ty.substs,
+ opaque_ty.args,
goal.param_env,
expected,
);
@@ -65,7 +68,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// e.g. assigning `impl Copy := NotCopy`
self.add_item_bounds_for_hidden_type(
opaque_ty.def_id,
- opaque_ty.substs,
+ opaque_ty.args,
goal.param_env,
expected,
);
@@ -73,7 +76,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
(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);
+ let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, opaque_ty.args);
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 e53b784a7..e47e22877 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -1,8 +1,7 @@
-use crate::traits::specialization_graph;
+use crate::traits::{check_args_compatible, specialization_graph};
use super::assembly::{self, structural_traits};
use super::EvalCtxt;
-use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::LangItem;
@@ -11,11 +10,12 @@ use rustc_infer::traits::specialization_graph::LeafDef;
use rustc_infer::traits::Reveal;
use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
+use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::ProjectionPredicate;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
-use rustc_span::{sym, DUMMY_SP};
+use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};
impl<'tcx> EvalCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self), ret)]
@@ -48,7 +48,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
self.merge_candidates(candidates)
}
ty::AssocItemContainer::ImplContainer => {
- bug!("IATs not supported here yet")
+ self.normalize_inherent_associated_type(goal)
}
}
} else {
@@ -58,7 +58,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
DefKind::AnonConst => self.normalize_anon_const(goal),
DefKind::OpaqueTy => self.normalize_opaque_type(goal),
- DefKind::TyAlias => self.normalize_weak_type(goal),
+ DefKind::TyAlias { .. } => self.normalize_weak_type(goal),
kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
}
}
@@ -72,7 +72,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
goal.param_env,
ty::UnevaluatedConst::new(
goal.predicate.projection_ty.def_id,
- goal.predicate.projection_ty.substs,
+ goal.predicate.projection_ty.args,
),
self.tcx()
.type_of(goal.predicate.projection_ty.def_id)
@@ -112,6 +112,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
) -> QueryResult<'tcx> {
if let Some(projection_pred) = assumption.as_projection_clause() {
if projection_pred.projection_def_id() == goal.predicate.def_id() {
+ let tcx = ecx.tcx();
ecx.probe_candidate("assumption").enter(|ecx| {
let assumption_projection_pred =
ecx.instantiate_binder_with_infer(projection_pred);
@@ -122,6 +123,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
)?;
ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
.expect("expected goal term to be fully unconstrained");
+
+ // Add GAT where clauses from the trait's definition
+ ecx.add_goals(
+ tcx.predicates_of(goal.predicate.def_id())
+ .instantiate_own(tcx, goal.predicate.projection_ty.args)
+ .map(|(pred, _)| goal.with(tcx, pred)),
+ );
+
then(ecx)
})
} else {
@@ -142,93 +151,116 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx);
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
- if !drcx.substs_refs_may_unify(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs) {
+ if !drcx.args_refs_may_unify(goal_trait_ref.args, impl_trait_ref.skip_binder().args) {
return Err(NoSolution);
}
- ecx.probe(
- |r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter(
- |ecx| {
- let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
- let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
-
- ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
-
- let where_clause_bounds = tcx
- .predicates_of(impl_def_id)
- .instantiate(tcx, impl_substs)
- .predicates
- .into_iter()
- .map(|pred| goal.with(tcx, pred));
- ecx.add_goals(where_clause_bounds);
-
- // In case the associated item is hidden due to specialization, we have to
- // return ambiguity this would otherwise be incomplete, resulting in
- // unsoundness during coherence (#105782).
- let Some(assoc_def) = fetch_eligible_assoc_item_def(
- ecx,
- goal.param_env,
- goal_trait_ref,
- goal.predicate.def_id(),
- impl_def_id
- )? else {
- return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ ecx.probe(|r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter(|ecx| {
+ let impl_args = ecx.fresh_args_for_item(impl_def_id);
+ let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
+
+ ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
+
+ let where_clause_bounds = tcx
+ .predicates_of(impl_def_id)
+ .instantiate(tcx, impl_args)
+ .predicates
+ .into_iter()
+ .map(|pred| goal.with(tcx, pred));
+ ecx.add_goals(where_clause_bounds);
+
+ // Add GAT where clauses from the trait's definition
+ ecx.add_goals(
+ tcx.predicates_of(goal.predicate.def_id())
+ .instantiate_own(tcx, goal.predicate.projection_ty.args)
+ .map(|(pred, _)| goal.with(tcx, pred)),
+ );
+
+ // In case the associated item is hidden due to specialization, we have to
+ // return ambiguity this would otherwise be incomplete, resulting in
+ // unsoundness during coherence (#105782).
+ let Some(assoc_def) = fetch_eligible_assoc_item_def(
+ ecx,
+ goal.param_env,
+ goal_trait_ref,
+ goal.predicate.def_id(),
+ impl_def_id,
+ )?
+ else {
+ return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ };
+
+ let error_response = |ecx: &mut EvalCtxt<'_, 'tcx>, reason| {
+ let guar = tcx.sess.delay_span_bug(tcx.def_span(assoc_def.item.def_id), reason);
+ let error_term = match assoc_def.item.kind {
+ ty::AssocKind::Const => ty::Const::new_error(
+ tcx,
+ guar,
+ tcx.type_of(goal.predicate.def_id())
+ .instantiate(tcx, goal.predicate.projection_ty.args),
+ )
+ .into(),
+ ty::AssocKind::Type => Ty::new_error(tcx, guar).into(),
+ ty::AssocKind::Fn => unreachable!(),
};
+ ecx.eq(goal.param_env, goal.predicate.term, error_term)
+ .expect("expected goal term to be fully unconstrained");
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ };
- if !assoc_def.item.defaultness(tcx).has_value() {
- let guar = tcx.sess.delay_span_bug(
- tcx.def_span(assoc_def.item.def_id),
- "missing value for assoc item in impl",
- );
- let error_term = match assoc_def.item.kind {
- ty::AssocKind::Const => ty::Const::new_error(tcx,
- guar,
- tcx.type_of(goal.predicate.def_id())
- .subst(tcx, goal.predicate.projection_ty.substs),
- )
- .into(),
- ty::AssocKind::Type => Ty::new_error(tcx,guar).into(),
- ty::AssocKind::Fn => unreachable!(),
- };
- ecx.eq(goal.param_env, goal.predicate.term, error_term)
- .expect("expected goal term to be fully unconstrained");
- return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
- }
+ if !assoc_def.item.defaultness(tcx).has_value() {
+ return error_response(ecx, "missing value for assoc item in impl");
+ }
- // Getting the right substitutions here is complex, e.g. given:
- // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
- // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
- // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
- //
- // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]`
- // to `[u32, u64]`.
- //
- // And then map these substs to the substs of the defining impl of `Assoc`, going
- // from `[u32, u64]` to `[u32, i32, u64]`.
- let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
- tcx,
- goal_trait_ref.def_id,
- impl_substs,
- );
- let substs = ecx.translate_substs(
- goal.param_env,
- impl_def_id,
- impl_substs_with_gat,
- assoc_def.defining_node,
+ // Getting the right args here is complex, e.g. given:
+ // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
+ // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
+ // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
+ //
+ // We first rebase the goal args onto the impl, going from `[Vec<u32>, i32, u64]`
+ // to `[u32, u64]`.
+ //
+ // And then map these args to the args of the defining impl of `Assoc`, going
+ // from `[u32, u64]` to `[u32, i32, u64]`.
+ let impl_args_with_gat = goal.predicate.projection_ty.args.rebase_onto(
+ tcx,
+ goal_trait_ref.def_id,
+ impl_args,
+ );
+ let args = ecx.translate_args(
+ goal.param_env,
+ impl_def_id,
+ impl_args_with_gat,
+ assoc_def.defining_node,
+ );
+
+ if !check_args_compatible(tcx, assoc_def.item, args) {
+ return error_response(
+ ecx,
+ "associated item has mismatched generic item arguments",
);
+ }
- // Finally we construct the actual value of the associated type.
- let term = match assoc_def.item.kind {
- ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()),
- ty::AssocKind::Const => bug!("associated const projection is not supported yet"),
- ty::AssocKind::Fn => unreachable!("we should never project to a fn"),
- };
+ // Finally we construct the actual value of the associated type.
+ let term = match assoc_def.item.kind {
+ ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()),
+ ty::AssocKind::Const => bug!("associated const projection is not supported yet"),
+ ty::AssocKind::Fn => unreachable!("we should never project to a fn"),
+ };
- ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
- .expect("expected goal term to be fully unconstrained");
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- },
- )
+ ecx.eq(goal.param_env, goal.predicate.term, term.instantiate(tcx, args))
+ .expect("expected goal term to be fully unconstrained");
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+
+ /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error`
+ /// and succeed. Can experiment with this to figure out what results in better error messages.
+ fn consider_error_guaranteed_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ _guar: ErrorGuaranteed,
+ ) -> QueryResult<'tcx> {
+ Err(NoSolution)
}
fn consider_auto_trait_candidate(
@@ -350,7 +382,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
ty::Dynamic(_, _, _) => {
let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
tcx.type_of(dyn_metadata)
- .subst(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
+ .instantiate(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
}
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
@@ -365,29 +397,21 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
tcx.types.unit
}
- ty::Adt(def, substs) if def.is_struct() => {
- match def.non_enum_variant().tail_opt() {
- None => tcx.types.unit,
- Some(field_def) => {
- let self_ty = field_def.ty(tcx, substs);
- ecx.add_goal(goal.with(
- tcx,
- ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
- ));
- return ecx
- .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
- }
+ ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
+ None => tcx.types.unit,
+ Some(field_def) => {
+ let self_ty = field_def.ty(tcx, args);
+ ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)));
+ return ecx
+ .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
}
- }
+ },
ty::Adt(_, _) => tcx.types.unit,
ty::Tuple(elements) => match elements.last() {
None => tcx.types.unit,
Some(&self_ty) => {
- ecx.add_goal(goal.with(
- tcx,
- ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
- ));
+ ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)));
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
}
@@ -413,7 +437,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
let self_ty = goal.predicate.self_ty();
- let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
+ let ty::Generator(def_id, args, _) = *self_ty.kind() else {
return Err(NoSolution);
};
@@ -423,15 +447,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
return Err(NoSolution);
}
- let term = substs.as_generator().return_ty().into();
+ let term = args.as_generator().return_ty().into();
Self::consider_implied_clause(
ecx,
goal,
- ty::Binder::dummy(ty::ProjectionPredicate {
+ ty::ProjectionPredicate {
projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]),
term,
- })
+ }
.to_predicate(tcx),
// Technically, we need to check that the future type is Sized,
// but that's already proven by the generator being WF.
@@ -444,7 +468,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
let self_ty = goal.predicate.self_ty();
- let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
+ let ty::Generator(def_id, args, _) = *self_ty.kind() else {
return Err(NoSolution);
};
@@ -454,7 +478,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
return Err(NoSolution);
}
- let generator = substs.as_generator();
+ let generator = args.as_generator();
let name = tcx.associated_item(goal.predicate.def_id()).name;
let term = if name == sym::Return {
@@ -468,12 +492,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
Self::consider_implied_clause(
ecx,
goal,
- ty::Binder::dummy(ty::ProjectionPredicate {
+ ty::ProjectionPredicate {
projection_ty: ecx
.tcx()
.mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
term,
- })
+ }
.to_predicate(tcx),
// Technically, we need to check that the future type is Sized,
// but that's already proven by the generator being WF.
@@ -481,17 +505,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
)
}
- fn consider_builtin_unsize_candidate(
+ fn consider_unsize_to_dyn_candidate(
_ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
- bug!("`Unsize` does not have an associated type: {:?}", goal);
+ bug!("`Unsize` does not have an associated type: {:?}", goal)
}
- fn consider_builtin_dyn_upcast_candidates(
+ fn consider_structural_builtin_unsize_candidates(
_ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- ) -> Vec<CanonicalResponse<'tcx>> {
+ ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
bug!("`Unsize` does not have an associated type: {:?}", goal);
}
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 56f126e91..be48447e2 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
@@ -19,21 +19,25 @@ rustc_index::newtype_index! {
#[derive(Debug, Clone)]
pub(super) struct ProvisionalEntry<'tcx> {
- // In case we have a coinductive cycle, this is the
- // the currently least restrictive result of this goal.
- pub(super) response: QueryResult<'tcx>,
- // In case of a cycle, the position of deepest stack entry involved
- // in that cycle. This is monotonically decreasing in the stack as all
- // elements between the current stack element in the deepest stack entry
- // involved have to also be involved in that cycle.
- //
- // We can only move entries to the global cache once we're complete done
- // with the cycle. If this entry has not been involved in a cycle,
- // this is just its own depth.
+ /// In case we have a coinductive cycle, this is the
+ /// the current provisional result of this goal.
+ ///
+ /// This starts out as `None` for all goals and gets to some
+ /// when the goal gets popped from the stack or we rerun evaluation
+ /// for this goal to reach a fixpoint.
+ pub(super) response: Option<QueryResult<'tcx>>,
+ /// In case of a cycle, the position of deepest stack entry involved
+ /// in that cycle. This is monotonically decreasing in the stack as all
+ /// elements between the current stack element in the deepest stack entry
+ /// involved have to also be involved in that cycle.
+ ///
+ /// We can only move entries to the global cache once we're complete done
+ /// with the cycle. If this entry has not been involved in a cycle,
+ /// this is just its own depth.
pub(super) depth: StackDepth,
- // The goal for this entry. Should always be equal to the corresponding goal
- // in the lookup table.
+ /// The goal for this entry. Should always be equal to the corresponding goal
+ /// in the lookup table.
pub(super) input: CanonicalInput<'tcx>,
}
@@ -92,7 +96,7 @@ impl<'tcx> ProvisionalCache<'tcx> {
self.entries[entry_index].depth
}
- pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> QueryResult<'tcx> {
+ pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> Option<QueryResult<'tcx>> {
self.entries[entry_index].response
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
index f00456e26..49ebfa4e6 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -1,37 +1,51 @@
mod cache;
-mod overflow;
-
-pub(super) use overflow::OverflowHandler;
-use rustc_middle::traits::solve::inspect::CacheHit;
use self::cache::ProvisionalEntry;
+use super::inspect::ProofTreeBuilder;
+use super::SolverMode;
use cache::ProvisionalCache;
-use overflow::OverflowData;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_index::Idx;
use rustc_index::IndexVec;
use rustc_middle::dep_graph::DepKind;
-use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryResult};
+use rustc_middle::traits::solve::inspect::CacheHit;
+use rustc_middle::traits::solve::CacheData;
+use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
use rustc_middle::ty::TyCtxt;
-use std::{collections::hash_map::Entry, mem};
-
-use super::inspect::ProofTreeBuilder;
-use super::SolverMode;
+use rustc_session::Limit;
+use std::collections::hash_map::Entry;
rustc_index::newtype_index! {
pub struct StackDepth {}
}
-struct StackElem<'tcx> {
+#[derive(Debug)]
+struct StackEntry<'tcx> {
input: CanonicalInput<'tcx>,
+ available_depth: Limit,
+ // The maximum depth reached by this stack entry, only up-to date
+ // for the top of the stack and lazily updated for the rest.
+ reached_depth: StackDepth,
+ encountered_overflow: bool,
has_been_used: bool,
+
+ /// We put only the root goal of a coinductive cycle into the global cache.
+ ///
+ /// If we were to use that result when later trying to prove another cycle
+ /// participant, we can end up with unstable query results.
+ ///
+ /// See tests/ui/new-solver/coinduction/incompleteness-unstable-result.rs for
+ /// an example of where this is needed.
+ cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
}
pub(super) struct SearchGraph<'tcx> {
mode: SolverMode,
+ local_overflow_limit: usize,
/// The stack of goals currently being computed.
///
/// An element is *deeper* in the stack if its index is *lower*.
- stack: IndexVec<StackDepth, StackElem<'tcx>>,
- overflow_data: OverflowData,
+ stack: IndexVec<StackDepth, StackEntry<'tcx>>,
provisional_cache: ProvisionalCache<'tcx>,
}
@@ -39,8 +53,8 @@ impl<'tcx> SearchGraph<'tcx> {
pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> {
Self {
mode,
+ local_overflow_limit: tcx.recursion_limit().0.ilog2() as usize,
stack: Default::default(),
- overflow_data: OverflowData::new(tcx),
provisional_cache: ProvisionalCache::empty(),
}
}
@@ -49,19 +63,42 @@ impl<'tcx> SearchGraph<'tcx> {
self.mode
}
- /// We do not use the global cache during coherence.
+ pub(super) fn local_overflow_limit(&self) -> usize {
+ self.local_overflow_limit
+ }
+
+ /// Update the stack and reached depths on cache hits.
+ #[instrument(level = "debug", skip(self))]
+ fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) {
+ let reached_depth = self.stack.next_index().plus(additional_depth);
+ if let Some(last) = self.stack.raw.last_mut() {
+ last.reached_depth = last.reached_depth.max(reached_depth);
+ last.encountered_overflow |= encountered_overflow;
+ }
+ }
+
+ /// Pops the highest goal from the stack, lazily updating the
+ /// the next goal in the stack.
///
+ /// Directly popping from the stack instead of using this method
+ /// would cause us to not track overflow and recursion depth correctly.
+ fn pop_stack(&mut self) -> StackEntry<'tcx> {
+ let elem = self.stack.pop().unwrap();
+ if let Some(last) = self.stack.raw.last_mut() {
+ last.reached_depth = last.reached_depth.max(elem.reached_depth);
+ last.encountered_overflow |= elem.encountered_overflow;
+ }
+ elem
+ }
+
/// The trait solver behavior is different for coherence
- /// so we would have to add the solver mode to the cache key.
- /// This is probably not worth it as trait solving during
- /// coherence tends to already be incredibly fast.
- ///
- /// We could add another global cache for coherence instead,
- /// but that's effort so let's only do it if necessary.
- pub(super) fn should_use_global_cache(&self) -> bool {
+ /// so we use a separate cache. Alternatively we could use
+ /// a single cache and share it between coherence and ordinary
+ /// trait solving.
+ pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> {
match self.mode {
- SolverMode::Normal => true,
- SolverMode::Coherence => false,
+ SolverMode::Normal => &tcx.new_solver_evaluation_cache,
+ SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache,
}
}
@@ -87,36 +124,111 @@ impl<'tcx> SearchGraph<'tcx> {
}
}
- /// Tries putting the new goal on the stack, returning an error if it is already cached.
+ /// Fetches whether the current goal encountered overflow.
+ ///
+ /// This should only be used for the check in `evaluate_goal`.
+ pub(super) fn encountered_overflow(&self) -> bool {
+ if let Some(last) = self.stack.raw.last() { last.encountered_overflow } else { false }
+ }
+
+ /// Resets `encountered_overflow` of the current goal.
+ ///
+ /// This should only be used for the check in `evaluate_goal`.
+ pub(super) fn reset_encountered_overflow(&mut self, encountered_overflow: bool) -> bool {
+ if let Some(last) = self.stack.raw.last_mut() {
+ let prev = last.encountered_overflow;
+ last.encountered_overflow = encountered_overflow;
+ prev
+ } else {
+ false
+ }
+ }
+
+ /// Returns the remaining depth allowed for nested goals.
+ ///
+ /// This is generally simply one less than the current depth.
+ /// However, if we encountered overflow, we significantly reduce
+ /// the remaining depth of all nested goals to prevent hangs
+ /// in case there is exponential blowup.
+ fn allowed_depth_for_nested(
+ tcx: TyCtxt<'tcx>,
+ stack: &IndexVec<StackDepth, StackEntry<'tcx>>,
+ ) -> Option<Limit> {
+ if let Some(last) = stack.raw.last() {
+ if last.available_depth.0 == 0 {
+ return None;
+ }
+
+ Some(if last.encountered_overflow {
+ Limit(last.available_depth.0 / 4)
+ } else {
+ Limit(last.available_depth.0 - 1)
+ })
+ } else {
+ Some(tcx.recursion_limit())
+ }
+ }
+
+ /// Probably the most involved method of the whole solver.
///
- /// This correctly updates the provisional cache if there is a cycle.
- #[instrument(level = "debug", skip(self, tcx, inspect), ret)]
- fn try_push_stack(
+ /// Given some goal which is proven via the `prove_goal` closure, this
+ /// handles caching, overflow, and coinductive cycles.
+ pub(super) fn with_new_goal(
&mut self,
tcx: TyCtxt<'tcx>,
input: CanonicalInput<'tcx>,
inspect: &mut ProofTreeBuilder<'tcx>,
- ) -> Result<(), QueryResult<'tcx>> {
- // Look at the provisional cache to check for cycles.
+ mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
+ ) -> QueryResult<'tcx> {
+ // Check for overflow.
+ let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else {
+ if let Some(last) = self.stack.raw.last_mut() {
+ last.encountered_overflow = true;
+ }
+ return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
+ };
+
+ // Try to fetch the goal from the global cache.
+ if inspect.use_global_cache() {
+ if let Some(CacheData { result, reached_depth, encountered_overflow }) =
+ self.global_cache(tcx).get(
+ tcx,
+ input,
+ |cycle_participants| {
+ self.stack.iter().any(|entry| cycle_participants.contains(&entry.input))
+ },
+ available_depth,
+ )
+ {
+ self.on_cache_hit(reached_depth, encountered_overflow);
+ return result;
+ }
+ }
+
+ // Look at the provisional cache to detect cycles.
let cache = &mut self.provisional_cache;
match cache.lookup_table.entry(input) {
- // No entry, simply push this goal on the stack after dealing with overflow.
+ // No entry, we push this goal on the stack and try to prove it.
Entry::Vacant(v) => {
- if self.overflow_data.has_overflow(self.stack.len()) {
- return Err(self.deal_with_overflow(tcx, input));
- }
-
- 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 });
+ let depth = self.stack.next_index();
+ let entry = StackEntry {
+ input,
+ available_depth,
+ reached_depth: depth,
+ encountered_overflow: false,
+ has_been_used: false,
+ cycle_participants: Default::default(),
+ };
+ assert_eq!(self.stack.push(entry), depth);
+ let entry_index =
+ cache.entries.push(ProvisionalEntry { response: None, depth, input });
v.insert(entry_index);
- Ok(())
}
// We have a nested goal which relies on a goal `root` deeper in the stack.
//
- // We first store that we may have to rerun `evaluate_goal` for `root` in case the
- // provisional response is not equal to the final response. We also update the depth
- // of all goals which recursively depend on our current goal to depend on `root`
+ // We first store that we may have to reprove `root` in case the provisional
+ // response is not equal to the final response. We also update the depth of all
+ // goals which recursively depend on our current goal to depend on `root`
// instead.
//
// Finally we can return either the provisional response for that goal if we have a
@@ -125,169 +237,144 @@ impl<'tcx> SearchGraph<'tcx> {
inspect.cache_hit(CacheHit::Provisional);
let entry_index = *entry_index.get();
-
let stack_depth = cache.depth(entry_index);
debug!("encountered cycle with depth {stack_depth:?}");
cache.add_dependency_of_leaf_on(entry_index);
+ let mut iter = self.stack.iter_mut();
+ let root = iter.nth(stack_depth.as_usize()).unwrap();
+ for e in iter {
+ root.cycle_participants.insert(e.input);
+ }
+ // If we're in a cycle, we have to retry proving the current goal
+ // until we reach a fixpoint.
self.stack[stack_depth].has_been_used = true;
- // NOTE: The goals on the stack aren't the only goals involved in this cycle.
- // We can also depend on goals which aren't part of the stack but coinductively
- // depend on the stack themselves. We already checked whether all the goals
- // between these goals and their root on the stack. This means that as long as
- // each goal in a cycle is checked for coinductivity by itself, simply checking
- // the stack is enough.
- if self.stack.raw[stack_depth.index()..]
- .iter()
- .all(|g| g.input.value.goal.predicate.is_coinductive(tcx))
- {
- Err(cache.provisional_result(entry_index))
+ return if let Some(result) = cache.provisional_result(entry_index) {
+ result
} else {
- Err(super::response_no_constraints(
- tcx,
- input,
- Certainty::Maybe(MaybeCause::Overflow),
- ))
- }
- }
- }
- }
-
- /// We cannot simply store the result of [super::EvalCtxt::compute_goal] as we have to deal with
- /// coinductive cycles.
- ///
- /// When we encounter a coinductive cycle, we have to prove the final result of that cycle
- /// while we are still computing that result. Because of this we continuously recompute the
- /// cycle until the result of the previous iteration is equal to the final result, at which
- /// point we are done.
- ///
- /// This function returns `true` if we were able to finalize the goal and `false` if it has
- /// 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_input), ret)]
- fn try_finalize_goal(
- &mut self,
- actual_input: CanonicalInput<'tcx>,
- response: QueryResult<'tcx>,
- ) -> bool {
- let stack_elem = self.stack.pop().unwrap();
- 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(&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
- // want to access the final response whenever we look at the cache.
- let prev_response = mem::replace(&mut provisional_entry.response, response);
-
- // Was the current goal the root of a cycle and was the provisional response
- // different from the final one.
- if has_been_used && prev_response != response {
- // If so, remove all entries whose result depends on this goal
- // from the provisional cache...
- //
- // That's not completely correct, as a nested goal can also
- // depend on a goal which is lower in the stack so it doesn't
- // actually depend on the current goal. This should be fairly
- // rare and is hopefully not relevant for performance.
- #[allow(rustc::potential_query_instability)]
- cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index);
- cache.entries.truncate(provisional_entry_index.index() + 1);
-
- // ...and finally push our goal back on the stack and reevaluate it.
- self.stack.push(StackElem { input, has_been_used: false });
- false
- } else {
- true
- }
- }
+ // If we don't have a provisional result yet, the goal has to
+ // still be on the stack.
+ let mut goal_on_stack = false;
+ let mut is_coinductive = true;
+ for entry in self.stack.raw[stack_depth.index()..]
+ .iter()
+ .skip_while(|entry| entry.input != input)
+ {
+ goal_on_stack = true;
+ is_coinductive &= entry.input.value.goal.predicate.is_coinductive(tcx);
+ }
+ debug_assert!(goal_on_stack);
- pub(super) fn with_new_goal(
- &mut self,
- tcx: TyCtxt<'tcx>,
- canonical_input: CanonicalInput<'tcx>,
- inspect: &mut ProofTreeBuilder<'tcx>,
- mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
- ) -> QueryResult<'tcx> {
- if self.should_use_global_cache() && inspect.use_global_cache() {
- if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
- debug!(?canonical_input, ?result, "cache hit");
- inspect.cache_hit(CacheHit::Global);
- return result;
+ if is_coinductive {
+ Self::response_no_constraints(tcx, input, Certainty::Yes)
+ } else {
+ Self::response_no_constraints(tcx, input, Certainty::OVERFLOW)
+ }
+ };
}
}
- match self.try_push_stack(tcx, canonical_input, inspect) {
- Ok(()) => {}
- // Our goal is already on the stack, eager return.
- Err(response) => return response,
- }
-
// This is for global caching, so we properly track query dependencies.
- // Everything that affects the `Result` should be performed within this
+ // Everything that affects the `result` should be performed within this
// `with_anon_task` closure.
- let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || {
- self.repeat_while_none(
- |this| {
- let result = this.deal_with_overflow(tcx, canonical_input);
- let _ = this.stack.pop().unwrap();
- result
- },
- |this| {
- let result = loop_body(this, inspect);
- this.try_finalize_goal(canonical_input, result).then(|| result)
- },
- )
- });
+ let ((final_entry, result), dep_node) =
+ tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || {
+ // When we encounter a coinductive cycle, we have to fetch the
+ // result of that cycle while we are still computing it. Because
+ // of this we continuously recompute the cycle until the result
+ // of the previous iteration is equal to the final result, at which
+ // point we are done.
+ for _ in 0..self.local_overflow_limit() {
+ let response = prove_goal(self, inspect);
+ // Check whether the current goal is the root of a cycle and whether
+ // we have to rerun because its provisional result differed from the
+ // final result.
+ //
+ // Also update the response for this goal stored in the provisional
+ // cache.
+ let stack_entry = self.pop_stack();
+ debug_assert_eq!(stack_entry.input, input);
+ let cache = &mut self.provisional_cache;
+ let provisional_entry_index =
+ *cache.lookup_table.get(&stack_entry.input).unwrap();
+ let provisional_entry = &mut cache.entries[provisional_entry_index];
+ if stack_entry.has_been_used
+ && provisional_entry.response.map_or(true, |r| r != response)
+ {
+ // If so, update the provisional result for this goal and remove
+ // all entries whose result depends on this goal from the provisional
+ // cache...
+ //
+ // That's not completely correct, as a nested goal can also only
+ // depend on a goal which is lower in the stack so it doesn't
+ // actually depend on the current goal. This should be fairly
+ // rare and is hopefully not relevant for performance.
+ provisional_entry.response = Some(response);
+ #[allow(rustc::potential_query_instability)]
+ cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index);
+ cache.entries.truncate(provisional_entry_index.index() + 1);
+
+ // ...and finally push our goal back on the stack and reevaluate it.
+ self.stack.push(StackEntry { has_been_used: false, ..stack_entry });
+ } else {
+ return (stack_entry, response);
+ }
+ }
+
+ debug!("canonical cycle overflow");
+ let current_entry = self.pop_stack();
+ let result = Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
+ (current_entry, result)
+ });
+
+ // We're now done with this goal. In case this goal is involved in a larger cycle
+ // do not remove it from the provisional cache and update its provisional result.
+ // We only add the root of cycles to the global cache.
+ //
+ // It is not possible for any nested goal to depend on something deeper on the
+ // stack, as this would have also updated the depth of the current goal.
let cache = &mut self.provisional_cache;
- let provisional_entry_index = *cache.lookup_table.get(&canonical_input).unwrap();
+ let provisional_entry_index = *cache.lookup_table.get(&input).unwrap();
let provisional_entry = &mut cache.entries[provisional_entry_index];
let depth = provisional_entry.depth;
-
- // If not, we're done with this goal.
- //
- // Check whether that this goal doesn't depend on a goal deeper on the stack
- // and if so, move it to the global cache.
- //
- // Note that if any nested goal were to depend on something deeper on the stack,
- // this would have also updated the depth of the current goal.
if depth == self.stack.next_index() {
- // If the current goal is the head of a cycle, we drop all other
- // cycle participants without moving them to the global cache.
- let other_cycle_participants = provisional_entry_index.index() + 1;
- for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) {
+ for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) {
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.input);
- debug_assert_eq!(Some(provisional_entry_index), actual_index);
- debug_assert!(current_goal.depth == depth);
-
- // We move the root goal to the global cache if we either did not hit an overflow or if it's
- // the root goal as that will now always hit the same overflow limit.
- //
- // NOTE: We cannot move any non-root goals to the global cache. When replaying the root goal's
- // dependencies, our non-root goal may no longer appear as child of the root goal.
+ // When encountering a cycle, both inductive and coinductive, we only
+ // move the root into the global cache. We also store all other cycle
+ // participants involved.
//
- // See https://github.com/rust-lang/rust/pull/108071 for some additional context.
- let can_cache = !self.overflow_data.did_overflow() || self.stack.is_empty();
- if self.should_use_global_cache() && can_cache {
- tcx.new_solver_evaluation_cache.insert(
- current_goal.input,
- dep_node,
- current_goal.response,
- );
- }
+ // We disable the global cache entry of the root goal if a cycle
+ // participant is on the stack. This is necessary to prevent unstable
+ // results. See the comment of `StackEntry::cycle_participants` for
+ // more details.
+ let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len();
+ self.global_cache(tcx).insert(
+ input,
+ reached_depth,
+ final_entry.encountered_overflow,
+ final_entry.cycle_participants,
+ dep_node,
+ result,
+ )
+ } else {
+ provisional_entry.response = Some(result);
}
result
}
+
+ fn response_no_constraints(
+ tcx: TyCtxt<'tcx>,
+ goal: CanonicalInput<'tcx>,
+ certainty: Certainty,
+ ) -> QueryResult<'tcx> {
+ Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty))
+ }
}
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
deleted file mode 100644
index e0a2e0c5c..000000000
--- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-use rustc_infer::infer::canonical::Canonical;
-use rustc_infer::traits::query::NoSolution;
-use rustc_middle::traits::solve::{Certainty, MaybeCause, QueryResult};
-use rustc_middle::ty::TyCtxt;
-use rustc_session::Limit;
-
-use super::SearchGraph;
-use crate::solve::{response_no_constraints, EvalCtxt};
-
-/// When detecting a solver overflow, we return ambiguity. Overflow can be
-/// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**.
-///
-/// This is in issue in case of exponential blowup, e.g. if each goal on the stack
-/// has multiple nested (overflowing) candidates. To deal with this, we reduce the limit
-/// used by the solver when hitting the default limit for the first time.
-///
-/// FIXME: Get tests where always using the `default_limit` results in a hang and refer
-/// to them here. We can also improve the overflow strategy if necessary.
-pub(super) struct OverflowData {
- default_limit: Limit,
- current_limit: Limit,
- /// When proving an **AND** we have to repeatedly iterate over the yet unproven goals.
- ///
- /// Because of this each iteration also increases the depth in addition to the stack
- /// depth.
- additional_depth: usize,
-}
-
-impl OverflowData {
- pub(super) fn new(tcx: TyCtxt<'_>) -> OverflowData {
- let default_limit = tcx.recursion_limit();
- OverflowData { default_limit, current_limit: default_limit, additional_depth: 0 }
- }
-
- #[inline]
- pub(super) fn did_overflow(&self) -> bool {
- self.default_limit.0 != self.current_limit.0
- }
-
- #[inline]
- pub(super) fn has_overflow(&self, depth: usize) -> bool {
- !self.current_limit.value_within_limit(depth + self.additional_depth)
- }
-
- /// Updating the current limit when hitting overflow.
- fn deal_with_overflow(&mut self) {
- // When first hitting overflow we reduce the overflow limit
- // for all future goals to prevent hangs if there's an exponential
- // blowup.
- self.current_limit.0 = self.default_limit.0 / 8;
- }
-}
-
-pub(in crate::solve) trait OverflowHandler<'tcx> {
- fn search_graph(&mut self) -> &mut SearchGraph<'tcx>;
-
- fn repeat_while_none<T>(
- &mut self,
- on_overflow: impl FnOnce(&mut Self) -> Result<T, NoSolution>,
- mut loop_body: impl FnMut(&mut Self) -> Option<Result<T, NoSolution>>,
- ) -> Result<T, NoSolution> {
- let start_depth = self.search_graph().overflow_data.additional_depth;
- let depth = self.search_graph().stack.len();
- while !self.search_graph().overflow_data.has_overflow(depth) {
- if let Some(result) = loop_body(self) {
- self.search_graph().overflow_data.additional_depth = start_depth;
- return result;
- }
-
- self.search_graph().overflow_data.additional_depth += 1;
- }
- self.search_graph().overflow_data.additional_depth = start_depth;
- self.search_graph().overflow_data.deal_with_overflow();
- on_overflow(self)
- }
-
- // Increment the `additional_depth` by one and evaluate `body`, or `on_overflow`
- // if the depth is overflown.
- fn with_incremented_depth<T>(
- &mut self,
- on_overflow: impl FnOnce(&mut Self) -> T,
- body: impl FnOnce(&mut Self) -> T,
- ) -> T {
- let depth = self.search_graph().stack.len();
- self.search_graph().overflow_data.additional_depth += 1;
-
- let result = if self.search_graph().overflow_data.has_overflow(depth) {
- self.search_graph().overflow_data.deal_with_overflow();
- on_overflow(self)
- } else {
- body(self)
- };
-
- self.search_graph().overflow_data.additional_depth -= 1;
- result
- }
-}
-
-impl<'tcx> OverflowHandler<'tcx> for EvalCtxt<'_, 'tcx> {
- fn search_graph(&mut self) -> &mut SearchGraph<'tcx> {
- &mut self.search_graph
- }
-}
-
-impl<'tcx> OverflowHandler<'tcx> for SearchGraph<'tcx> {
- fn search_graph(&mut self) -> &mut SearchGraph<'tcx> {
- self
- }
-}
-
-impl<'tcx> SearchGraph<'tcx> {
- pub fn deal_with_overflow(
- &mut self,
- tcx: TyCtxt<'tcx>,
- goal: Canonical<'tcx, impl Sized>,
- ) -> QueryResult<'tcx> {
- self.overflow_data.deal_with_overflow();
- response_no_constraints(tcx, goal, Certainty::Maybe(MaybeCause::Overflow))
- }
-}
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index ef5f25b1f..8685f3100 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -5,13 +5,13 @@ use super::{EvalCtxt, SolverMode};
use rustc_hir::def_id::DefId;
use rustc_hir::{LangItem, Movability};
use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::util::supertraits;
+use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
-use rustc_middle::traits::Reveal;
+use rustc_middle::traits::{BuiltinImplSource, Reveal};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
-use rustc_span::DUMMY_SP;
+use rustc_span::{ErrorGuaranteed, DUMMY_SP};
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
fn self_ty(self) -> Ty<'tcx> {
@@ -39,10 +39,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
- if !drcx.substs_refs_may_unify(
- goal.predicate.trait_ref.substs,
- impl_trait_ref.skip_binder().substs,
- ) {
+ if !drcx
+ .args_refs_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
+ {
return Err(NoSolution);
}
@@ -63,13 +62,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
};
ecx.probe_candidate("impl").enter(|ecx| {
- let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
- let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
+ let impl_args = ecx.fresh_args_for_item(impl_def_id);
+ let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
let where_clause_bounds = tcx
.predicates_of(impl_def_id)
- .instantiate(tcx, impl_substs)
+ .instantiate(tcx, impl_args)
.predicates
.into_iter()
.map(|pred| goal.with(tcx, pred));
@@ -79,6 +78,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
})
}
+ fn consider_error_guaranteed_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ _guar: ErrorGuaranteed,
+ ) -> QueryResult<'tcx> {
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
@@ -164,7 +170,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx.probe_candidate("trait alias").enter(|ecx| {
let nested_obligations = tcx
.predicates_of(goal.predicate.def_id())
- .instantiate(tcx, goal.predicate.trait_ref.substs);
+ .instantiate(tcx, goal.predicate.trait_ref.args);
ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
@@ -337,7 +343,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
}
let self_ty = goal.predicate.self_ty();
- let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
+ let ty::Generator(def_id, args, _) = *self_ty.kind() else {
return Err(NoSolution);
};
@@ -347,7 +353,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
return Err(NoSolution);
}
- let generator = substs.as_generator();
+ let generator = args.as_generator();
Self::consider_implied_clause(
ecx,
goal,
@@ -359,7 +365,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
)
}
- fn consider_builtin_unsize_candidate(
+ fn consider_builtin_discriminant_kind_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
@@ -367,131 +373,205 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
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);
- if b_ty.is_ty_var() {
- return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ // `DiscriminantKind` is automatically implemented for every type.
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+
+ fn consider_builtin_destruct_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ if goal.predicate.polarity != ty::ImplPolarity::Positive {
+ return Err(NoSolution);
}
- ecx.probe_candidate("builtin unsize").enter(|ecx| {
- match (a_ty.kind(), b_ty.kind()) {
- // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
- (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
- // Dyn upcasting is handled separately, since due to upcasting,
- // when there are two supertraits that differ by substs, we
- // may return more than one query response.
- Err(NoSolution)
- }
- // `T` -> `dyn Trait` unsizing
- (_, &ty::Dynamic(data, region, ty::Dyn)) => {
- // Can only unsize to an object-safe type
- if data
- .principal_def_id()
- .is_some_and(|def_id| !tcx.check_is_object_safe(def_id))
- {
- return Err(NoSolution);
- }
- let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
- return Err(NoSolution);
- };
- // Check that the type implements all of the predicates of the def-id.
- // (i.e. the principal, all of the associated types match, and any auto traits)
- ecx.add_goals(
- data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
- );
- // The type must be Sized to be unsized.
- ecx.add_goal(goal.with(tcx, ty::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))),
- );
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- }
- // `[T; n]` -> `[T]` unsizing
- (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
- // We just require that the element type stays the same
- ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- }
- // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
- (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
- if a_def.is_struct() && a_def.did() == b_def.did() =>
- {
- let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
- // We must be unsizing some type parameters. This also implies
- // that the struct has a tail field.
- if unsizing_params.is_empty() {
- return Err(NoSolution);
- }
+ // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver
- let tail_field = a_def.non_enum_variant().tail();
- let tail_field_ty = tcx.type_of(tail_field.did);
-
- let a_tail_ty = tail_field_ty.subst(tcx, a_substs);
- let b_tail_ty = tail_field_ty.subst(tcx, b_substs);
-
- // Substitute just the unsizing params from B into A. The type after
- // this substitution must be equal to B. This is so we don't unsize
- // unrelated type parameters.
- let new_a_substs =
- tcx.mk_substs_from_iter(a_substs.iter().enumerate().map(|(i, a)| {
- if unsizing_params.contains(i as u32) { b_substs[i] } else { a }
- }));
- let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_substs);
-
- // Finally, we require that `TailA: Unsize<TailB>` for the tail field
- // types.
- ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
- ecx.add_goal(goal.with(
- tcx,
- ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
- ));
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- }
- // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
- (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
- if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
- {
- let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
- let b_last_ty = b_tys.last().unwrap();
-
- // Substitute just the tail field of B., and require that they're equal.
- let unsized_a_ty =
- Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
- ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
-
- // Similar to ADTs, require that the rest of the fields are equal.
- ecx.add_goal(goal.with(
- tcx,
- ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
- ));
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- }
- _ => Err(NoSolution),
+ // `Destruct` is automatically implemented for every type in
+ // non-const environments.
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+
+ fn consider_builtin_transmute_candidate(
+ 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);
+ }
+
+ // Erase regions because we compute layouts in `rustc_transmute`,
+ // which will ICE for region vars.
+ let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args);
+
+ let Some(assume) =
+ rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3))
+ else {
+ return Err(NoSolution);
+ };
+
+ let certainty = ecx.is_transmutable(
+ rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) },
+ args.type_at(2),
+ assume,
+ )?;
+ ecx.evaluate_added_goals_and_make_canonical_response(certainty)
+ }
+
+ fn consider_unsize_to_dyn_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| {
+ let a_ty = goal.predicate.self_ty();
+ // We need to normalize the b_ty since it's destructured as a `dyn Trait`.
+ let Some(b_ty) =
+ ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))?
+ else {
+ return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
+ };
+
+ let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
+ return Err(NoSolution);
+ };
+
+ let tcx = ecx.tcx();
+
+ // Can only unsize to an object-safe trait.
+ if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
+ return Err(NoSolution);
+ }
+
+ // Check that the type implements all of the predicates of the trait object.
+ // (i.e. the principal, all of the associated types match, and any auto traits)
+ ecx.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))));
+
+ // The type must be `Sized` to be unsized.
+ if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
+ ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
+ } else {
+ return Err(NoSolution);
}
+
+ // The type must outlive the lifetime of the `dyn` we're unsizing into.
+ ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
- fn consider_builtin_dyn_upcast_candidates(
+ /// ```ignore (builtin impl example)
+ /// trait Trait {
+ /// fn foo(&self);
+ /// }
+ /// // results in the following builtin impl
+ /// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
+ /// ```
+ fn consider_structural_builtin_unsize_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- ) -> Vec<CanonicalResponse<'tcx>> {
+ ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
if goal.predicate.polarity != ty::ImplPolarity::Positive {
return vec![];
}
- let tcx = ecx.tcx();
-
- let a_ty = goal.predicate.self_ty();
- let b_ty = goal.predicate.trait_ref.substs.type_at(1);
- let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
- return vec![];
+ let misc_candidate = |ecx: &mut EvalCtxt<'_, 'tcx>, certainty| {
+ (
+ ecx.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(),
+ BuiltinImplSource::Misc,
+ )
};
- let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
- return vec![];
+
+ let result_to_single = |result, source| match result {
+ Ok(resp) => vec![(resp, source)],
+ Err(NoSolution) => vec![],
};
+ ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| {
+ let a_ty = goal.predicate.self_ty();
+ // We need to normalize the b_ty since it's matched structurally
+ // in the other functions below.
+ let b_ty = match ecx
+ .try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
+ {
+ Ok(Some(b_ty)) => b_ty,
+ Ok(None) => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
+ Err(_) => return vec![],
+ };
+
+ let goal = goal.with(ecx.tcx(), (a_ty, b_ty));
+ match (a_ty.kind(), b_ty.kind()) {
+ (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"),
+ (_, ty::Infer(ty::TyVar(..))) => vec![misc_candidate(ecx, Certainty::AMBIGUOUS)],
+
+ // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`.
+ (
+ &ty::Dynamic(a_data, a_region, ty::Dyn),
+ &ty::Dynamic(b_data, b_region, ty::Dyn),
+ ) => ecx.consider_builtin_dyn_upcast_candidates(
+ goal, a_data, a_region, b_data, b_region,
+ ),
+
+ // `T` -> `dyn Trait` unsizing is handled separately in `consider_unsize_to_dyn_candidate`
+ (_, &ty::Dynamic(..)) => vec![],
+
+ // `[T; N]` -> `[T]` unsizing
+ (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single(
+ ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty),
+ BuiltinImplSource::Misc,
+ ),
+
+ // `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
+ (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
+ if a_def.is_struct() && a_def == b_def =>
+ {
+ result_to_single(
+ ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args),
+ BuiltinImplSource::Misc,
+ )
+ }
+
+ // `(A, B, T)` -> `(A, B, U)` where `T: Unsize<U>`
+ (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
+ if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
+ {
+ result_to_single(
+ ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys),
+ BuiltinImplSource::TupleUnsizing,
+ )
+ }
+
+ _ => vec![],
+ }
+ })
+ }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ /// Trait upcasting allows for coercions between trait objects:
+ /// ```ignore (builtin impl example)
+ /// trait Super {}
+ /// trait Trait: Super {}
+ /// // results in builtin impls upcasting to a super trait
+ /// impl<'a, 'b: 'a> Unsize<dyn Super + 'a> for dyn Trait + 'b {}
+ /// // and impls removing auto trait bounds.
+ /// impl<'a, 'b: 'a> Unsize<dyn Trait + 'a> for dyn Trait + Send + 'b {}
+ /// ```
+ fn consider_builtin_dyn_upcast_candidates(
+ &mut self,
+ goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+ a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+ a_region: ty::Region<'tcx>,
+ b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+ b_region: ty::Region<'tcx>,
+ ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
+ let tcx = self.tcx();
+ let Goal { predicate: (a_ty, _b_ty), .. } = goal;
+
// All of a's auto traits need to be in b's auto traits.
let auto_traits_compatible =
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
@@ -499,125 +579,241 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
return vec![];
}
- let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
- ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> {
- // Require that all of the trait predicates from A match B, except for
- // the auto traits. We do this by constructing a new A type with B's
- // auto traits, and equating these types.
- let new_a_data = principal
- .into_iter()
- .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
- .chain(a_data.iter().filter(|a| {
- matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
- }))
- .chain(
- b_data
- .auto_traits()
- .map(ty::ExistentialPredicate::AutoTrait)
- .map(ty::Binder::dummy),
- );
- let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
- let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
-
- // We also require that A's lifetime outlives B's lifetime.
- ecx.eq(goal.param_env, new_a_ty, b_ty)?;
- ecx.add_goal(
- goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
- );
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- })
- };
-
let mut responses = vec![];
// If the principal def ids match (or are both none), then we're not doing
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
if a_data.principal_def_id() == b_data.principal_def_id() {
- if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) {
- responses.push(response);
- }
- } else if let Some(a_principal) = a_data.principal()
- && let Some(b_principal) = b_data.principal()
- {
- for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) {
- if super_trait_ref.def_id() != b_principal.def_id() {
- continue;
- }
- let erased_trait_ref = super_trait_ref
- .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
- if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
- responses.push(response);
- }
+ if let Ok(resp) = self.consider_builtin_upcast_to_principal(
+ goal,
+ a_data,
+ a_region,
+ b_data,
+ b_region,
+ a_data.principal(),
+ ) {
+ responses.push((resp, BuiltinImplSource::Misc));
}
+ } else if let Some(a_principal) = a_data.principal() {
+ self.walk_vtable(
+ a_principal.with_self_ty(tcx, a_ty),
+ |ecx, new_a_principal, _, vtable_vptr_slot| {
+ if let Ok(resp) = ecx.probe_candidate("dyn upcast").enter(|ecx| {
+ ecx.consider_builtin_upcast_to_principal(
+ goal,
+ a_data,
+ a_region,
+ b_data,
+ b_region,
+ Some(new_a_principal.map_bound(|trait_ref| {
+ ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
+ })),
+ )
+ }) {
+ responses
+ .push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }));
+ }
+ },
+ );
}
responses
}
- fn consider_builtin_discriminant_kind_candidate(
- ecx: &mut EvalCtxt<'_, 'tcx>,
- goal: Goal<'tcx, Self>,
+ fn consider_builtin_upcast_to_principal(
+ &mut self,
+ goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+ a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+ a_region: ty::Region<'tcx>,
+ b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+ b_region: ty::Region<'tcx>,
+ upcast_principal: Option<ty::PolyExistentialTraitRef<'tcx>>,
) -> QueryResult<'tcx> {
- if goal.predicate.polarity != ty::ImplPolarity::Positive {
- return Err(NoSolution);
+ let param_env = goal.param_env;
+
+ // More than one projection in a_ty's bounds may match the projection
+ // in b_ty's bound. Use this to first determine *which* apply without
+ // having any inference side-effects. We process obligations because
+ // unification may initially succeed due to deferred projection equality.
+ let projection_may_match =
+ |ecx: &mut Self,
+ source_projection: ty::PolyExistentialProjection<'tcx>,
+ target_projection: ty::PolyExistentialProjection<'tcx>| {
+ source_projection.item_def_id() == target_projection.item_def_id()
+ && ecx
+ .probe(|_| CandidateKind::UpcastProbe)
+ .enter(|ecx| -> Result<(), NoSolution> {
+ ecx.eq(param_env, source_projection, target_projection)?;
+ let _ = ecx.try_evaluate_added_goals()?;
+ Ok(())
+ })
+ .is_ok()
+ };
+
+ for bound in b_data {
+ match bound.skip_binder() {
+ // Check that a's supertrait (upcast_principal) is compatible
+ // with the target (b_ty).
+ ty::ExistentialPredicate::Trait(target_principal) => {
+ self.eq(param_env, upcast_principal.unwrap(), bound.rebind(target_principal))?;
+ }
+ // Check that b_ty's projection is satisfied by exactly one of
+ // a_ty's projections. First, we look through the list to see if
+ // any match. If not, error. Then, if *more* than one matches, we
+ // return ambiguity. Otherwise, if exactly one matches, equate
+ // it with b_ty's projection.
+ ty::ExistentialPredicate::Projection(target_projection) => {
+ let target_projection = bound.rebind(target_projection);
+ let mut matching_projections =
+ a_data.projection_bounds().filter(|source_projection| {
+ projection_may_match(self, *source_projection, target_projection)
+ });
+ let Some(source_projection) = matching_projections.next() else {
+ return Err(NoSolution);
+ };
+ if matching_projections.next().is_some() {
+ return self.evaluate_added_goals_and_make_canonical_response(
+ Certainty::AMBIGUOUS,
+ );
+ }
+ self.eq(param_env, source_projection, target_projection)?;
+ }
+ // Check that b_ty's auto traits are present in a_ty's bounds.
+ ty::ExistentialPredicate::AutoTrait(def_id) => {
+ if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
+ return Err(NoSolution);
+ }
+ }
+ }
}
- // `DiscriminantKind` is automatically implemented for every type.
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ // Also require that a_ty's lifetime outlives b_ty's lifetime.
+ self.add_goal(Goal::new(
+ self.tcx(),
+ param_env,
+ ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
+ ));
+
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
- fn consider_builtin_destruct_candidate(
- ecx: &mut EvalCtxt<'_, 'tcx>,
- goal: Goal<'tcx, Self>,
+ /// We have the following builtin impls for arrays:
+ /// ```ignore (builtin impl example)
+ /// impl<T: ?Sized, const N: usize> Unsize<[T]> for [T; N] {}
+ /// ```
+ /// While the impl itself could theoretically not be builtin,
+ /// the actual unsizing behavior is builtin. Its also easier to
+ /// make all impls of `Unsize` builtin as we're able to use
+ /// `#[rustc_deny_explicit_impl]` in this case.
+ fn consider_builtin_array_unsize(
+ &mut self,
+ goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+ a_elem_ty: Ty<'tcx>,
+ b_elem_ty: Ty<'tcx>,
) -> 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.
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- } else {
- // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver
- Err(NoSolution)
- }
+ self.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
- fn consider_builtin_transmute_candidate(
- ecx: &mut EvalCtxt<'_, 'tcx>,
- goal: Goal<'tcx, Self>,
+ /// We generate a builtin `Unsize` impls for structs with generic parameters only
+ /// mentioned by the last field.
+ /// ```ignore (builtin impl example)
+ /// struct Foo<T, U: ?Sized> {
+ /// sized_field: Vec<T>,
+ /// unsizable: Box<U>,
+ /// }
+ /// // results in the following builtin impl
+ /// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<Foo<T, V>> for Foo<T, U>
+ /// where
+ /// Box<U>: Unsize<Box<V>>,
+ /// {}
+ /// ```
+ fn consider_builtin_struct_unsize(
+ &mut self,
+ goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+ def: ty::AdtDef<'tcx>,
+ a_args: ty::GenericArgsRef<'tcx>,
+ b_args: ty::GenericArgsRef<'tcx>,
) -> QueryResult<'tcx> {
- if goal.predicate.polarity != ty::ImplPolarity::Positive {
- return Err(NoSolution);
- }
+ let tcx = self.tcx();
+ let Goal { predicate: (_a_ty, b_ty), .. } = goal;
- // `rustc_transmute` does not have support for type or const params
- if goal.has_non_region_placeholders() {
+ let unsizing_params = tcx.unsizing_params_for_adt(def.did());
+ // We must be unsizing some type parameters. This also implies
+ // that the struct has a tail field.
+ if unsizing_params.is_empty() {
return Err(NoSolution);
}
- // Erase regions because we compute layouts in `rustc_transmute`,
- // which will ICE for region vars.
- let substs = ecx.tcx().erase_regions(goal.predicate.trait_ref.substs);
+ let tail_field = def.non_enum_variant().tail();
+ let tail_field_ty = tcx.type_of(tail_field.did);
+
+ let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
+ let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
+
+ // Substitute just the unsizing params from B into A. The type after
+ // this substitution must be equal to B. This is so we don't unsize
+ // unrelated type parameters.
+ let new_a_args = tcx.mk_args_from_iter(
+ a_args
+ .iter()
+ .enumerate()
+ .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }),
+ );
+ let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args);
+
+ // Finally, we require that `TailA: Unsize<TailB>` for the tail field
+ // types.
+ self.eq(goal.param_env, unsized_a_ty, b_ty)?;
+ self.add_goal(goal.with(
+ tcx,
+ ty::TraitRef::new(
+ tcx,
+ tcx.lang_items().unsize_trait().unwrap(),
+ [a_tail_ty, b_tail_ty],
+ ),
+ ));
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
- let Some(assume) = rustc_transmute::Assume::from_const(
- ecx.tcx(),
- goal.param_env,
- substs.const_at(3),
- ) else {
- return Err(NoSolution);
- };
+ /// We generate the following builtin impl for tuples of all sizes.
+ ///
+ /// This impl is still unstable and we emit a feature error when it
+ /// when it is used by a coercion.
+ /// ```ignore (builtin impl example)
+ /// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<(T, V)> for (T, U)
+ /// where
+ /// U: Unsize<V>,
+ /// {}
+ /// ```
+ fn consider_builtin_tuple_unsize(
+ &mut self,
+ goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+ a_tys: &'tcx ty::List<Ty<'tcx>>,
+ b_tys: &'tcx ty::List<Ty<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let Goal { predicate: (_a_ty, b_ty), .. } = goal;
- let certainty = ecx.is_transmutable(
- rustc_transmute::Types { dst: substs.type_at(0), src: substs.type_at(1) },
- substs.type_at(2),
- assume,
- )?;
- ecx.evaluate_added_goals_and_make_canonical_response(certainty)
+ let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
+ let &b_last_ty = b_tys.last().unwrap();
+
+ // Substitute just the tail field of B., and require that they're equal.
+ let unsized_a_ty =
+ Ty::new_tup_from_iter(tcx, a_rest_tys.iter().copied().chain([b_last_ty]));
+ self.eq(goal.param_env, unsized_a_ty, b_ty)?;
+
+ // Similar to ADTs, require that we can unsize the tail.
+ self.add_goal(goal.with(
+ tcx,
+ ty::TraitRef::new(
+ tcx,
+ tcx.lang_items().unsize_trait().unwrap(),
+ [a_last_ty, b_last_ty],
+ ),
+ ));
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
-}
-impl<'tcx> EvalCtxt<'_, 'tcx> {
// Return `Some` if there is an impl (built-in or user provided) that may
// hold for the self type of the goal, which for coherence and soundness
// purposes must disqualify the built-in auto impl assembled by considering
@@ -689,7 +885,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
| ty::Tuple(_)
| ty::Adt(_, _)
// FIXME: Handling opaques here is kinda sus. Especially because we
- // simplify them to PlaceholderSimplifiedType.
+ // simplify them to SimplifiedType::Placeholder.
| ty::Alias(ty::Opaque, _) => {
let mut disqualifying_impl = None;
self.tcx().for_each_relevant_impl_treating_projections(
@@ -726,12 +922,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
ecx.add_goals(
constituent_tys(ecx, goal.predicate.self_ty())?
.into_iter()
- .map(|ty| {
- goal.with(
- ecx.tcx(),
- ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)),
- )
- })
+ .map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty)))
.collect::<Vec<_>>(),
);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
diff --git a/compiler/rustc_trait_selection/src/solve/weak_types.rs b/compiler/rustc_trait_selection/src/solve/weak_types.rs
index b095b54c5..54de32cf6 100644
--- a/compiler/rustc_trait_selection/src/solve/weak_types.rs
+++ b/compiler/rustc_trait_selection/src/solve/weak_types.rs
@@ -1,3 +1,8 @@
+//! Computes a normalizes-to (projection) goal for inherent associated types,
+//! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`.
+//!
+//! Since a weak alias is not ambiguous, this just computes the `type_of` of
+//! the alias and registers the where-clauses of the type alias.
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
use rustc_middle::ty;
@@ -12,8 +17,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let weak_ty = goal.predicate.projection_ty;
let expected = goal.predicate.term.ty().expect("no such thing as a const alias");
- let actual = tcx.type_of(weak_ty.def_id).subst(tcx, weak_ty.substs);
+ let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
self.eq(goal.param_env, expected, actual)?;
+
+ // Check where clauses
+ self.add_goals(
+ tcx.predicates_of(weak_ty.def_id)
+ .instantiate(tcx, weak_ty.args)
+ .predicates
+ .into_iter()
+ .map(|pred| goal.with(tcx, pred)),
+ );
+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index cb38d0ac8..ba5000da6 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -97,7 +97,6 @@ impl<'tcx> AutoTraitFinder<'tcx> {
orig_env,
ty::TraitPredicate {
trait_ref,
- constness: ty::BoundConstness::NotConst,
polarity: if polarity {
ImplPolarity::Positive
} else {
@@ -152,21 +151,16 @@ impl<'tcx> AutoTraitFinder<'tcx> {
// traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing
// SelectionContext to return it back to us.
- let Some((new_env, user_env)) = self.evaluate_predicates(
- &infcx,
- trait_did,
- ty,
- orig_env,
- orig_env,
- &mut fresh_preds,
- ) else {
+ let Some((new_env, user_env)) =
+ self.evaluate_predicates(&infcx, trait_did, ty, orig_env, orig_env, &mut fresh_preds)
+ else {
return AutoTraitResult::NegativeImpl;
};
let (full_env, full_user_env) = self
.evaluate_predicates(&infcx, trait_did, ty, new_env, user_env, &mut fresh_preds)
.unwrap_or_else(|| {
- panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env)
+ panic!("Failed to fully process: {ty:?} {trait_did:?} {orig_env:?}")
});
debug!(
@@ -183,7 +177,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
ocx.register_bound(ObligationCause::dummy(), full_env, ty, trait_did);
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
- panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors);
+ panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}");
}
let outlives_env = OutlivesEnvironment::new(full_env);
@@ -265,7 +259,6 @@ impl<'tcx> AutoTraitFinder<'tcx> {
predicates.push_back(ty::Binder::dummy(ty::TraitPredicate {
trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]),
- constness: ty::BoundConstness::NotConst,
// Auto traits are positive
polarity: ty::ImplPolarity::Positive,
}));
@@ -329,7 +322,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
}
Ok(None) => {}
Err(SelectionError::Unimplemented) => {
- if self.is_param_no_infer(pred.skip_binder().trait_ref.substs) {
+ if self.is_param_no_infer(pred.skip_binder().trait_ref.args) {
already_visited.remove(&pred);
self.add_user_pred(&mut user_computed_preds, pred.to_predicate(self.tcx));
predicates.push_back(pred);
@@ -339,12 +332,12 @@ impl<'tcx> AutoTraitFinder<'tcx> {
{:?} {:?} {:?}",
ty,
pred,
- pred.skip_binder().trait_ref.substs
+ pred.skip_binder().trait_ref.args
);
return None;
}
}
- _ => panic!("Unexpected error for '{:?}': {:?}", ty, result),
+ _ => panic!("Unexpected error for '{ty:?}': {result:?}"),
};
let normalized_preds =
@@ -352,14 +345,12 @@ impl<'tcx> AutoTraitFinder<'tcx> {
new_env = ty::ParamEnv::new(
tcx.mk_clauses_from_iter(normalized_preds.filter_map(|p| p.as_clause())),
param_env.reveal(),
- param_env.constness(),
);
}
let final_user_env = ty::ParamEnv::new(
tcx.mk_clauses_from_iter(user_computed_preds.into_iter().filter_map(|p| p.as_clause())),
user_env.reveal(),
- user_env.constness(),
);
debug!(
"evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \
@@ -406,17 +397,17 @@ impl<'tcx> AutoTraitFinder<'tcx> {
) = (new_pred.kind().skip_binder(), old_pred.kind().skip_binder())
{
if new_trait.def_id() == old_trait.def_id() {
- let new_substs = new_trait.trait_ref.substs;
- let old_substs = old_trait.trait_ref.substs;
+ let new_args = new_trait.trait_ref.args;
+ let old_args = old_trait.trait_ref.args;
- if !new_substs.types().eq(old_substs.types()) {
+ if !new_args.types().eq(old_args.types()) {
// We can't compare lifetimes if the types are different,
// so skip checking `old_pred`.
return true;
}
for (new_region, old_region) in
- iter::zip(new_substs.regions(), old_substs.regions())
+ iter::zip(new_args.regions(), old_args.regions())
{
match (*new_region, *old_region) {
// If both predicates have an `ReLateBound` (a HRTB) in the
@@ -569,8 +560,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
finished_map
}
- fn is_param_no_infer(&self, substs: SubstsRef<'_>) -> bool {
- self.is_of_param(substs.type_at(0)) && !substs.types().any(|t| t.has_infer_types())
+ fn is_param_no_infer(&self, args: GenericArgsRef<'_>) -> bool {
+ self.is_of_param(args.type_at(0)) && !args.types().any(|t| t.has_infer_types())
}
pub fn is_of_param(&self, ty: Ty<'_>) -> bool {
@@ -641,7 +632,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
// an inference variable.
// Additionally, we check if we've seen this predicate before,
// to avoid rendering duplicate bounds to the user.
- if self.is_param_no_infer(p.skip_binder().projection_ty.substs)
+ if self.is_param_no_infer(p.skip_binder().projection_ty.args)
&& !p.term().skip_binder().has_infer_types()
&& is_new_pred
{
@@ -754,7 +745,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
// subobligations or getting an error) when we started off with
// inference variables
if p.term().skip_binder().has_infer_types() {
- panic!("Unexpected result when selecting {:?} {:?}", ty, obligation)
+ panic!("Unexpected result when selecting {ty:?} {obligation:?}")
}
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 1b1285e1b..5746781ae 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -7,7 +7,7 @@
use crate::infer::outlives::env::OutlivesEnvironment;
use crate::infer::InferOk;
use crate::traits::outlives_bounds::InferCtxtExt as _;
-use crate::traits::select::IntercrateAmbiguityCause;
+use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs};
use crate::traits::util::impl_subject_and_oblig;
use crate::traits::SkipLeakCheck;
use crate::traits::{
@@ -24,6 +24,7 @@ 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, Ty, TyCtxt, TypeVisitor};
+use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE;
use rustc_span::symbol::sym;
use rustc_span::DUMMY_SP;
use std::fmt::Debug;
@@ -96,9 +97,7 @@ pub fn overlapping_impls(
let impl1_ref = tcx.impl_trait_ref(impl1_def_id);
let impl2_ref = tcx.impl_trait_ref(impl2_def_id);
let may_overlap = match (impl1_ref, impl2_ref) {
- (Some(a), Some(b)) => {
- drcx.substs_refs_may_unify(a.skip_binder().substs, b.skip_binder().substs)
- }
+ (Some(a), Some(b)) => drcx.args_refs_may_unify(a.skip_binder().args, b.skip_binder().args),
(None, None) => {
let self_ty1 = tcx.type_of(impl1_def_id).skip_binder();
let self_ty2 = tcx.type_of(impl2_def_id).skip_binder();
@@ -143,24 +142,26 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
impl_def_id: DefId,
) -> ty::ImplHeader<'tcx> {
let tcx = selcx.tcx();
- let impl_substs = selcx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+ let impl_args = selcx.infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
let header = ty::ImplHeader {
impl_def_id,
- self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs),
- trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)),
+ self_ty: tcx.type_of(impl_def_id).instantiate(tcx, impl_args),
+ trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.instantiate(tcx, impl_args)),
predicates: tcx
.predicates_of(impl_def_id)
- .instantiate(tcx, impl_substs)
+ .instantiate(tcx, impl_args)
.iter()
- .map(|(c, _)| c.as_predicate())
+ .map(|(c, s)| (c.as_predicate(), s))
.collect(),
};
- let InferOk { value: mut header, obligations } =
- selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(header);
+ let InferOk { value: mut header, obligations } = selcx
+ .infcx
+ .at(&ObligationCause::dummy_with_span(tcx.def_span(impl_def_id)), param_env)
+ .normalize(header);
- header.predicates.extend(obligations.into_iter().map(|o| o.predicate));
+ header.predicates.extend(obligations.into_iter().map(|o| (o.predicate, o.cause.span)));
header
}
@@ -209,16 +210,76 @@ fn overlap<'tcx>(
let equate_obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?;
debug!("overlap: unification check succeeded");
- if overlap_mode.use_implicit_negative()
- && impl_intersection_has_impossible_obligation(
- selcx,
- param_env,
- &impl1_header,
- impl2_header,
- equate_obligations,
- )
- {
- return None;
+ if overlap_mode.use_implicit_negative() {
+ for mode in [TreatInductiveCycleAs::Ambig, TreatInductiveCycleAs::Recur] {
+ if let Some(failing_obligation) = selcx.with_treat_inductive_cycle_as(mode, |selcx| {
+ impl_intersection_has_impossible_obligation(
+ selcx,
+ param_env,
+ &impl1_header,
+ &impl2_header,
+ &equate_obligations,
+ )
+ }) {
+ if matches!(mode, TreatInductiveCycleAs::Recur) {
+ let first_local_impl = impl1_header
+ .impl_def_id
+ .as_local()
+ .or(impl2_header.impl_def_id.as_local())
+ .expect("expected one of the impls to be local");
+ infcx.tcx.struct_span_lint_hir(
+ COINDUCTIVE_OVERLAP_IN_COHERENCE,
+ infcx.tcx.local_def_id_to_hir_id(first_local_impl),
+ infcx.tcx.def_span(first_local_impl),
+ format!(
+ "implementations {} will conflict in the future",
+ match impl1_header.trait_ref {
+ Some(trait_ref) => {
+ let trait_ref = infcx.resolve_vars_if_possible(trait_ref);
+ format!(
+ "of `{}` for `{}`",
+ trait_ref.print_only_trait_path(),
+ trait_ref.self_ty()
+ )
+ }
+ None => format!(
+ "for `{}`",
+ infcx.resolve_vars_if_possible(impl1_header.self_ty)
+ ),
+ },
+ ),
+ |lint| {
+ lint.note(
+ "impls that are not considered to overlap may be considered to \
+ overlap in the future",
+ )
+ .span_label(
+ infcx.tcx.def_span(impl1_header.impl_def_id),
+ "the first impl is here",
+ )
+ .span_label(
+ infcx.tcx.def_span(impl2_header.impl_def_id),
+ "the second impl is here",
+ );
+ if !failing_obligation.cause.span.is_dummy() {
+ lint.span_label(
+ failing_obligation.cause.span,
+ format!(
+ "`{}` may be considered to hold in future releases, \
+ causing the impls to overlap",
+ infcx
+ .resolve_vars_if_possible(failing_obligation.predicate)
+ ),
+ );
+ }
+ lint
+ },
+ );
+ }
+
+ return None;
+ }
+ }
}
// We toggle the `leak_check` by using `skip_leak_check` when constructing the
@@ -286,40 +347,30 @@ fn impl_intersection_has_impossible_obligation<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
impl1_header: &ty::ImplHeader<'tcx>,
- impl2_header: ty::ImplHeader<'tcx>,
- obligations: PredicateObligations<'tcx>,
-) -> bool {
+ impl2_header: &ty::ImplHeader<'tcx>,
+ obligations: &PredicateObligations<'tcx>,
+) -> Option<PredicateObligation<'tcx>> {
let infcx = selcx.infcx;
- let obligation_guaranteed_to_fail = move |obligation: &PredicateObligation<'tcx>| {
- if infcx.next_trait_solver() {
- infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply())
- } else {
- // We use `evaluate_root_obligation` to correctly track
- // intercrate ambiguity clauses. We do not need this in the
- // new solver.
- selcx.evaluate_root_obligation(obligation).map_or(
- false, // Overflow has occurred, and treat the obligation as possibly holding.
- |result| !result.may_apply(),
- )
- }
- };
-
- let opt_failing_obligation = [&impl1_header.predicates, &impl2_header.predicates]
+ [&impl1_header.predicates, &impl2_header.predicates]
.into_iter()
.flatten()
- .map(|&predicate| {
- Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate)
+ .map(|&(predicate, span)| {
+ Obligation::new(infcx.tcx, ObligationCause::dummy_with_span(span), param_env, predicate)
+ })
+ .chain(obligations.into_iter().cloned())
+ .find(|obligation: &PredicateObligation<'tcx>| {
+ if infcx.next_trait_solver() {
+ infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply())
+ } else {
+ // We use `evaluate_root_obligation` to correctly track intercrate
+ // ambiguity clauses. We cannot use this in the new solver.
+ selcx.evaluate_root_obligation(obligation).map_or(
+ false, // Overflow has occurred, and treat the obligation as possibly holding.
+ |result| !result.may_apply(),
+ )
+ }
})
- .chain(obligations)
- .find(obligation_guaranteed_to_fail);
-
- if let Some(failing_obligation) = opt_failing_obligation {
- debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
- true
- } else {
- false
- }
}
/// Check if both impls can be satisfied by a common type by considering whether
@@ -353,13 +404,13 @@ fn impl_intersection_has_negative_obligation(
&infcx,
ObligationCause::dummy(),
impl_env,
- tcx.impl_subject(impl1_def_id).subst_identity(),
+ tcx.impl_subject(impl1_def_id).instantiate_identity(),
) {
Ok(s) => s,
Err(err) => {
tcx.sess.delay_span_bug(
tcx.def_span(impl1_def_id),
- format!("failed to fully normalize {:?}: {:?}", impl1_def_id, err),
+ format!("failed to fully normalize {impl1_def_id:?}: {err:?}"),
);
return false;
}
@@ -367,16 +418,16 @@ fn impl_intersection_has_negative_obligation(
// Attempt to prove that impl2 applies, given all of the above.
let selcx = &mut SelectionContext::new(&infcx);
- let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
+ let impl2_args = infcx.fresh_args_for_item(DUMMY_SP, impl2_def_id);
let (subject2, normalization_obligations) =
- impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs, |_, _| {
+ impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_args, |_, _| {
ObligationCause::dummy()
});
// do the impls unify? If not, then it's not currently possible to prove any
// obligations about their intersection.
let Ok(InferOk { obligations: equate_obligations, .. }) =
- infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No,subject1, subject2)
+ infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No, subject1, subject2)
else {
debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2);
return false;
@@ -437,8 +488,7 @@ fn prove_negated_obligation<'tcx>(
let body_def_id = body_def_id.as_local().unwrap_or(CRATE_DEF_ID);
let ocx = ObligationCtxt::new(&infcx);
- let Ok(wf_tys) = ocx.assumed_wf_types(param_env, body_def_id)
- else {
+ let Ok(wf_tys) = ocx.assumed_wf_types(param_env, body_def_id) else {
return false;
};
@@ -455,22 +505,23 @@ fn prove_negated_obligation<'tcx>(
/// This both checks whether any downstream or sibling crates could
/// implement it and whether an upstream crate can add this impl
/// without breaking backwards compatibility.
-#[instrument(level = "debug", skip(tcx), ret)]
-pub fn trait_ref_is_knowable<'tcx>(
+#[instrument(level = "debug", skip(tcx, lazily_normalize_ty), ret)]
+pub fn trait_ref_is_knowable<'tcx, E: Debug>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
-) -> Result<(), Conflict> {
+ mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
+) -> Result<Result<(), Conflict>, E> {
if Some(trait_ref.def_id) == tcx.lang_items().fn_ptr_trait() {
// The only types implementing `FnPtr` are function pointers,
// so if there's no impl of `FnPtr` in the current crate,
// then such an impl will never be added in the future.
- return Ok(());
+ return Ok(Ok(()));
}
- if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() {
+ if orphan_check_trait_ref(trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() {
// A downstream or cousin crate is allowed to implement some
// substitution of this trait-ref.
- return Err(Conflict::Downstream);
+ return Ok(Err(Conflict::Downstream));
}
if trait_ref_is_local_or_fundamental(tcx, trait_ref) {
@@ -479,7 +530,7 @@ pub fn trait_ref_is_knowable<'tcx>(
// allowed to implement a substitution of this trait ref, which
// means impls could only come from dependencies of this crate,
// which we already know about.
- return Ok(());
+ return Ok(Ok(()));
}
// This is a remote non-fundamental trait, so if another crate
@@ -490,10 +541,10 @@ pub fn trait_ref_is_knowable<'tcx>(
// and if we are an intermediate owner, then we don't care
// about future-compatibility, which means that we're OK if
// we are an owner.
- if orphan_check_trait_ref(trait_ref, InCrate::Local).is_ok() {
- Ok(())
+ if orphan_check_trait_ref(trait_ref, InCrate::Local, &mut lazily_normalize_ty)?.is_ok() {
+ Ok(Ok(()))
} else {
- Err(Conflict::Upstream)
+ Ok(Err(Conflict::Upstream))
}
}
@@ -520,7 +571,7 @@ pub enum OrphanCheckErr<'tcx> {
pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> {
// We only except this routine to be invoked on implementations
// of a trait, not inherent implementations.
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
debug!(?trait_ref);
// If the *trait* is local to the crate, ok.
@@ -529,7 +580,7 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
return Ok(());
}
- orphan_check_trait_ref(trait_ref, InCrate::Local)
+ orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap()
}
/// Checks whether a trait-ref is potentially implementable by a crate.
@@ -618,11 +669,12 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
///
/// Note that this function is never called for types that have both type
/// parameters and inference variables.
-#[instrument(level = "trace", ret)]
-fn orphan_check_trait_ref<'tcx>(
+#[instrument(level = "trace", skip(lazily_normalize_ty), ret)]
+fn orphan_check_trait_ref<'tcx, E: Debug>(
trait_ref: ty::TraitRef<'tcx>,
in_crate: InCrate,
-) -> Result<(), OrphanCheckErr<'tcx>> {
+ lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
+) -> Result<Result<(), OrphanCheckErr<'tcx>>, E> {
if trait_ref.has_infer() && trait_ref.has_param() {
bug!(
"can't orphan check a trait ref with both params and inference variables {:?}",
@@ -630,9 +682,10 @@ fn orphan_check_trait_ref<'tcx>(
);
}
- let mut checker = OrphanChecker::new(in_crate);
- match trait_ref.visit_with(&mut checker) {
+ let mut checker = OrphanChecker::new(in_crate, lazily_normalize_ty);
+ Ok(match trait_ref.visit_with(&mut checker) {
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
+ ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err),
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
// Does there exist some local type after the `ParamTy`.
checker.search_first_local_ty = true;
@@ -645,34 +698,39 @@ fn orphan_check_trait_ref<'tcx>(
}
}
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()),
- }
+ })
}
-struct OrphanChecker<'tcx> {
+struct OrphanChecker<'tcx, F> {
in_crate: InCrate,
in_self_ty: bool,
+ lazily_normalize_ty: F,
/// Ignore orphan check failures and exclusively search for the first
/// local type.
search_first_local_ty: bool,
non_local_tys: Vec<(Ty<'tcx>, bool)>,
}
-impl<'tcx> OrphanChecker<'tcx> {
- fn new(in_crate: InCrate) -> Self {
+impl<'tcx, F, E> OrphanChecker<'tcx, F>
+where
+ F: FnOnce(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
+{
+ fn new(in_crate: InCrate, lazily_normalize_ty: F) -> Self {
OrphanChecker {
in_crate,
in_self_ty: true,
+ lazily_normalize_ty,
search_first_local_ty: false,
non_local_tys: Vec::new(),
}
}
- fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
+ fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
self.non_local_tys.push((t, self.in_self_ty));
ControlFlow::Continue(())
}
- fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
+ fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
if self.search_first_local_ty {
ControlFlow::Continue(())
} else {
@@ -688,18 +746,28 @@ impl<'tcx> OrphanChecker<'tcx> {
}
}
-enum OrphanCheckEarlyExit<'tcx> {
+enum OrphanCheckEarlyExit<'tcx, E> {
+ NormalizationFailure(E),
ParamTy(Ty<'tcx>),
LocalTy(Ty<'tcx>),
}
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> {
- type BreakTy = OrphanCheckEarlyExit<'tcx>;
+impl<'tcx, F, E> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx, F>
+where
+ F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
+{
+ type BreakTy = OrphanCheckEarlyExit<'tcx, E>;
fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ // Need to lazily normalize here in with `-Ztrait-solver=next-coherence`.
+ let ty = match (self.lazily_normalize_ty)(ty) {
+ Ok(ty) => ty,
+ Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)),
+ };
+
let result = match *ty.kind() {
ty::Bool
| ty::Char
@@ -729,11 +797,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> {
// For fundamental types, we just look inside of them.
ty::Ref(_, ty, _) => ty.visit_with(self),
- ty::Adt(def, substs) => {
+ ty::Adt(def, args) => {
if self.def_id_is_local(def.did()) {
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
} else if def.is_fundamental() {
- substs.visit_with(self)
+ args.visit_with(self)
} 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 8dc13b827..3d0d3812d 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -191,7 +191,7 @@ fn satisfied_from_param_env<'tcx>(
if let ty::ConstKind::Expr(e) = c.kind() {
e.visit_with(self)
} else {
- // FIXME(generic_const_exprs): This doesn't recurse into `<T as Trait<U>>::ASSOC`'s substs.
+ // FIXME(generic_const_exprs): This doesn't recurse into `<T as Trait<U>>::ASSOC`'s args.
// This is currently unobservable as `<T as Trait<{ U + 1 }>>::ASSOC` creates an anon const
// with its own `ConstEvaluatable` bound in the param env which we will visit separately.
//
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index 61f693e1b..820973dc0 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -97,7 +97,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
cause,
recursion_depth: 0,
param_env,
- predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx),
+ predicate: ty::Binder::dummy(trait_ref).to_predicate(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 f785c4eaf..fd813ca4e 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
@@ -26,8 +26,8 @@ pub fn recompute_applicable_impls<'tcx>(
let obligation_trait_ref =
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
- let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
- let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
+ let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
if let Err(_) =
@@ -36,7 +36,7 @@ pub fn recompute_applicable_impls<'tcx>(
return false;
}
- let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs);
+ let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args);
ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
}));
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 f34218059..457d5420c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -7,6 +7,7 @@ use super::{
ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow,
PredicateObligation, SelectionError, TraitNotObjectSafe,
};
+use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch};
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{self, InferCtxt};
@@ -100,7 +101,6 @@ pub trait InferCtxtExt<'tcx> {
&self,
param_env: ty::ParamEnv<'tcx>,
ty: ty::Binder<'tcx, Ty<'tcx>>,
- constness: ty::BoundConstness,
polarity: ty::ImplPolarity,
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>;
}
@@ -226,7 +226,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
(span, None, vec![ArgKind::empty(); variant_data.fields().len()])
}
- _ => panic!("non-FnLike node found: {:?}", node),
+ _ => panic!("non-FnLike node found: {node:?}"),
})
}
@@ -273,10 +273,10 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
found_str,
);
- err.span_label(span, format!("expected {} that takes {}", kind, expected_str));
+ err.span_label(span, format!("expected {kind} that takes {expected_str}"));
if let Some(found_span) = found_span {
- err.span_label(found_span, format!("takes {}", found_str));
+ err.span_label(found_span, format!("takes {found_str}"));
// Suggest to take and ignore the arguments with expected_args_length `_`s if
// found arguments is empty (assume the user just wants to ignore args in this case).
@@ -289,7 +289,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
"consider changing the closure to take and ignore the expected argument{}",
pluralize!(expected_args.len())
),
- format!("|{}|", underscores),
+ format!("|{underscores}|"),
Applicability::MachineApplicable,
);
}
@@ -304,7 +304,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
err.span_suggestion_verbose(
found_span,
"change the closure to take multiple arguments instead of a single tuple",
- format!("|{}|", sugg),
+ format!("|{sugg}|"),
Applicability::MachineApplicable,
);
}
@@ -356,7 +356,6 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
&self,
param_env: ty::ParamEnv<'tcx>,
ty: ty::Binder<'tcx, Ty<'tcx>>,
- constness: ty::BoundConstness,
polarity: ty::ImplPolarity,
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> {
self.commit_if_ok(|_| {
@@ -372,12 +371,13 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
span: DUMMY_SP,
kind: TypeVariableOriginKind::MiscVariable,
});
+ // FIXME(effects)
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]);
let obligation = Obligation::new(
self.tcx,
ObligationCause::dummy(),
param_env,
- ty.rebind(ty::TraitPredicate { trait_ref, constness, polarity }),
+ ty.rebind(ty::TraitPredicate { trait_ref, polarity }),
);
let ocx = ObligationCtxt::new(self);
ocx.register_obligation(obligation);
@@ -687,11 +687,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => {
let trait_predicate = bound_predicate.rebind(trait_predicate);
- let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate);
+ let trait_predicate = self.resolve_vars_if_possible(trait_predicate);
- trait_predicate.remap_constness_diag(obligation.param_env);
- let predicate_is_const = ty::BoundConstness::ConstIfConst
- == trait_predicate.skip_binder().constness;
+ // FIXME(effects)
+ let predicate_is_const = false;
if self.tcx.sess.has_errors().is_some()
&& trait_predicate.references_error()
@@ -704,9 +703,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
.get_parent_trait_ref(obligation.cause.code())
.map(|(t, s)| {
(
- format!(" in `{}`", t),
- format!("within `{}`, ", t),
- s.map(|s| (format!("within this `{}`", t), s)),
+ format!(" in `{t}`"),
+ format!("within `{t}`, "),
+ s.map(|s| (format!("within this `{t}`"), s)),
)
})
.unwrap_or_default();
@@ -1050,8 +1049,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
report_object_safety_error(self.tcx, span, trait_def_id, violations)
}
- ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => {
- let found_kind = self.closure_kind(closure_substs).unwrap();
+ ty::PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => {
+ let found_kind = self.closure_kind(closure_args).unwrap();
self.report_closure_error(&obligation, closure_def_id, found_kind, kind)
}
@@ -1071,7 +1070,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 `{ty}` is not well-formed"),
)
}
}
@@ -1109,7 +1108,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
let mut diag = self.tcx.sess.struct_span_err(
span,
- format!("the constant `{}` is not of type `{}`", ct, ty),
+ format!("the constant `{ct}` is not of type `{ty}`"),
);
self.note_type_err(
&mut diag,
@@ -1627,19 +1626,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::TermKind::Ty(_) => Ty::new_projection(
self.tcx,
data.projection_ty.def_id,
- data.projection_ty.substs,
+ data.projection_ty.args,
)
.into(),
ty::TermKind::Const(ct) => ty::Const::new_unevaluated(
self.tcx,
ty::UnevaluatedConst {
def: data.projection_ty.def_id,
- substs: data.projection_ty.substs,
+ args: data.projection_ty.args,
},
ct.ty(),
)
.into(),
};
+ // FIXME(-Ztrait-solver=next): For diagnostic purposes, it would be nice
+ // to deeply normalize this type.
let normalized_term =
ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
@@ -1908,9 +1909,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
.all_impls(trait_pred.def_id())
.filter_map(|def_id| {
if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative
- || !trait_pred
- .skip_binder()
- .is_constness_satisfied_by(self.tcx.constness(def_id))
|| !self.tcx.is_user_visible_dep(def_id.krate)
{
return None;
@@ -1970,7 +1968,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
traits.sort();
traits.dedup();
// FIXME: this could use a better heuristic, like just checking
- // that substs[1..] is the same.
+ // that args[1..] is the same.
let all_traits_equal = traits.len() == 1;
let candidates: Vec<String> = candidates
@@ -1979,7 +1977,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if all_traits_equal {
format!("\n {}", c.self_ty())
} else {
- format!("\n {}", c)
+ format!("\n {c}")
}
})
.collect();
@@ -2016,7 +2014,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|| self.tcx.is_automatically_derived(def_id)
})
.filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
- .map(ty::EarlyBinder::subst_identity)
+ .map(ty::EarlyBinder::instantiate_identity)
.filter(|trait_ref| {
let self_ty = trait_ref.self_ty();
// Avoid mentioning type parameters.
@@ -2177,10 +2175,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
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
- );
+ let crate_msg =
+ format!("perhaps two different versions of crate `{trait_crate}` are being used?");
err.note(crate_msg);
suggested = true;
}
@@ -2265,7 +2261,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Pick the first substitution that still contains inference variables as the one
// we're going to emit an error for. If there are none (see above), fall back to
// a more general error.
- let subst = data.trait_ref.substs.iter().find(|s| s.has_non_region_infer());
+ let subst = data.trait_ref.args.iter().find(|s| s.has_non_region_infer());
let mut err = if let Some(subst) = subst {
self.emit_inference_failure_err(
@@ -2290,7 +2286,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&obligation.with(self.tcx, trait_ref),
);
let has_non_region_infer =
- trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_or_numeric_infer());
+ trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer());
// It doesn't make sense to talk about applicable impls if there are more
// than a handful of them.
if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
@@ -2308,7 +2304,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());
if impl_candidates.len() < 10 {
@@ -2328,7 +2324,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
}
- if let Some(ty::subst::GenericArgKind::Type(_)) = subst.map(|subst| subst.unpack())
+ if let Some(ty::GenericArgKind::Type(_)) = subst.map(|subst| subst.unpack())
&& let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
{
let mut expr_finder = FindExprBySpan::new(span);
@@ -2372,7 +2368,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let Some(local_def_id) = data.trait_ref.def_id.as_local()
&& let Some(hir::Node::Item(hir::Item { ident: trait_name, kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), .. })) = self.tcx.hir().find_by_def_id(local_def_id)
&& let Some(method_ref) = trait_item_refs.iter().find(|item_ref| item_ref.ident == *assoc_item_name) {
- err.span_label(method_ref.span, format!("`{}::{}` defined here", trait_name, assoc_item_name));
+ err.span_label(method_ref.span, format!("`{trait_name}::{assoc_item_name}` defined here"));
}
err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
@@ -2386,14 +2382,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// If there is only one implementation of the trait, suggest using it.
// Otherwise, use a placeholder comment for the implementation.
let (message, impl_suggestion) = if non_blanket_impl_count == 1 {(
- "use the fully-qualified path to the only available implementation".to_string(),
- format!("<{} as ", self.tcx.type_of(impl_def_id).subst_identity())
- )} else {(
- format!(
- "use a fully-qualified path to a specific available implementation ({} found)",
- non_blanket_impl_count
- ),
- "</* self type */ as ".to_string()
+ "use the fully-qualified path to the only available implementation",
+ format!("<{} as ", self.tcx.type_of(impl_def_id).instantiate_identity())
+ )} else {
+ ("use a fully-qualified path to a specific available implementation",
+ "</* self type */ as ".to_string()
)};
let mut suggestions = vec![(
path.span.shrink_to_lo(),
@@ -2464,7 +2457,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
let subst = data
.projection_ty
- .substs
+ .args
.iter()
.chain(Some(data.term.into_arg()))
.find(|g| g.has_non_region_infer());
@@ -2476,7 +2469,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
@@ -2487,7 +2480,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
}
}
@@ -2515,7 +2508,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
}
}
@@ -2530,7 +2523,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
}
};
@@ -2567,7 +2560,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
}
- let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
+ let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect();
crate_names.sort();
crate_names.dedup();
post.sort();
@@ -2594,7 +2587,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
predicate
);
let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
- format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::<Vec<_>>().join("\n"),)
+ format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),)
} else if post.len() == 1 {
format!(": `{}`", post[0])
} else {
@@ -2603,7 +2596,7 @@ 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,));
@@ -2706,10 +2699,17 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
) {
- let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = obligation.predicate.kind().skip_binder() else { return; };
+ let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
+ obligation.predicate.kind().skip_binder()
+ else {
+ return;
+ };
let (ObligationCauseCode::BindingObligation(item_def_id, span)
- | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..))
- = *obligation.cause.code().peel_derives() else { return; };
+ | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..)) =
+ *obligation.cause.code().peel_derives()
+ else {
+ return;
+ };
debug!(?pred, ?item_def_id, ?span);
let (Some(node), true) = (
@@ -2767,7 +2767,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.span_suggestion_verbose(
span,
"consider relaxing the implicit `Sized` restriction",
- format!("{} ?Sized", separator),
+ format!("{separator} ?Sized"),
Applicability::MachineApplicable,
);
}
@@ -2820,9 +2820,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if obligated_types.iter().any(|ot| ot == &self_ty) {
return true;
}
- if let ty::Adt(def, substs) = self_ty.kind()
- && let [arg] = &substs[..]
- && let ty::subst::GenericArgKind::Type(ty) = arg.unpack()
+ if let ty::Adt(def, args) = self_ty.kind()
+ && let [arg] = &args[..]
+ && let ty::GenericArgKind::Type(ty) = arg.unpack()
&& let ty::Adt(inner_def, _) = ty.kind()
&& inner_def == def
{
@@ -2858,7 +2858,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
})
.unwrap_or_else(|| {
- format!("the trait bound `{}` is not satisfied{}", trait_predicate, post_message)
+ format!("the trait bound `{trait_predicate}` is not satisfied{post_message}")
})
}
@@ -2874,14 +2874,20 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
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),
+ dst: trait_ref.args.type_at(0),
+ src: trait_ref.args.type_at(1),
+ };
+ let scope = trait_ref.args.type_at(2);
+ let Some(assume) = rustc_transmute::Assume::from_const(
+ self.infcx.tcx,
+ obligation.param_env,
+ trait_ref.args.const_at(3),
+ ) else {
+ span_bug!(
+ span,
+ "Unable to construct rustc_transmute::Assume where it was previously possible"
+ );
};
- let scope = trait_ref.substs.type_at(2);
- let Some(assume) =
- rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, trait_ref.substs.const_at(3)) else {
- span_bug!(span, "Unable to construct rustc_transmute::Assume where it was previously possible");
- };
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
obligation.cause,
@@ -2890,8 +2896,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
assume,
) {
Answer::No(reason) => {
- let dst = trait_ref.substs.type_at(0);
- let src = trait_ref.substs.type_at(1);
+ let dst = trait_ref.args.type_at(0);
+ let src = trait_ref.args.type_at(1);
let err_msg = format!(
"`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`"
);
@@ -2982,12 +2988,19 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
unsatisfied_const: bool,
) {
let body_def_id = obligation.cause.body_id;
+ let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } =
+ obligation.cause.code()
+ {
+ *rhs_span
+ } else {
+ span
+ };
+
// 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,
)
{
@@ -3021,8 +3034,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.report_similar_impl_candidates_for_root_obligation(&obligation, *trait_predicate, body_def_id, err);
}
- self.maybe_suggest_convert_to_slice(
+ self.suggest_convert_to_slice(
err,
+ obligation,
trait_ref,
impl_candidates.as_slice(),
span,
@@ -3058,7 +3072,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Note any argument mismatches
let given_ty = params.skip_binder();
- let expected_ty = trait_ref.skip_binder().substs.type_at(1);
+ let expected_ty = trait_ref.skip_binder().args.type_at(1);
if let ty::Tuple(given) = given_ty.kind()
&& let ty::Tuple(expected) = expected_ty.kind()
{
@@ -3089,34 +3103,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, '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,
+ _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(),
- ),
- );
- }
- }
+ let unsatisfied_const = UnsatisfiedConst(false);
+ // FIXME(effects)
unsatisfied_const
}
@@ -3128,24 +3122,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
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(
+ let mut err = ClosureKindMismatch {
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),
- );
+ expected: kind,
+ found: found_kind,
+ cause_span: obligation.cause.span,
+ fn_once_label: None,
+ fn_mut_label: None,
+ };
// Additional context information explaining why the closure only implements
// a particular trait.
@@ -3153,30 +3138,22 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
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)
- ),
- );
+ err.fn_once_label = Some(ClosureFnOnceLabel {
+ span: *span,
+ place: 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_mut_label = Some(ClosureFnMutLabel {
+ span: *span,
+ place: ty::place_to_string_for_capture(self.tcx, &place),
+ })
}
_ => {}
}
}
- err
+ self.tcx.sess.create_err(err)
}
fn report_type_parameter_mismatch_cyclic_type_error(
@@ -3273,7 +3250,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut not_tupled = false;
- let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() {
+ let found = match found_trait_ref.skip_binder().args.type_at(1).kind() {
ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()],
_ => {
not_tupled = true;
@@ -3281,7 +3258,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
};
- let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1);
+ let expected_ty = expected_trait_ref.skip_binder().args.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()
@@ -3371,8 +3348,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
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
+ "try adding a `where` bound using this expression: `where [(); {snippet}]:`"
)),
_ => err.help("consider adding a `where` bound using this expression"),
};
@@ -3554,7 +3530,7 @@ pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &
.1
.expect("proof tree should have been generated");
let mut lock = std::io::stdout().lock();
- let _ = lock.write_fmt(format_args!("{tree:?}"));
+ let _ = lock.write_fmt(format_args!("{tree:?}\n"));
let _ = lock.flush();
});
}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index b16d2eb5f..0e73bad19 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
@@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{struct_span_err, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-use rustc_middle::ty::SubstsRef;
+use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
use rustc_span::symbol::{kw, sym, Symbol};
@@ -25,7 +25,7 @@ pub trait TypeErrCtxtExt<'tcx> {
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
- ) -> Option<(DefId, SubstsRef<'tcx>)>;
+ ) -> Option<(DefId, GenericArgsRef<'tcx>)>;
/*private*/
fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>;
@@ -56,7 +56,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
- ) -> Option<(DefId, SubstsRef<'tcx>)> {
+ ) -> Option<(DefId, GenericArgsRef<'tcx>)> {
let tcx = self.tcx;
let param_env = obligation.param_env;
let trait_ref = self.instantiate_binder_with_placeholders(trait_ref);
@@ -66,26 +66,23 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut fuzzy_match_impls = vec![];
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
- let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
- let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
+ let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
+ let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
let impl_self_ty = impl_trait_ref.self_ty();
if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
- self_match_impls.push((def_id, impl_substs));
+ self_match_impls.push((def_id, impl_args));
- if iter::zip(
- trait_ref.substs.types().skip(1),
- impl_trait_ref.substs.types().skip(1),
- )
- .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
+ if iter::zip(trait_ref.args.types().skip(1), impl_trait_ref.args.types().skip(1))
+ .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
{
- fuzzy_match_impls.push((def_id, impl_substs));
+ fuzzy_match_impls.push((def_id, impl_args));
}
}
});
- let impl_def_id_and_substs = if self_match_impls.len() == 1 {
+ let impl_def_id_and_args = if self_match_impls.len() == 1 {
self_match_impls[0]
} else if fuzzy_match_impls.len() == 1 {
fuzzy_match_impls[0]
@@ -93,8 +90,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return None;
};
- tcx.has_attr(impl_def_id_and_substs.0, sym::rustc_on_unimplemented)
- .then_some(impl_def_id_and_substs)
+ tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
+ .then_some(impl_def_id_and_args)
}
/// Used to set on_unimplemented's `ItemContext`
@@ -143,9 +140,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> OnUnimplementedNote {
- let (def_id, substs) = self
+ let (def_id, args) = self
.impl_similar_to(trait_ref, obligation)
- .unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().substs));
+ .unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().args));
let trait_ref = trait_ref.skip_binder();
let mut flags = vec![];
@@ -173,7 +170,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let Some(k) = obligation.cause.span.desugaring_kind() {
flags.push((sym::from_desugaring, None));
- flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
+ flags.push((sym::from_desugaring, Some(format!("{k:?}"))));
}
if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
@@ -192,14 +189,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// signature with no type arguments resolved
flags.push((
sym::_Self,
- Some(self.tcx.type_of(def.did()).subst_identity().to_string()),
+ Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
));
}
for param in generics.params.iter() {
let value = match param.kind {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
- substs[param.index as usize].to_string()
+ args[param.index as usize].to_string()
}
GenericParamDefKind::Lifetime => continue,
};
@@ -207,13 +204,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
flags.push((name, Some(value)));
if let GenericParamDefKind::Type { .. } = param.kind {
- let param_ty = substs[param.index as usize].expect_ty();
+ let param_ty = args[param.index as usize].expect_ty();
if let Some(def) = param_ty.ty_adt_def() {
// We also want to be able to select the parameter's
// original signature with no type arguments resolved
flags.push((
name,
- Some(self.tcx.type_of(def.did()).subst_identity().to_string()),
+ Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
));
}
}
@@ -249,7 +246,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// signature with no type arguments resolved
flags.push((
sym::_Self,
- Some(format!("[{}]", self.tcx.type_of(def.did()).subst_identity())),
+ Some(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())),
));
}
if aty.is_integral() {
@@ -261,14 +258,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let ty::Array(aty, len) = self_ty.kind() {
flags.push((sym::_Self, Some("[]".to_string())));
let len = len.try_to_value().and_then(|v| v.try_to_target_usize(self.tcx));
- flags.push((sym::_Self, Some(format!("[{}; _]", aty))));
+ flags.push((sym::_Self, Some(format!("[{aty}; _]"))));
if let Some(n) = len {
- flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n))));
+ flags.push((sym::_Self, Some(format!("[{aty}; {n}]"))));
}
if let Some(def) = aty.ty_adt_def() {
// We also want to be able to select the array's type's original
// signature with no type arguments resolved
- let def_ty = self.tcx.type_of(def.did()).subst_identity();
+ let def_ty = self.tcx.type_of(def.did()).instantiate_identity();
flags.push((sym::_Self, Some(format!("[{def_ty}; _]"))));
if let Some(n) = len {
flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]"))));
@@ -332,18 +329,13 @@ pub struct OnUnimplementedNote {
}
/// Append a message for `~const Trait` errors.
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum AppendConstMessage {
+ #[default]
Default,
Custom(Symbol),
}
-impl Default for AppendConstMessage {
- fn default() -> Self {
- AppendConstMessage::Default
- }
-}
-
impl<'tcx> OnUnimplementedDirective {
fn parse(
tcx: TyCtxt<'tcx>,
@@ -587,7 +579,7 @@ impl<'tcx> OnUnimplementedFormatString {
"there is no parameter `{}` on {}",
s,
if trait_def_id == item_def_id {
- format!("trait `{}`", trait_name)
+ format!("trait `{trait_name}`")
} else {
"impl".to_string()
}
@@ -629,7 +621,7 @@ impl<'tcx> OnUnimplementedFormatString {
.filter_map(|param| {
let value = match param.kind {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
- trait_ref.substs[param.index as usize].to_string()
+ trait_ref.args[param.index as usize].to_string()
}
GenericParamDefKind::Lifetime => return None,
};
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 9ac1ba027..611ec6b00 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -5,6 +5,7 @@ use super::{
PredicateObligation,
};
+use crate::errors;
use crate::infer::InferCtxt;
use crate::traits::{NormalizeExt, ObligationCtxt};
@@ -30,7 +31,7 @@ use rustc_middle::hir::map;
use rustc_middle::ty::error::TypeError::{self, Sorts};
use rustc_middle::ty::{
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind,
- GeneratorDiagnosticData, GeneratorInteriorTypeCause, InferTy, InternalSubsts, IsSuggestable,
+ GeneratorDiagnosticData, GeneratorInteriorTypeCause, GenericArgs, InferTy, IsSuggestable,
ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeVisitableExt, TypeckResults,
};
@@ -40,7 +41,6 @@ use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
use rustc_target::spec::abi;
use std::borrow::Cow;
use std::iter;
-use std::ops::Deref;
use super::InferCtxtPrivExt;
use crate::infer::InferCtxtExt as _;
@@ -398,9 +398,10 @@ pub trait TypeErrCtxtExt<'tcx> {
param_env: ty::ParamEnv<'tcx>,
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>;
- fn maybe_suggest_convert_to_slice(
+ fn suggest_convert_to_slice(
&self,
err: &mut Diagnostic,
+ obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
candidate_impls: &[ImplCandidate<'tcx>],
span: Span,
@@ -435,7 +436,7 @@ fn suggest_restriction<'tcx>(
) {
if hir_generics.where_clause_span.from_expansion()
|| hir_generics.where_clause_span.desugaring_kind().is_some()
- || projection.is_some_and(|projection| tcx.opt_rpitit_info(projection.def_id).is_some())
+ || projection.is_some_and(|projection| tcx.is_impl_trait_in_trait(projection.def_id))
{
return;
}
@@ -479,13 +480,13 @@ fn suggest_restriction<'tcx>(
.visit_ty(input);
}
// The type param `T: Trait` we will suggest to introduce.
- let type_param = format!("{}: {}", type_param_name, bound_str);
+ let type_param = format!("{type_param_name}: {bound_str}");
let mut sugg = vec![
if let Some(span) = hir_generics.span_for_param_suggestion() {
- (span, format!(", {}", type_param))
+ (span, format!(", {type_param}"))
} else {
- (hir_generics.span, format!("<{}>", type_param))
+ (hir_generics.span, format!("<{type_param}>"))
},
// `fn foo(t: impl Trait)`
// ^ suggest `where <T as Trait>::A: Bound`
@@ -530,7 +531,7 @@ fn suggest_restriction<'tcx>(
err.span_suggestion_verbose(
sp,
- format!("consider further restricting {}", msg),
+ format!("consider further restricting {msg}"),
suggestion,
Applicability::MachineApplicable,
);
@@ -654,6 +655,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| hir::ItemKind::Impl(hir::Impl { generics, .. })
| hir::ItemKind::Fn(_, generics, _)
| hir::ItemKind::TyAlias(_, generics)
+ | hir::ItemKind::Const(_, generics, _)
| hir::ItemKind::TraitAlias(generics, _)
| hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
..
@@ -670,7 +672,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// this that we do in `suggest_restriction` and pull the
// `impl Trait` into a new generic if it shows up somewhere
// else in the predicate.
- if !trait_pred.skip_binder().trait_ref.substs[1..]
+ if !trait_pred.skip_binder().trait_ref.args[1..]
.iter()
.all(|g| g.is_suggestable(self.tcx, false))
{
@@ -693,7 +695,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
term
);
} else {
- constraint.push_str(&format!("<{} = {}>", name, term));
+ constraint.push_str(&format!("<{name} = {term}>"));
}
}
@@ -719,6 +721,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| hir::ItemKind::Impl(hir::Impl { generics, .. })
| hir::ItemKind::Fn(_, generics, _)
| hir::ItemKind::TyAlias(_, generics)
+ | hir::ItemKind::Const(_, generics, _)
| hir::ItemKind::TraitAlias(generics, _)
| hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
..
@@ -752,14 +755,20 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
// It only make sense when suggesting dereferences for arguments
- let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, call_hir_id, .. } = obligation.cause.code()
- else { return false; };
- let Some(typeck_results) = &self.typeck_results
- else { return false; };
- let hir::Node::Expr(expr) = self.tcx.hir().get(*arg_hir_id)
- else { return false; };
- let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr)
- else { return false; };
+ let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, call_hir_id, .. } =
+ obligation.cause.code()
+ else {
+ return false;
+ };
+ let Some(typeck_results) = &self.typeck_results else {
+ return false;
+ };
+ let hir::Node::Expr(expr) = self.tcx.hir().get(*arg_hir_id) else {
+ return false;
+ };
+ let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) else {
+ return false;
+ };
let span = obligation.cause.span;
let mut real_trait_pred = trait_pred;
@@ -770,18 +779,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
real_trait_pred = parent_trait_pred;
}
- let real_ty = real_trait_pred.self_ty();
// We `erase_late_bound_regions` here because `make_subregion` does not handle
// `ReLateBound`, and we don't particularly care about the regions.
- if !self.can_eq(
- obligation.param_env,
- self.tcx.erase_late_bound_regions(real_ty),
- arg_ty,
- ) {
+ let real_ty = self.tcx.erase_late_bound_regions(real_trait_pred.self_ty());
+ if !self.can_eq(obligation.param_env, real_ty, arg_ty) {
continue;
}
- if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() {
+ if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
let autoderef = (self.autoderef_steps)(base_ty);
if let Some(steps) =
autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| {
@@ -933,11 +938,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_pred.self_ty(),
);
- let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(
- obligation.cause.body_id,
- obligation.param_env,
- self_ty,
- ) else { return false; };
+ let Some((def_id_or_name, output, inputs)) =
+ self.extract_callable_info(obligation.cause.body_id, obligation.param_env, self_ty)
+ else {
+ return false;
+ };
// Remapping bound vars here
let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output));
@@ -1012,7 +1017,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let name = self.tcx.def_path_str(def_id);
err.span_label(
self.tcx.def_span(def_id),
- format!("consider calling the constructor for `{}`", name),
+ format!("consider calling the constructor for `{name}`"),
);
name
}
@@ -1035,26 +1040,40 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
span.remove_mark();
}
let mut expr_finder = FindExprBySpan::new(span);
- let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return; };
+ let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else {
+ return;
+ };
let body = self.tcx.hir().body(body_id);
expr_finder.visit_expr(body.value);
- let Some(expr) = expr_finder.result else { return; };
- let Some(typeck) = &self.typeck_results else { return; };
- let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else { return; };
+ let Some(expr) = expr_finder.result else {
+ return;
+ };
+ let Some(typeck) = &self.typeck_results else {
+ return;
+ };
+ let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else {
+ return;
+ };
if !ty.is_unit() {
return;
};
- let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; };
- let hir::def::Res::Local(hir_id) = path.res else { return; };
+ let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {
+ return;
+ };
+ let hir::def::Res::Local(hir_id) = path.res else {
+ return;
+ };
let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else {
return;
};
- let Some(hir::Node::Local(hir::Local {
- ty: None,
- init: Some(init),
- ..
- })) = self.tcx.hir().find_parent(pat.hir_id) else { return; };
- let hir::ExprKind::Block(block, None) = init.kind else { return; };
+ let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) =
+ self.tcx.hir().find_parent(pat.hir_id)
+ else {
+ return;
+ };
+ let hir::ExprKind::Block(block, None) = init.kind else {
+ return;
+ };
if block.expr.is_some() {
return;
}
@@ -1062,7 +1081,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.span_label(block.span, "this empty block is missing a tail expression");
return;
};
- let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; };
+ let hir::StmtKind::Semi(tail_expr) = stmt.kind else {
+ return;
+ };
let Some(ty) = typeck.expr_ty_opt(tail_expr) else {
err.span_label(block.span, "this block is missing a tail expression");
return;
@@ -1092,12 +1113,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
) -> bool {
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
let ty = self.instantiate_binder_with_placeholders(self_ty);
- let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else { return false };
+ let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else {
+ return false;
+ };
let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
let ty::Param(param) = inner_ty.kind() else { return false };
- let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() else { return false };
+ let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
+ obligation.cause.code()
+ else {
+ return false;
+ };
let arg_node = self.tcx.hir().get(*arg_hir_id);
- let Node::Expr(Expr { kind: hir::ExprKind::Path(_), ..}) = arg_node else { return false };
+ let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else { return false };
let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None);
let has_clone = |ty| {
@@ -1143,24 +1170,33 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
found: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
// Autoderef is useful here because sometimes we box callables, etc.
- let Some((def_id_or_name, output, inputs)) = (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| {
- match *found.kind() {
- ty::FnPtr(fn_sig) =>
- Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
- ty::FnDef(def_id, _) => {
- let fn_sig = found.fn_sig(self.tcx);
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
- }
- ty::Closure(def_id, substs) => {
- let fn_sig = substs.as_closure().sig();
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
- }
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- self.tcx.item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
+ let Some((def_id_or_name, output, inputs)) =
+ (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| {
+ match *found.kind() {
+ ty::FnPtr(fn_sig) => Some((
+ DefIdOrName::Name("function pointer"),
+ fn_sig.output(),
+ fn_sig.inputs(),
+ )),
+ ty::FnDef(def_id, _) => {
+ let fn_sig = found.fn_sig(self.tcx);
+ Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
+ }
+ ty::Closure(def_id, args) => {
+ let fn_sig = args.as_closure().sig();
+ Some((
+ DefIdOrName::DefId(def_id),
+ fn_sig.output(),
+ fn_sig.inputs().map_bound(|inputs| &inputs[1..]),
+ ))
+ }
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
+ self.tcx.item_bounds(def_id).instantiate(self.tcx, args).iter().find_map(
+ |pred| {
+ if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ // args tuple will always be args[1]
+ && let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind()
{
Some((
DefIdOrName::DefId(def_id),
@@ -1170,14 +1206,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
} else {
None
}
- })
- }
- ty::Dynamic(data, _, ty::Dyn) => {
- data.iter().find_map(|pred| {
- if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
+ },
+ )
+ }
+ ty::Dynamic(data, _, ty::Dyn) => {
+ data.iter().find_map(|pred| {
+ if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
&& Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
- // for existential projection, substs are shifted over by 1
- && let ty::Tuple(args) = proj.substs.type_at(0).kind()
+ // for existential projection, args are shifted over by 1
+ && let ty::Tuple(args) = proj.args.type_at(0).kind()
{
Some((
DefIdOrName::Name("trait object"),
@@ -1187,11 +1224,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
} else {
None
}
- })
- }
- ty::Param(param) => {
- let generics = self.tcx.generics_of(body_id);
- let name = if generics.count() > param.index as usize
+ })
+ }
+ ty::Param(param) => {
+ let generics = self.tcx.generics_of(body_id);
+ let name = if generics.count() > param.index as usize
&& let def = generics.param_at(param.index as usize, self.tcx)
&& matches!(def.kind, ty::GenericParamDefKind::Type { .. })
&& def.name == param.name
@@ -1200,12 +1237,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
} else {
DefIdOrName::Name("type parameter")
};
- param_env.caller_bounds().iter().find_map(|pred| {
- if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
+ param_env.caller_bounds().iter().find_map(|pred| {
+ if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
&& proj.projection_ty.self_ty() == found
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ // args tuple will always be args[1]
+ && let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind()
{
Some((
name,
@@ -1215,11 +1252,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
} else {
None
}
- })
+ })
+ }
+ _ => None,
}
- _ => None,
- }
- }) else { return None; };
+ })
+ else {
+ return None;
+ };
let output = self.instantiate_binder_with_fresh_vars(
DUMMY_SP,
@@ -1356,7 +1396,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Because of this, we modify the error to refer to the original obligation and
// return early in the caller.
- let msg = format!("the trait bound `{}` is not satisfied", old_pred);
+ let msg = format!("the trait bound `{old_pred}` is not satisfied");
if has_custom_message {
err.note(msg);
} else {
@@ -1389,30 +1429,34 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Issue #109436, we need to add parentheses properly for method calls
// for example, `foo.into()` should be `(&foo).into()`
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(
- self.tcx.sess.source_map().span_look_ahead(span, Some("."), Some(50)),
- ) {
- if snippet == "." {
- err.multipart_suggestion_verbose(
- sugg_msg,
- vec![
- (span.shrink_to_lo(), format!("({}", sugg_prefix)),
- (span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- return true;
- }
+ if let Some(_) =
+ self.tcx.sess.source_map().span_look_ahead(span, ".", Some(50))
+ {
+ err.multipart_suggestion_verbose(
+ sugg_msg,
+ vec![
+ (span.shrink_to_lo(), format!("({sugg_prefix}")),
+ (span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ return true;
}
// Issue #104961, we need to add parentheses properly for 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; };
+ let Some(body_id) =
+ self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
+ else {
+ return false;
+ };
let body = self.tcx.hir().body(body_id);
let mut expr_finder = FindExprBySpan::new(span);
expr_finder.visit_expr(body.value);
- let Some(expr) = expr_finder.result else { return false; };
+ let Some(expr) = expr_finder.result else {
+ return false;
+ };
let needs_parens = match expr.kind {
// parenthesize if needed (Issue #46756)
hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
@@ -1423,10 +1467,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let span = if needs_parens { span } else { span.shrink_to_lo() };
let suggestions = if !needs_parens {
- vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))]
+ vec![(span.shrink_to_lo(), sugg_prefix)]
} else {
vec![
- (span.shrink_to_lo(), format!("{}(", sugg_prefix)),
+ (span.shrink_to_lo(), format!("{sugg_prefix}(")),
(span.shrink_to_hi(), ")".to_string()),
]
};
@@ -1463,8 +1507,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self_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 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 = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty);
for predicate in predicates.iter() {
@@ -1566,7 +1614,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
// Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
- let Some(mut expr) = expr_finder.result else { return false; };
+ let Some(mut expr) = expr_finder.result else {
+ return false;
+ };
let mut count = 0;
let mut suggestions = vec![];
// Skipping binder here, remapping below
@@ -1646,13 +1696,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
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 ty::FnDef(def_id, _args) = 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
+ "alternatively, consider making `fn {ident}` asynchronous"
);
if vis_span.is_empty() {
err.span_suggestion_verbose(
@@ -1798,7 +1847,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
let hir = self.tcx.hir();
- let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) = hir.find_by_def_id(obligation.cause.body_id) else {
+ let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) =
+ hir.find_by_def_id(obligation.cause.body_id)
+ else {
return None;
};
@@ -1901,10 +1952,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// don't print out the [type error] here
err.delay_as_bug();
} else {
- err.span_label(
- expr.span,
- format!("this returned value is of type `{}`", ty),
- );
+ err.span_label(expr.span, format!("this returned value is of type `{ty}`"));
}
}
}
@@ -1925,7 +1973,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
infcx: &InferCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Ty<'tcx> {
- let inputs = trait_ref.skip_binder().substs.type_at(1);
+ let inputs = trait_ref.skip_binder().args.type_at(1);
let sig = match inputs.kind() {
ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id()) => {
infcx.tcx.mk_fn_sig(
@@ -2006,12 +2054,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
let expected_self =
self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.self_ty()));
- let expected_substs = self
+ let expected_args = self
.tcx
- .anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.substs));
+ .anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.args));
// Find another predicate whose self-type is equal to the expected self type,
- // but whose substs don't match.
+ // but whose args don't match.
let other_pred = predicates.into_iter()
.enumerate()
.find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
@@ -2024,10 +2072,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
== self.tcx.anonymize_bound_vars(
pred.kind().rebind(trait_pred.self_ty()),
)
- // But the substs don't match (i.e. incompatible args)
- && expected_substs
+ // But the args don't match (i.e. incompatible args)
+ && expected_args
!= self.tcx.anonymize_bound_vars(
- pred.kind().rebind(trait_pred.trait_ref.substs),
+ pred.kind().rebind(trait_pred.trait_ref.args),
) =>
{
true
@@ -2222,7 +2270,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Only continue if a generator was found.
debug!(?generator, ?trait_ref, ?target_ty);
- let (Some(generator_did), Some(trait_ref), Some(target_ty)) = (generator, trait_ref, target_ty) else {
+ let (Some(generator_did), Some(trait_ref), Some(target_ty)) =
+ (generator, trait_ref, target_ty)
+ else {
return false;
};
@@ -2403,8 +2453,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.clear_code();
err.set_primary_message(format!(
- "{} cannot be {} between threads safely",
- future_or_generator, trait_verb
+ "{future_or_generator} cannot be {trait_verb} between threads safely"
));
let original_span = err.span.primary_span().unwrap();
@@ -2413,7 +2462,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let message = outer_generator
.and_then(|generator_did| {
Some(match self.tcx.generator_kind(generator_did).unwrap() {
- GeneratorKind::Gen => format!("generator is not {}", trait_name),
+ GeneratorKind::Gen => format!("generator is not {trait_name}"),
GeneratorKind::Async(AsyncGeneratorKind::Fn) => self
.tcx
.parent(generator_did)
@@ -2421,73 +2470,73 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
.map(|parent_did| hir.local_def_id_to_hir_id(parent_did))
.and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
.map(|name| {
- format!("future returned by `{}` is not {}", name, trait_name)
+ format!("future returned by `{name}` is not {trait_name}")
})?,
GeneratorKind::Async(AsyncGeneratorKind::Block) => {
- format!("future created by async block is not {}", trait_name)
+ format!("future created by async block is not {trait_name}")
}
GeneratorKind::Async(AsyncGeneratorKind::Closure) => {
- format!("future created by async closure is not {}", trait_name)
+ format!("future created by async closure is not {trait_name}")
}
})
})
- .unwrap_or_else(|| format!("{} is not {}", future_or_generator, trait_name));
+ .unwrap_or_else(|| format!("{future_or_generator} is not {trait_name}"));
span.push_span_label(original_span, message);
err.set_span(span);
- format!("is not {}", trait_name)
+ format!("is not {trait_name}")
} else {
format!("does not implement `{}`", trait_pred.print_modifiers_and_trait_path())
};
- let mut explain_yield =
- |interior_span: Span, yield_span: Span, scope_span: Option<Span>| {
- let mut span = MultiSpan::from_span(yield_span);
- let snippet = match source_map.span_to_snippet(interior_span) {
- // #70935: If snippet contains newlines, display "the value" instead
- // so that we do not emit complex diagnostics.
- Ok(snippet) if !snippet.contains('\n') => format!("`{}`", snippet),
- _ => "the value".to_string(),
- };
- // note: future is not `Send` as this value is used across an await
- // --> $DIR/issue-70935-complex-spans.rs:13:9
- // |
- // LL | baz(|| async {
- // | ______________-
- // | |
- // | |
- // LL | | foo(tx.clone());
- // LL | | }).await;
- // | | - ^^^^^^ await occurs here, with value maybe used later
- // | |__________|
- // | has type `closure` which is not `Send`
- // note: value is later dropped here
- // LL | | }).await;
- // | | ^
- //
- span.push_span_label(
- yield_span,
- format!("{} occurs here, with {} maybe used later", await_or_yield, snippet),
- );
- span.push_span_label(
- interior_span,
- format!("has type `{}` which {}", target_ty, trait_explanation),
- );
- if let Some(scope_span) = scope_span {
- let scope_span = source_map.end_point(scope_span);
+ let mut explain_yield = |interior_span: Span,
+ yield_span: Span,
+ scope_span: Option<Span>| {
+ let mut span = MultiSpan::from_span(yield_span);
+ let snippet = match source_map.span_to_snippet(interior_span) {
+ // #70935: If snippet contains newlines, display "the value" instead
+ // so that we do not emit complex diagnostics.
+ Ok(snippet) if !snippet.contains('\n') => format!("`{snippet}`"),
+ _ => "the value".to_string(),
+ };
+ // note: future is not `Send` as this value is used across an await
+ // --> $DIR/issue-70935-complex-spans.rs:13:9
+ // |
+ // LL | baz(|| async {
+ // | ______________-
+ // | |
+ // | |
+ // LL | | foo(tx.clone());
+ // LL | | }).await;
+ // | | - ^^^^^^ await occurs here, with value maybe used later
+ // | |__________|
+ // | has type `closure` which is not `Send`
+ // note: value is later dropped here
+ // LL | | }).await;
+ // | | ^
+ //
+ span.push_span_label(
+ yield_span,
+ format!("{await_or_yield} occurs here, with {snippet} maybe used later"),
+ );
+ span.push_span_label(
+ interior_span,
+ format!("has type `{target_ty}` which {trait_explanation}"),
+ );
+ if let Some(scope_span) = scope_span {
+ let scope_span = source_map.end_point(scope_span);
- let msg = format!("{} is later dropped here", snippet);
- span.push_span_label(scope_span, msg);
- }
- err.span_note(
+ let msg = format!("{snippet} is later dropped here");
+ span.push_span_label(scope_span, msg);
+ }
+ err.span_note(
span,
format!(
- "{} {} as this value is used across {}",
- future_or_generator, trait_explanation, an_await_or_yield
+ "{future_or_generator} {trait_explanation} as this value is used across {an_await_or_yield}"
),
);
- };
+ };
match interior_or_upvar_span {
GeneratorInteriorOrUpvar::Interior(interior_span, interior_extra_info) => {
if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info {
@@ -2497,15 +2546,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
span.push_span_label(
await_span,
format!(
- "await occurs here on type `{}`, which {}",
- target_ty, trait_explanation
+ "await occurs here on type `{target_ty}`, which {trait_explanation}"
),
);
err.span_note(
span,
format!(
- "future {not_trait} as it awaits another future which {not_trait}",
- not_trait = trait_explanation
+ "future {trait_explanation} as it awaits another future which {trait_explanation}"
),
);
} else {
@@ -2588,18 +2635,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let ref_kind = if is_mut { "&mut" } else { "&" };
(
format!(
- "has type `{}` which {}, because `{}` is not `{}`",
- target_ty, trait_explanation, ref_ty, ref_ty_trait
+ "has type `{target_ty}` which {trait_explanation}, because `{ref_ty}` is not `{ref_ty_trait}`"
),
format!(
- "captured value {} because `{}` references cannot be sent unless their referent is `{}`",
- trait_explanation, ref_kind, ref_ty_trait
+ "captured value {trait_explanation} because `{ref_kind}` references cannot be sent unless their referent is `{ref_ty_trait}`"
),
)
}
None => (
- format!("has type `{}` which {}", target_ty, trait_explanation),
- format!("captured value {}", trait_explanation),
+ format!("has type `{target_ty}` which {trait_explanation}"),
+ format!("captured value {trait_explanation}"),
),
};
@@ -2655,7 +2700,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::MatchImpl(..)
| ObligationCauseCode::ReturnType
| ObligationCauseCode::ReturnValue(_)
- | ObligationCauseCode::BlockTailExpression(_)
+ | ObligationCauseCode::BlockTailExpression(..)
| ObligationCauseCode::AwaitableExpr(_)
| ObligationCauseCode::ForLoopIterator
| ObligationCauseCode::QuestionMark
@@ -2688,8 +2733,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
ObligationCauseCode::ObjectTypeBound(object_ty, region) => {
err.note(format!(
- "required so that the lifetime bound of `{}` for `{}` is satisfied",
- region, object_ty,
+ "required so that the lifetime bound of `{region}` for `{object_ty}` is satisfied",
));
}
ObligationCauseCode::ItemObligation(_)
@@ -2743,7 +2787,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// implement this trait and list them.
err.note(format!(
"`{short_item_name}` is a \"sealed trait\", because to implement \
- it you also need to implelement `{}`, which is not accessible; \
+ it you also need to implement `{}`, which is not accessible; \
this is usually done to force you to use one of the provided \
types that already implement it",
with_no_trimmed_paths!(tcx.def_path_str(def_id)),
@@ -2808,7 +2852,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.note("all local variables must have a statically known size");
}
Some(Node::Local(hir::Local {
- init: Some(hir::Expr { kind: hir::ExprKind::Index(_, _), span, .. }),
+ init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }),
..
})) => {
// When encountering an assignment of an unsized trait, like
@@ -3009,11 +3053,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut msg =
"required because it captures the following types: ".to_owned();
for ty in bound_tys.skip_binder() {
- with_forced_trimmed_paths!(write!(msg, "`{}`, ", ty).unwrap());
+ with_forced_trimmed_paths!(write!(msg, "`{ty}`, ").unwrap());
}
err.note(msg.trim_end_matches(", ").to_string())
}
- ty::GeneratorWitnessMIR(def_id, substs) => {
+ ty::GeneratorWitnessMIR(def_id, args) => {
use std::fmt::Write;
// FIXME: this is kind of an unusual format for rustc, can we make it more clear?
@@ -3022,8 +3066,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut msg =
"required because it captures the following types: ".to_owned();
for bty in tcx.generator_hidden_types(*def_id) {
- let ty = bty.subst(tcx, substs);
- write!(msg, "`{}`, ", ty).unwrap();
+ let ty = bty.instantiate(tcx, args);
+ write!(msg, "`{ty}`, ").unwrap();
}
err.note(msg.trim_end_matches(", ").to_string())
}
@@ -3082,7 +3126,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ObligationCauseCode::ImplDerivedObligation(ref data) => {
let mut parent_trait_pred =
self.resolve_vars_if_possible(data.derived.parent_trait_pred);
- parent_trait_pred.remap_constness_diag(param_env);
let parent_def_id = parent_trait_pred.def_id();
let (self_ty, file) =
self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty());
@@ -3350,7 +3393,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
Ty::new_projection(
self.tcx,
item_def_id,
- // Future::Output has no substs
+ // Future::Output has no args
[trait_pred.self_ty()],
)
});
@@ -3391,7 +3434,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
_ => return,
};
if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind()
- && let ty::Infer(InferTy::IntVar(_)) = trait_ref.skip_binder().substs.type_at(1).kind()
+ && let ty::Infer(InferTy::IntVar(_)) = trait_ref.skip_binder().args.type_at(1).kind()
{
err.span_suggestion_verbose(
rhs_span.shrink_to_hi(),
@@ -3411,15 +3454,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
return;
};
- let (adt, substs) = match trait_pred.skip_binder().self_ty().kind() {
- ty::Adt(adt, substs) if adt.did().is_local() => (adt, substs),
+ let (adt, args) = match trait_pred.skip_binder().self_ty().kind() {
+ ty::Adt(adt, args) if adt.did().is_local() => (adt, args),
_ => return,
};
let can_derive = {
let is_derivable_trait = match diagnostic_name {
sym::Default => !adt.is_enum(),
sym::PartialEq | sym::PartialOrd => {
- let rhs_ty = trait_pred.skip_binder().trait_ref.substs.type_at(1);
+ let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1);
trait_pred.skip_binder().self_ty() == rhs_ty
}
sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
@@ -3428,8 +3471,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
is_derivable_trait &&
// Ensure all fields impl the trait.
adt.all_fields().all(|field| {
- let field_ty = field.ty(self.tcx, substs);
- let trait_substs = match diagnostic_name {
+ let field_ty = field.ty(self.tcx, args);
+ let trait_args = match diagnostic_name {
sym::PartialEq | sym::PartialOrd => {
Some(field_ty)
}
@@ -3438,7 +3481,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
trait_ref: ty::TraitRef::new(self.tcx,
trait_pred.def_id(),
- [field_ty].into_iter().chain(trait_substs),
+ [field_ty].into_iter().chain(trait_args),
),
..*tr
});
@@ -3459,7 +3502,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_pred.skip_binder().self_ty(),
diagnostic_name,
),
- format!("#[derive({})]\n", diagnostic_name),
+ format!("#[derive({diagnostic_name})]\n"),
Applicability::MaybeIncorrect,
);
}
@@ -3473,7 +3516,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
) {
if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code()
&& self.tcx.is_diagnostic_item(sym::SliceIndex, trait_pred.skip_binder().trait_ref.def_id)
- && let ty::Slice(_) = trait_pred.skip_binder().trait_ref.substs.type_at(1).kind()
+ && let ty::Slice(_) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind()
&& let ty::Ref(_, inner_ty, _) = trait_pred.skip_binder().self_ty().kind()
&& let ty::Uint(ty::UintTy::Usize) = inner_ty.kind()
{
@@ -3522,9 +3565,9 @@ 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)
+ if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code
+ && let Some(node_args) = typeck_results.node_args_opt(call_hir_id)
+ && let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args)
&& let Some(where_pred) = where_clauses.predicates.get(*idx)
{
if let Some(where_pred) = where_pred.as_trait_clause()
@@ -3538,7 +3581,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
let zipped =
- iter::zip(where_pred.trait_ref.substs, failed_pred.trait_ref.substs);
+ iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args);
for (expected, actual) in zipped {
self.probe(|_| {
match self
@@ -3617,7 +3660,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let Some(typeck_results) = self.typeck_results.as_ref() else { return };
// Make sure we're dealing with the `Option` type.
- let Some(option_ty_adt) = typeck_results.expr_ty_adjusted(expr).ty_adt_def() else { return };
+ let Some(option_ty_adt) = typeck_results.expr_ty_adjusted(expr).ty_adt_def() else {
+ return;
+ };
if !tcx.is_diagnostic_item(sym::Option, option_ty_adt.did()) {
return;
}
@@ -3627,7 +3672,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, .. }))
= failed_pred.kind().skip_binder()
&& tcx.is_fn_trait(trait_ref.def_id)
- && let [self_ty, found_ty] = trait_ref.substs.as_slice()
+ && let [self_ty, found_ty] = trait_ref.args.as_slice()
&& let Some(fn_ty) = self_ty.as_type().filter(|ty| ty.is_fn())
&& let fn_sig @ ty::FnSig {
abi: abi::Abi::Rust,
@@ -3647,7 +3692,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Extract `<U as Deref>::Target` assoc type and check that it is `T`
&& let Some(deref_target_did) = tcx.lang_items().deref_target()
- && let projection = Ty::new_projection(tcx,deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)]))
+ && let projection = Ty::new_projection(tcx,deref_target_did, tcx.mk_args(&[ty::GenericArg::from(found_ty)]))
&& let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection)
&& obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation))
&& infcx.can_eq(param_env, deref_target, target_ty)
@@ -3749,11 +3794,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
while let Some(assocs_in_method) = assocs.next() {
let Some(prev_assoc_in_method) = assocs.peek() else {
for entry in assocs_in_method {
- let Some((span, (assoc, ty))) = entry else { continue; };
- if primary_spans.is_empty() || type_diffs.iter().any(|diff| {
- let Sorts(expected_found) = diff else { return false; };
- self.can_eq(param_env, expected_found.found, ty)
- }) {
+ let Some((span, (assoc, ty))) = entry else {
+ continue;
+ };
+ if primary_spans.is_empty()
+ || type_diffs.iter().any(|diff| {
+ let Sorts(expected_found) = diff else {
+ return false;
+ };
+ self.can_eq(param_env, expected_found.found, ty)
+ })
+ {
// FIXME: this doesn't quite work for `Iterator::collect`
// because we have `Vec<i32>` and `()`, but we'd want `i32`
// to point at the `.into_iter()` call, but as long as we
@@ -3781,7 +3832,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let assoc = with_forced_trimmed_paths!(self.tcx.def_path_str(assoc));
if !self.can_eq(param_env, ty, *prev_ty) {
if type_diffs.iter().any(|diff| {
- let Sorts(expected_found) = diff else { return false; };
+ let Sorts(expected_found) = diff else {
+ return false;
+ };
self.can_eq(param_env, expected_found.found, ty)
}) {
primary_spans.push(span);
@@ -3829,15 +3882,19 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let ocx = ObligationCtxt::new(self.infcx);
let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
for diff in type_diffs {
- let Sorts(expected_found) = diff else { continue; };
- let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { continue; };
+ let Sorts(expected_found) = diff else {
+ continue;
+ };
+ let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else {
+ continue;
+ };
let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
let trait_def_id = proj.trait_def_id(self.tcx);
// Make `Self` be equivalent to the type of the call chain
// expression we're looking at now, so that we can tell what
// for example `Iterator::Item` is at this point in the chain.
- let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
+ let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| {
match param.kind {
ty::GenericParamDefKind::Type { .. } => {
if param.index == 0 {
@@ -3855,7 +3912,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// This corresponds to `<ExprTy as Iterator>::Item = _`.
let projection = ty::Binder::dummy(ty::PredicateKind::Clause(
ty::ClauseKind::Projection(ty::ProjectionPredicate {
- projection_ty: self.tcx.mk_alias_ty(proj.def_id, substs),
+ projection_ty: self.tcx.mk_alias_ty(proj.def_id, args),
term: ty_var.into(),
}),
));
@@ -3886,13 +3943,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
/// If the type that failed selection is an array or a reference to an array,
/// but the trait is implemented for slices, suggest that the user converts
/// the array into a slice.
- fn maybe_suggest_convert_to_slice(
+ fn suggest_convert_to_slice(
&self,
err: &mut Diagnostic,
+ obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
candidate_impls: &[ImplCandidate<'tcx>],
span: Span,
) {
+ // We can only suggest the slice coersion for function and binary operation arguments,
+ // since the suggestion would make no sense in turbofish or call
+ let (ObligationCauseCode::BinOp { .. }
+ | ObligationCauseCode::FunctionArgumentObligation { .. }) = obligation.cause.code()
+ else {
+ return;
+ };
+
// Three cases where we can make a suggestion:
// 1. `[T; _]` (array of T)
// 2. `&[T; _]` (reference to array of T)
@@ -3931,7 +3997,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_ty}` slice instead");
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let mut suggestions = vec![];
@@ -3960,6 +4026,10 @@ fn hint_missing_borrow<'tcx>(
found_node: Node<'_>,
err: &mut Diagnostic,
) {
+ if matches!(found_node, Node::TraitItem(..)) {
+ return;
+ }
+
let found_args = match found.kind() {
ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(),
kind => {
@@ -3974,7 +4044,9 @@ fn hint_missing_borrow<'tcx>(
};
// This could be a variant constructor, for example.
- let Some(fn_decl) = found_node.fn_decl() else { return; };
+ let Some(fn_decl) = found_node.fn_decl() else {
+ return;
+ };
let args = fn_decl.inputs.iter();
@@ -4029,19 +4101,11 @@ fn hint_missing_borrow<'tcx>(
}
if !to_borrow.is_empty() {
- err.multipart_suggestion_verbose(
- "consider borrowing the argument",
- to_borrow,
- Applicability::MaybeIncorrect,
- );
+ err.subdiagnostic(errors::AdjustSignatureBorrow::Borrow { to_borrow });
}
if !remove_borrow.is_empty() {
- err.multipart_suggestion_verbose(
- "do not borrow the argument",
- remove_borrow,
- Applicability::MaybeIncorrect,
- );
+ err.subdiagnostic(errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow });
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index cf9d9315f..3ebf1246a 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -8,7 +8,7 @@ use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt};
use std::marker::PhantomData;
@@ -410,8 +410,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
- ty::PredicateKind::ClosureKind(_, closure_substs, kind) => {
- match self.selcx.infcx.closure_kind(closure_substs) {
+ ty::PredicateKind::ClosureKind(_, closure_args, kind) => {
+ match self.selcx.infcx.closure_kind(closure_args) {
Some(closure_kind) => {
if closure_kind.extends(kind) {
ProcessResult::Changed(vec![])
@@ -536,7 +536,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
if let Ok(new_obligations) = infcx
.at(&obligation.cause, obligation.param_env)
.trace(c1, c2)
- .eq(DefineOpaqueTypes::No, a.substs, b.substs)
+ .eq(DefineOpaqueTypes::No, a.args, b.args)
{
return ProcessResult::Changed(mk_pending(
new_obligations.into_obligations(),
@@ -559,31 +559,30 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
let stalled_on = &mut pending_obligation.stalled_on;
- let mut evaluate = |c: Const<'tcx>| {
- if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() {
- match self.selcx.infcx.try_const_eval_resolve(
- obligation.param_env,
- unevaluated,
- c.ty(),
- Some(obligation.cause.span),
- ) {
- Ok(val) => Ok(val),
- Err(e) => match e {
- ErrorHandled::TooGeneric => {
- stalled_on.extend(
- unevaluated.substs.iter().filter_map(
+ let mut evaluate =
+ |c: Const<'tcx>| {
+ if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() {
+ match self.selcx.infcx.try_const_eval_resolve(
+ obligation.param_env,
+ unevaluated,
+ c.ty(),
+ Some(obligation.cause.span),
+ ) {
+ Ok(val) => Ok(val),
+ Err(e) => match e {
+ ErrorHandled::TooGeneric => {
+ stalled_on.extend(unevaluated.args.iter().filter_map(
TyOrConstInferVar::maybe_from_generic_arg,
- ),
- );
- Err(ErrorHandled::TooGeneric)
- }
- _ => Err(e),
- },
+ ));
+ Err(ErrorHandled::TooGeneric)
+ }
+ _ => Err(e),
+ },
+ }
+ } else {
+ Ok(c)
}
- } else {
- Ok(c)
- }
- };
+ };
match (evaluate(c1), evaluate(c2)) {
(Ok(c1), Ok(c2)) => {
@@ -671,7 +670,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>,
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
let infcx = self.selcx.infcx;
- if obligation.predicate.is_global() {
+ if obligation.predicate.is_global() && !self.selcx.is_intercrate() {
// no type variables present, can use evaluation for better caching.
// FIXME: consider caching errors too.
if infcx.predicate_must_hold_considering_regions(obligation) {
@@ -696,9 +695,9 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
// trait selection is because we don't have enough
// information about the types in the trait.
stalled_on.clear();
- stalled_on.extend(substs_infer_vars(
+ stalled_on.extend(args_infer_vars(
&self.selcx,
- trait_obligation.predicate.map_bound(|pred| pred.trait_ref.substs),
+ trait_obligation.predicate.map_bound(|pred| pred.trait_ref.args),
));
debug!(
@@ -725,7 +724,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
let tcx = self.selcx.tcx();
- if obligation.predicate.is_global() {
+ if obligation.predicate.is_global() && !self.selcx.is_intercrate() {
// no type variables present, can use evaluation for better caching.
// FIXME: consider caching errors too.
if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) {
@@ -753,9 +752,9 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)),
ProjectAndUnifyResult::FailedNormalization => {
stalled_on.clear();
- stalled_on.extend(substs_infer_vars(
+ stalled_on.extend(args_infer_vars(
&self.selcx,
- project_obligation.predicate.map_bound(|pred| pred.projection_ty.substs),
+ project_obligation.predicate.map_bound(|pred| pred.projection_ty.args),
));
ProcessResult::Unchanged
}
@@ -770,14 +769,14 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
}
}
-/// Returns the set of inference variables contained in `substs`.
-fn substs_infer_vars<'a, 'tcx>(
+/// Returns the set of inference variables contained in `args`.
+fn args_infer_vars<'a, 'tcx>(
selcx: &SelectionContext<'a, 'tcx>,
- substs: ty::Binder<'tcx, SubstsRef<'tcx>>,
+ args: ty::Binder<'tcx, GenericArgsRef<'tcx>>,
) -> impl Iterator<Item = TyOrConstInferVar<'tcx>> {
selcx
.infcx
- .resolve_vars_if_possible(substs)
+ .resolve_vars_if_possible(args)
.skip_binder() // ok because this check doesn't care about regions
.iter()
.filter(|arg| arg.has_non_region_infer())
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index e9cfd63e2..ab07b10c6 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -43,7 +43,7 @@ pub fn type_allowed_to_implement_copy<'tcx>(
self_type: Ty<'tcx>,
parent_cause: ObligationCause<'tcx>,
) -> Result<(), CopyImplementationError<'tcx>> {
- let (adt, substs) = match self_type.kind() {
+ let (adt, args) = match self_type.kind() {
// These types used to have a builtin impl.
// Now libcore provides that impl.
ty::Uint(_)
@@ -56,7 +56,7 @@ 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, args) => (adt, args),
_ => return Err(CopyImplementationError::NotAnAdt),
};
@@ -66,7 +66,7 @@ pub fn type_allowed_to_implement_copy<'tcx>(
param_env,
self_type,
adt,
- substs,
+ args,
parent_cause,
hir::LangItem::Copy,
)
@@ -91,7 +91,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
self_type: Ty<'tcx>,
parent_cause: ObligationCause<'tcx>,
) -> Result<(), ConstParamTyImplementationError<'tcx>> {
- let (adt, substs) = match self_type.kind() {
+ let (adt, args) = match self_type.kind() {
// `core` provides these impls.
ty::Uint(_)
| ty::Int(_)
@@ -103,7 +103,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
| ty::Ref(.., hir::Mutability::Not)
| ty::Tuple(_) => return Ok(()),
- &ty::Adt(adt, substs) => (adt, substs),
+ &ty::Adt(adt, args) => (adt, args),
_ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
};
@@ -113,7 +113,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
param_env,
self_type,
adt,
- substs,
+ args,
parent_cause,
hir::LangItem::ConstParamTy,
)
@@ -128,7 +128,7 @@ pub fn all_fields_implement_trait<'tcx>(
param_env: ty::ParamEnv<'tcx>,
self_type: Ty<'tcx>,
adt: AdtDef<'tcx>,
- substs: &'tcx List<GenericArg<'tcx>>,
+ args: &'tcx List<GenericArg<'tcx>>,
parent_cause: ObligationCause<'tcx>,
lang_item: LangItem,
) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>> {
@@ -141,7 +141,7 @@ pub fn all_fields_implement_trait<'tcx>(
let infcx = tcx.infer_ctxt().build();
let ocx = traits::ObligationCtxt::new(&infcx);
- let unnormalized_ty = field.ty(tcx, substs);
+ let unnormalized_ty = field.ty(tcx, args);
if unnormalized_ty.references_error() {
continue;
}
@@ -154,11 +154,11 @@ pub fn all_fields_implement_trait<'tcx>(
// FIXME(compiler-errors): This gives us better spans for bad
// projection types like in issue-50480.
- // If the ADT has substs, point to the cause we are given.
+ // If the ADT has args, point to the cause we are given.
// If it does not, then this field probably doesn't normalize
// to begin with, and point to the bad field's span instead.
let normalization_cause = if field
- .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did()))
+ .ty(tcx, traits::GenericArgs::identity_for_item(tcx, adt.did()))
.has_non_region_param()
{
parent_cause.clone()
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 1af8323b6..d2210c6d5 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -13,12 +13,12 @@ mod object_safety;
pub mod outlives_bounds;
pub mod project;
pub mod query;
-#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))]
+#[allow(hidden_glob_reexports)]
mod select;
mod specialize;
mod structural_match;
mod structural_normalize;
-#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))]
+#[allow(hidden_glob_reexports)]
mod util;
pub mod vtable;
pub mod wf;
@@ -32,7 +32,7 @@ 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, TypeFolder, TypeSuperVisitable};
-use rustc_middle::ty::{InternalSubsts, SubstsRef};
+use rustc_middle::ty::{GenericArgs, GenericArgsRef};
use rustc_span::def_id::DefId;
use rustc_span::Span;
@@ -61,13 +61,13 @@ 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, translate_substs_with_cause, OverlapError,
+ specialization_graph, translate_args, translate_args_with_cause, OverlapError,
};
pub use self::structural_match::search_for_structural_match_violation;
pub use self::structural_normalize::StructurallyNormalizeExt;
pub use self::util::elaborate;
pub use self::util::{
- check_substs_compatible, supertrait_def_ids, supertraits, transitive_bounds,
+ check_args_compatible, supertrait_def_ids, supertraits, transitive_bounds,
transitive_bounds_that_define_assoc_item, SupertraitDefIds,
};
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
@@ -133,7 +133,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
def_id: DefId,
) -> bool {
let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]);
- pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref.without_const())
+ pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref)
}
/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist?
@@ -328,11 +328,7 @@ pub fn normalize_param_env_or_error<'tcx>(
debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
- let elaborated_env = ty::ParamEnv::new(
- tcx.mk_clauses(&predicates),
- unnormalized_env.reveal(),
- unnormalized_env.constness(),
- );
+ let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal());
// HACK: we are trying to normalize the param-env inside *itself*. The problem is that
// normalization expects its param-env to be already normalized, which means we have
@@ -362,12 +358,9 @@ pub fn normalize_param_env_or_error<'tcx>(
"normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})",
predicates, outlives_predicates
);
- let Ok(non_outlives_predicates) = do_normalize_predicates(
- tcx,
- cause.clone(),
- elaborated_env,
- predicates,
- ) else {
+ let Ok(non_outlives_predicates) =
+ do_normalize_predicates(tcx, cause.clone(), elaborated_env, predicates)
+ else {
// An unnormalized env is better than nothing.
debug!("normalize_param_env_or_error: errored resolving non-outlives predicates");
return elaborated_env;
@@ -379,17 +372,11 @@ pub fn normalize_param_env_or_error<'tcx>(
// here. I believe they should not matter, because we are ignoring TypeOutlives param-env
// predicates here anyway. Keeping them here anyway because it seems safer.
let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned();
- let outlives_env = ty::ParamEnv::new(
- tcx.mk_clauses_from_iter(outlives_env),
- unnormalized_env.reveal(),
- unnormalized_env.constness(),
- );
- let Ok(outlives_predicates) = do_normalize_predicates(
- tcx,
- cause,
- outlives_env,
- outlives_predicates,
- ) else {
+ let outlives_env =
+ ty::ParamEnv::new(tcx.mk_clauses_from_iter(outlives_env), unnormalized_env.reveal());
+ let Ok(outlives_predicates) =
+ do_normalize_predicates(tcx, cause, outlives_env, outlives_predicates)
+ else {
// An unnormalized env is better than nothing.
debug!("normalize_param_env_or_error: errored resolving outlives predicates");
return elaborated_env;
@@ -399,11 +386,7 @@ pub fn normalize_param_env_or_error<'tcx>(
let mut predicates = non_outlives_predicates;
predicates.extend(outlives_predicates);
debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
- ty::ParamEnv::new(
- tcx.mk_clauses(&predicates),
- unnormalized_env.reveal(),
- unnormalized_env.constness(),
- )
+ ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal())
}
/// Normalize a type and process all resulting obligations, returning any errors.
@@ -460,7 +443,7 @@ pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec<ty::Clause
fn subst_and_check_impossible_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
- key: (DefId, SubstsRef<'tcx>),
+ key: (DefId, GenericArgsRef<'tcx>),
) -> bool {
debug!("subst_and_check_impossible_predicates(key={:?})", key);
@@ -480,11 +463,14 @@ fn subst_and_check_impossible_predicates<'tcx>(
result
}
-/// Checks whether a trait's method is impossible to call on a given impl.
+/// Checks whether a trait's associated item is impossible to reference on a given impl.
///
/// This only considers predicates that reference the impl's generics, and not
/// those that reference the method's generics.
-fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefId, DefId)) -> bool {
+fn is_impossible_associated_item(
+ tcx: TyCtxt<'_>,
+ (impl_def_id, trait_item_def_id): (DefId, DefId),
+) -> bool {
struct ReferencesOnlyParentGenerics<'tcx> {
tcx: TyCtxt<'tcx>,
generics: &'tcx ty::Generics,
@@ -527,7 +513,7 @@ fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefI
let impl_trait_ref = tcx
.impl_trait_ref(impl_def_id)
.expect("expected impl to correspond to trait")
- .subst_identity();
+ .instantiate_identity();
let param_env = tcx.param_env(impl_def_id);
let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id };
@@ -537,7 +523,7 @@ fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefI
tcx,
ObligationCause::dummy_with_span(*span),
param_env,
- ty::EarlyBinder::bind(*pred).subst(tcx, impl_trait_ref.substs),
+ ty::EarlyBinder::bind(*pred).instantiate(tcx, impl_trait_ref.args),
)
})
});
@@ -562,7 +548,7 @@ pub fn provide(providers: &mut Providers) {
specializes: specialize::specializes,
subst_and_check_impossible_predicates,
check_tys_might_be_eq: misc::check_tys_might_be_eq,
- is_impossible_method,
+ is_impossible_associated_item,
..*providers
};
}
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index c31944c16..5823b4508 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -17,10 +17,10 @@ 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,
};
+use rustc_middle::ty::{GenericArg, GenericArgs};
use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
use rustc_span::symbol::Symbol;
@@ -270,7 +270,7 @@ 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).subst_identity_iter_copied())
+ .flat_map(|item| tcx.explicit_item_bounds(item.def_id).instantiate_identity_iter_copied())
.filter_map(|c| predicate_references_self(tcx, c))
.collect()
}
@@ -284,7 +284,7 @@ fn predicate_references_self<'tcx>(
match predicate.kind().skip_binder() {
ty::ClauseKind::Trait(ref data) => {
// In the case of a trait predicate, we can skip the "self" type.
- data.trait_ref.substs[1..].iter().any(has_self_ty).then_some(sp)
+ data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp)
}
ty::ClauseKind::Projection(ref data) => {
// And similarly for projections. This should be redundant with
@@ -302,7 +302,7 @@ fn predicate_references_self<'tcx>(
//
// This is ALT2 in issue #56288, see that for discussion of the
// possible alternatives.
- data.projection_ty.substs[1..].iter().any(has_self_ty).then_some(sp)
+ data.projection_ty.args[1..].iter().any(has_self_ty).then_some(sp)
}
ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp),
@@ -393,7 +393,7 @@ fn object_safety_violation_for_assoc_item(
ty::AssocKind::Type => {
if !tcx.features().generic_associated_types_extended
&& !tcx.generics_of(item.def_id).params.is_empty()
- && item.opt_rpitit_info.is_none()
+ && !item.is_impl_trait_in_trait()
{
Some(ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span))
} else {
@@ -414,7 +414,7 @@ fn virtual_call_violation_for_method<'tcx>(
trait_def_id: DefId,
method: ty::AssocItem,
) -> Option<MethodViolationCode> {
- let sig = tcx.fn_sig(method.def_id).subst_identity();
+ let sig = tcx.fn_sig(method.def_id).instantiate_identity();
// The method's first parameter must be named `self`
if !method.fn_has_self_parameter {
@@ -517,8 +517,7 @@ fn virtual_call_violation_for_method<'tcx>(
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
format!(
- "receiver when `Self = ()` should have a Scalar ABI; found {:?}",
- abi
+ "receiver when `Self = ()` should have a Scalar ABI; found {abi:?}"
),
);
}
@@ -536,8 +535,7 @@ fn virtual_call_violation_for_method<'tcx>(
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
format!(
- "receiver when `Self = {}` should have a ScalarPair ABI; found {:?}",
- trait_object_ty, abi
+ "receiver when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}"
),
);
}
@@ -576,7 +574,6 @@ fn virtual_call_violation_for_method<'tcx>(
// implement auto traits if the underlying type does as well.
if let ty::ClauseKind::Trait(ty::TraitPredicate {
trait_ref: pred_trait_ref,
- constness: ty::BoundConstness::NotConst,
polarity: ty::ImplPolarity::Positive,
}) = pred.kind().skip_binder()
&& pred_trait_ref.self_ty() == tcx.types.self_param
@@ -586,7 +583,7 @@ fn virtual_call_violation_for_method<'tcx>(
// allowed to have generic parameters so `auto trait Bound<T> {}`
// would already have reported an error at the definition of the
// auto trait.
- if pred_trait_ref.substs.len() != 1 {
+ if pred_trait_ref.args.len() != 1 {
tcx.sess.diagnostic().delay_span_bug(
span,
"auto traits cannot have generic parameters",
@@ -612,11 +609,11 @@ fn receiver_for_self_ty<'tcx>(
method_def_id: DefId,
) -> Ty<'tcx> {
debug!("receiver_for_self_ty({:?}, {:?}, {:?})", receiver_ty, self_ty, method_def_id);
- let substs = InternalSubsts::for_item(tcx, method_def_id, |param, _| {
+ let args = GenericArgs::for_item(tcx, method_def_id, |param, _| {
if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) }
});
- let result = EarlyBinder::bind(receiver_ty).subst(tcx, substs);
+ let result = EarlyBinder::bind(receiver_ty).instantiate(tcx, args);
debug!(
"receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}",
receiver_ty, self_ty, method_def_id, result
@@ -751,21 +748,17 @@ fn receiver_is_dispatchable<'tcx>(
// U: Trait<Arg1, ..., ArgN>
let trait_predicate = {
let trait_def_id = method.trait_container(tcx).unwrap();
- let substs = InternalSubsts::for_item(tcx, trait_def_id, |param, _| {
+ let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
});
- ty::TraitRef::new(tcx, trait_def_id, substs).to_predicate(tcx)
+ ty::TraitRef::new(tcx, trait_def_id, args).to_predicate(tcx)
};
let caller_bounds =
param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]);
- ty::ParamEnv::new(
- tcx.mk_clauses_from_iter(caller_bounds),
- param_env.reveal(),
- param_env.constness(),
- )
+ ty::ParamEnv::new(tcx.mk_clauses_from_iter(caller_bounds), param_env.reveal())
};
// Receiver: DispatchFromDyn<Receiver[Self => U]>
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index ae6fc7cf8..32bbd626d 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -79,7 +79,8 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
&canonical_var_values,
canonical_result,
&mut constraints,
- ) else {
+ )
+ else {
return vec![];
};
assert_eq!(&obligations, &[]);
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index a10bca31f..06a1027e5 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1,10 +1,9 @@
//! Code for projecting associated types out of trait references.
-use super::check_substs_compatible;
+use super::check_args_compatible;
use super::specialization_graph;
-use super::translate_substs;
+use super::translate_args;
use super::util;
-use super::ImplSourceUserDefinedData;
use super::MismatchedProjectionTypes;
use super::Obligation;
use super::ObligationCause;
@@ -13,6 +12,9 @@ use super::Selection;
use super::SelectionContext;
use super::SelectionError;
use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
+use rustc_middle::traits::BuiltinImplSource;
+use rustc_middle::traits::ImplSource;
+use rustc_middle::traits::ImplSourceUserDefinedData;
use crate::errors::InherentProjectionNormalizationOverflow;
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -131,8 +133,6 @@ enum ProjectionCandidate<'tcx> {
/// From an "impl" (or a "pseudo-impl" returned by select)
Select(Selection<'tcx>),
-
- ImplTraitInTrait(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>),
}
enum ProjectionCandidateSet<'tcx> {
@@ -483,8 +483,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
assert!(
!value.has_escaping_bound_vars(),
- "Normalizing {:?} without wrapping in a `Binder`",
- value
+ "Normalizing {value:?} without wrapping in a `Binder`"
);
if !needs_normalization(&value, self.param_env.reveal()) {
@@ -526,7 +525,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
// ```
// for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>)
// ```
- // We normalize the substs on the projection before the projecting, but
+ // We normalize the args on the projection before the projecting, but
// if we're naive, we'll
// replace bound vars on inner, project inner, replace placeholders on inner,
// replace bound vars on outer, project outer, replace placeholders on outer
@@ -541,7 +540,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
//
// On the other hand, this does add a bit of complexity, since we only
// replace bound vars if the current type is a `Projection` and we need
- // to make sure we don't forget to fold the substs regardless.
+ // to make sure we don't forget to fold the args regardless.
match kind {
ty::Opaque => {
@@ -560,9 +559,9 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
);
}
- let substs = data.substs.fold_with(self);
+ let args = data.args.fold_with(self);
let generic_ty = self.interner().type_of(data.def_id);
- let concrete_ty = generic_ty.subst(self.interner(), substs);
+ let concrete_ty = generic_ty.instantiate(self.interner(), args);
self.depth += 1;
let folded_ty = self.fold_ty(concrete_ty);
self.depth -= 1;
@@ -662,11 +661,8 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
ty::Weak => {
let infcx = self.selcx.infcx;
self.obligations.extend(
- infcx
- .tcx
- .predicates_of(data.def_id)
- .instantiate_own(infcx.tcx, data.substs)
- .map(|(mut predicate, span)| {
+ infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map(
+ |(mut predicate, span)| {
if data.has_escaping_bound_vars() {
(predicate, ..) = BoundVarReplacer::replace_bound_vars(
infcx,
@@ -679,9 +675,10 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
ObligationCauseCode::TypeAlias(code, span, data.def_id)
});
Obligation::new(infcx.tcx, cause, self.param_env, predicate)
- }),
+ },
+ ),
);
- infcx.tcx.type_of(data.def_id).subst(infcx.tcx, data.substs).fold_with(self)
+ infcx.tcx.type_of(data.def_id).instantiate(infcx.tcx, data.args).fold_with(self)
}
ty::Inherent if !data.has_escaping_bound_vars() => {
@@ -1217,7 +1214,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term);
- let mut result = if projected_term.has_projections() {
+ let result = if projected_term.has_projections() {
let mut normalizer = AssocTypeNormalizer::new(
selcx,
param_env,
@@ -1227,19 +1224,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
);
let normalized_ty = normalizer.fold(projected_term);
+ let mut deduped = SsoHashSet::with_capacity(projected_obligations.len());
+ projected_obligations.retain(|obligation| deduped.insert(obligation.clone()));
+
Normalized { value: normalized_ty, obligations: projected_obligations }
} else {
Normalized { value: projected_term, obligations: projected_obligations }
};
- let mut deduped: SsoHashSet<_> = Default::default();
- result.obligations.retain(|projected_obligation| {
- if !deduped.insert(projected_obligation.clone()) {
- return false;
- }
- true
- });
-
if use_cache {
infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone());
}
@@ -1309,7 +1301,7 @@ fn normalize_to_error<'a, 'tcx>(
cause,
recursion_depth: depth,
param_env,
- predicate: trait_ref.without_const().to_predicate(selcx.tcx()),
+ predicate: trait_ref.to_predicate(selcx.tcx()),
};
let tcx = selcx.infcx.tcx;
let new_value = selcx.infcx.next_ty_var(TypeVariableOrigin {
@@ -1339,7 +1331,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
});
}
- let substs = compute_inherent_assoc_ty_substs(
+ let args = compute_inherent_assoc_ty_args(
selcx,
param_env,
alias_ty,
@@ -1349,7 +1341,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
);
// 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);
+ let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, args);
for (predicate, span) in predicates {
let predicate = normalize_with_depth_to(
selcx,
@@ -1383,7 +1375,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
));
}
- let ty = tcx.type_of(alias_ty.def_id).subst(tcx, substs);
+ let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args);
let mut ty = selcx.infcx.resolve_vars_if_possible(ty);
if ty.has_projections() {
@@ -1393,22 +1385,30 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
ty
}
-pub fn compute_inherent_assoc_ty_substs<'a, 'b, 'tcx>(
+pub fn compute_inherent_assoc_ty_args<'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> {
+) -> ty::GenericArgsRef<'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_args = selcx.infcx.fresh_args_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);
+ let mut impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args);
+ if !selcx.infcx.next_trait_solver() {
+ 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.
@@ -1425,7 +1425,7 @@ pub fn compute_inherent_assoc_ty_substs<'a, 'b, 'tcx>(
}
}
- alias_ty.rebase_substs_onto_impl(impl_substs, tcx)
+ alias_ty.rebase_inherent_args_onto_impl(impl_args, tcx)
}
enum Projected<'tcx> {
@@ -1472,8 +1472,6 @@ fn project<'cx, 'tcx>(
let mut candidates = ProjectionCandidateSet::None;
- assemble_candidate_for_impl_trait_in_trait(selcx, obligation, &mut candidates);
-
// Make sure that the following procedures are kept in order. ParamEnv
// needs to be first because it has highest priority, and Select checks
// the return value of push_candidate which assumes it's ran at last.
@@ -1499,20 +1497,18 @@ fn project<'cx, 'tcx>(
ProjectionCandidateSet::None => {
let tcx = selcx.tcx();
let term = match tcx.def_kind(obligation.predicate.def_id) {
- DefKind::AssocTy | DefKind::ImplTraitPlaceholder => Ty::new_projection(
- tcx,
- obligation.predicate.def_id,
- obligation.predicate.substs,
- )
- .into(),
+ DefKind::AssocTy => {
+ Ty::new_projection(tcx, obligation.predicate.def_id, obligation.predicate.args)
+ .into()
+ }
DefKind::AssocConst => ty::Const::new_unevaluated(
tcx,
ty::UnevaluatedConst::new(
obligation.predicate.def_id,
- obligation.predicate.substs,
+ obligation.predicate.args,
),
tcx.type_of(obligation.predicate.def_id)
- .subst(tcx, obligation.predicate.substs),
+ .instantiate(tcx, obligation.predicate.args),
)
.into(),
kind => {
@@ -1530,47 +1526,6 @@ fn project<'cx, 'tcx>(
}
}
-/// If the predicate's item is an `ImplTraitPlaceholder`, we do a select on the
-/// corresponding trait ref. If this yields an `impl`, then we're able to project
-/// to a concrete type, since we have an `impl`'s method to provide the RPITIT.
-fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
- selcx: &mut SelectionContext<'cx, 'tcx>,
- obligation: &ProjectionTyObligation<'tcx>,
- candidate_set: &mut ProjectionCandidateSet<'tcx>,
-) {
- let tcx = selcx.tcx();
- if tcx.def_kind(obligation.predicate.def_id) == DefKind::ImplTraitPlaceholder {
- let trait_fn_def_id = tcx.impl_trait_in_trait_parent_fn(obligation.predicate.def_id);
-
- let trait_def_id = tcx.parent(trait_fn_def_id);
- let trait_substs =
- obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id));
- 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)) {
- Ok(Some(super::ImplSource::UserDefined(data))) => {
- candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
- Ok(())
- }
- Ok(None) => {
- candidate_set.mark_ambiguous();
- Err(())
- }
- Ok(Some(_)) => {
- // Don't know enough about the impl to provide a useful signature
- Err(())
- }
- Err(e) => {
- debug!(error = ?e, "selection error");
- candidate_set.mark_error(e);
- Err(())
- }
- }
- });
- }
-}
-
/// The first thing we have to do is scan through the parameter
/// environment to see whether there are any projection predicates
/// there that can answer this question.
@@ -1612,7 +1567,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
let bounds = match *obligation.predicate.self_ty().kind() {
// Excluding IATs and type aliases here as they don't have meaningful item bounds.
ty::Alias(ty::Projection | ty::Opaque, ref data) => {
- tcx.item_bounds(data.def_id).subst(tcx, data.substs)
+ tcx.item_bounds(data.def_id).instantiate(tcx, data.args)
}
ty::Infer(ty::TyVar(_)) => {
// If the self-type is an inference variable, then it MAY wind up
@@ -1739,11 +1694,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionCandidateSet<'tcx>,
) {
- // Can't assemble candidate from impl for RPITIT
- if selcx.tcx().def_kind(obligation.predicate.def_id) == DefKind::ImplTraitPlaceholder {
- return;
- }
-
// If we are resolving `<T as TraitRef<...>>::Item == Type`,
// start out by selecting the predicate `T as TraitRef<...>`:
let trait_ref = obligation.predicate.trait_ref(selcx.tcx());
@@ -1763,7 +1713,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
};
let eligible = match &impl_source {
- super::ImplSource::UserDefined(impl_data) => {
+ ImplSource::UserDefined(impl_data) => {
// We have to be careful when projecting out of an
// impl because of specialization. If we are not in
// codegen (i.e., projection mode is not "any"), and the
@@ -1813,7 +1763,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
}
}
}
- super::ImplSource::Builtin(..) => {
+ ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
// While a builtin impl may be known to exist, the associated type may not yet
// be known. Any type with multiple potential associated types is therefore
// not eligible.
@@ -1912,8 +1862,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
if selcx.infcx.predicate_must_hold_modulo_regions(
&obligation.with(
selcx.tcx(),
- ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty])
- .without_const(),
+ ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]),
),
) =>
{
@@ -1937,7 +1886,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
bug!("unexpected builtin trait with associated type: {trait_ref:?}")
}
}
- super::ImplSource::Param(..) => {
+ ImplSource::Param(..) => {
// This case tell us nothing about the value of an
// associated type. Consider:
//
@@ -1965,17 +1914,18 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// in `assemble_candidates_from_param_env`.
false
}
- super::ImplSource::Object(_) => {
+ ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) => {
// Handled by the `Object` projection candidate. See
// `assemble_candidates_from_object_ty` for an explanation of
// why we special case object types.
false
}
- | super::ImplSource::TraitUpcasting(_) => {
+ ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
+ | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
// 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(());
}
@@ -2012,9 +1962,6 @@ fn confirm_candidate<'cx, 'tcx>(
ProjectionCandidate::Select(impl_source) => {
confirm_select_candidate(selcx, obligation, impl_source)
}
- ProjectionCandidate::ImplTraitInTrait(data) => {
- confirm_impl_trait_in_trait_candidate(selcx, obligation, data)
- }
};
// When checking for cycle during evaluation, we compare predicates with
@@ -2034,8 +1981,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
impl_source: Selection<'tcx>,
) -> Progress<'tcx> {
match impl_source {
- super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
- super::ImplSource::Builtin(data) => {
+ ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
+ ImplSource::Builtin(BuiltinImplSource::Misc, data) => {
let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx());
let lang_items = selcx.tcx().lang_items();
if lang_items.gen_trait() == Some(trait_def_id) {
@@ -2052,9 +1999,10 @@ fn confirm_select_candidate<'cx, 'tcx>(
confirm_builtin_candidate(selcx, obligation, data)
}
}
- super::ImplSource::Object(_)
- | super::ImplSource::Param(..)
- | super::ImplSource::TraitUpcasting(_) => {
+ ImplSource::Builtin(BuiltinImplSource::Object { .. }, _)
+ | ImplSource::Param(..)
+ | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
+ | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
// we don't create Select candidates with this kind of resolution
span_bug!(
obligation.cause.span,
@@ -2070,12 +2018,12 @@ fn confirm_generator_candidate<'cx, 'tcx>(
obligation: &ProjectionTyObligation<'tcx>,
nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let ty::Generator(_, substs, _) =
+ let ty::Generator(_, args, _) =
selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
else {
unreachable!()
};
- let gen_sig = substs.as_generator().poly_sig();
+ let gen_sig = args.as_generator().poly_sig();
let Normalized { value: gen_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@@ -2107,7 +2055,7 @@ fn confirm_generator_candidate<'cx, 'tcx>(
};
ty::ProjectionPredicate {
- projection_ty: tcx.mk_alias_ty(obligation.predicate.def_id, trait_ref.substs),
+ projection_ty: tcx.mk_alias_ty(obligation.predicate.def_id, trait_ref.args),
term: ty.into(),
}
});
@@ -2122,12 +2070,12 @@ fn confirm_future_candidate<'cx, 'tcx>(
obligation: &ProjectionTyObligation<'tcx>,
nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let ty::Generator(_, substs, _) =
+ let ty::Generator(_, args, _) =
selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
else {
unreachable!()
};
- let gen_sig = substs.as_generator().poly_sig();
+ let gen_sig = args.as_generator().poly_sig();
let Normalized { value: gen_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@@ -2151,7 +2099,7 @@ fn confirm_future_candidate<'cx, 'tcx>(
debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Output);
ty::ProjectionPredicate {
- projection_ty: tcx.mk_alias_ty(obligation.predicate.def_id, trait_ref.substs),
+ projection_ty: tcx.mk_alias_ty(obligation.predicate.def_id, trait_ref.args),
term: return_ty.into(),
}
});
@@ -2168,7 +2116,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
) -> Progress<'tcx> {
let tcx = selcx.tcx();
let self_ty = obligation.predicate.self_ty();
- let substs = tcx.mk_substs(&[self_ty.into()]);
+ let args = tcx.mk_args(&[self_ty.into()]);
let lang_items = tcx.lang_items();
let item_def_id = obligation.predicate.def_id;
let trait_def_id = tcx.trait_of_item(item_def_id).unwrap();
@@ -2198,8 +2146,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
LangItem::Sized,
obligation.cause.span(),
[self_ty],
- )
- .without_const();
+ );
obligations.push(obligation.with(tcx, sized_predicate));
}
(metadata_ty.into(), obligations)
@@ -2208,7 +2155,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
};
let predicate =
- ty::ProjectionPredicate { projection_ty: tcx.mk_alias_ty(item_def_id, substs), term };
+ ty::ProjectionPredicate { projection_ty: tcx.mk_alias_ty(item_def_id, args), term };
confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
.with_addl_obligations(obligations)
@@ -2240,12 +2187,11 @@ fn confirm_closure_candidate<'cx, 'tcx>(
obligation: &ProjectionTyObligation<'tcx>,
nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let ty::Closure(_, substs) =
- selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
+ let ty::Closure(_, args) = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
else {
unreachable!()
};
- let closure_sig = substs.as_closure().sig();
+ let closure_sig = args.as_closure().sig();
let Normalized { value: closure_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@@ -2282,7 +2228,7 @@ fn confirm_callable_candidate<'cx, 'tcx>(
flag,
)
.map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate {
- projection_ty: tcx.mk_alias_ty(fn_once_output_def_id, trait_ref.substs),
+ projection_ty: tcx.mk_alias_ty(fn_once_output_def_id, trait_ref.args),
term: ret_type.into(),
});
@@ -2349,8 +2295,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
}
Err(e) => {
let msg = format!(
- "Failed to unify obligation `{:?}` with poly_projection `{:?}`: {:?}",
- obligation, poly_cache_entry, e,
+ "Failed to unify obligation `{obligation:?}` with poly_projection `{poly_cache_entry:?}`: {e:?}",
);
debug!("confirm_param_env_candidate: {}", msg);
let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg);
@@ -2366,7 +2311,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
) -> Progress<'tcx> {
let tcx = selcx.tcx();
- let ImplSourceUserDefinedData { impl_def_id, substs, mut nested } = impl_impl_source;
+ let ImplSourceUserDefinedData { impl_def_id, args, mut nested } = impl_impl_source;
let assoc_item_id = obligation.predicate.def_id;
let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
@@ -2390,23 +2335,22 @@ fn confirm_impl_candidate<'cx, 'tcx>(
// If we're trying to normalize `<Vec<u32> as X>::A<S>` using
//`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
//
- // * `obligation.predicate.substs` is `[Vec<u32>, S]`
- // * `substs` is `[u32]`
- // * `substs` ends up as `[u32, S]`
- let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs);
- let substs =
- translate_substs(selcx.infcx, param_env, impl_def_id, substs, assoc_ty.defining_node);
+ // * `obligation.predicate.args` is `[Vec<u32>, S]`
+ // * `args` is `[u32]`
+ // * `args` ends up as `[u32, S]`
+ let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args);
+ let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node);
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 did = assoc_ty.item.def_id;
- let identity_substs = crate::traits::InternalSubsts::identity_for_item(tcx, did);
- let uv = ty::UnevaluatedConst::new(did, identity_substs);
+ let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did);
+ let uv = ty::UnevaluatedConst::new(did, identity_args);
ty.map_bound(|ty| ty::Const::new_unevaluated(tcx, uv, ty).into())
} else {
ty.map_bound(|ty| ty.into())
};
- if !check_substs_compatible(tcx, assoc_ty.item, substs) {
+ if !check_args_compatible(tcx, assoc_ty.item, args) {
let err = Ty::new_error_with_message(
tcx,
obligation.cause.span,
@@ -2415,107 +2359,10 @@ fn confirm_impl_candidate<'cx, 'tcx>(
Progress { term: err.into(), obligations: nested }
} else {
assoc_ty_own_obligations(selcx, obligation, &mut nested);
- Progress { term: term.subst(tcx, substs), obligations: nested }
+ Progress { term: term.instantiate(tcx, args), obligations: nested }
}
}
-fn confirm_impl_trait_in_trait_candidate<'tcx>(
- selcx: &mut SelectionContext<'_, 'tcx>,
- obligation: &ProjectionTyObligation<'tcx>,
- data: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>,
-) -> Progress<'tcx> {
- let tcx = selcx.tcx();
- let mut obligations = data.nested;
-
- let trait_fn_def_id = tcx.impl_trait_in_trait_parent_fn(obligation.predicate.def_id);
- let leaf_def = match specialization_graph::assoc_def(tcx, data.impl_def_id, trait_fn_def_id) {
- Ok(assoc_ty) => assoc_ty,
- Err(guar) => return Progress::error(tcx, guar),
- };
- // We don't support specialization for RPITITs anyways... yet.
- // Also don't try to project to an RPITIT that has no value
- if !leaf_def.is_final() || !leaf_def.item.defaultness(tcx).has_value() {
- return Progress { term: Ty::new_misc_error(tcx).into(), obligations };
- }
-
- // Use the default `impl Trait` for the trait, e.g., for a default trait body
- if leaf_def.item.container == ty::AssocItemContainer::TraitContainer {
- return Progress {
- term: Ty::new_opaque(tcx, obligation.predicate.def_id, obligation.predicate.substs)
- .into(),
- obligations,
- };
- }
-
- // Rebase from {trait}::{fn}::{opaque} to {impl}::{fn}::{opaque},
- // since `data.substs` are the impl substs.
- let impl_fn_substs =
- obligation.predicate.substs.rebase_onto(tcx, tcx.parent(trait_fn_def_id), data.substs);
- let impl_fn_substs = translate_substs(
- selcx.infcx,
- obligation.param_env,
- data.impl_def_id,
- impl_fn_substs,
- leaf_def.defining_node,
- );
-
- if !check_substs_compatible(tcx, leaf_def.item, impl_fn_substs) {
- let err = Ty::new_error_with_message(
- tcx,
- obligation.cause.span,
- "impl method and trait method have different parameters",
- );
- return Progress { term: err.into(), obligations };
- }
-
- let impl_fn_def_id = leaf_def.item.def_id;
-
- let cause = ObligationCause::new(
- obligation.cause.span,
- obligation.cause.body_id,
- super::ItemObligation(impl_fn_def_id),
- );
- let predicates = normalize_with_depth_to(
- selcx,
- obligation.param_env,
- cause.clone(),
- obligation.recursion_depth + 1,
- tcx.predicates_of(impl_fn_def_id).instantiate(tcx, impl_fn_substs),
- &mut obligations,
- );
- obligations.extend(predicates.into_iter().map(|(pred, span)| {
- Obligation::with_depth(
- tcx,
- ObligationCause::new(
- obligation.cause.span,
- obligation.cause.body_id,
- if span.is_dummy() {
- super::ItemObligation(impl_fn_def_id)
- } else {
- super::BindingObligation(impl_fn_def_id, span)
- },
- ),
- obligation.recursion_depth + 1,
- obligation.param_env,
- pred,
- )
- }));
-
- let ty = normalize_with_depth_to(
- selcx,
- obligation.param_env,
- cause.clone(),
- obligation.recursion_depth + 1,
- tcx.collect_return_position_impl_trait_in_trait_tys(impl_fn_def_id).map_or_else(
- |guar| Ty::new_error(tcx, guar),
- |tys| tys[&obligation.predicate.def_id].subst(tcx, impl_fn_substs),
- ),
- &mut obligations,
- );
-
- Progress { term: ty.into(), obligations }
-}
-
// Get obligations corresponding to the predicates from the where-clause of the
// associated type itself.
fn assoc_ty_own_obligations<'cx, 'tcx>(
@@ -2526,7 +2373,7 @@ fn assoc_ty_own_obligations<'cx, 'tcx>(
let tcx = selcx.tcx();
let predicates = tcx
.predicates_of(obligation.predicate.def_id)
- .instantiate_own(tcx, obligation.predicate.substs);
+ .instantiate_own(tcx, obligation.predicate.args);
for (predicate, span) in predicates {
let normalized = normalize_with_depth_to(
selcx,
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 709c3f432..9484a50e3 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -49,8 +49,8 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
// (T1..Tn) and closures have same properties as T1..Tn --
// check if *all* of them are trivial.
ty::Tuple(tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t)),
- ty::Closure(_, ref substs) => {
- trivial_dropck_outlives(tcx, substs.as_closure().tupled_upvars_ty())
+ ty::Closure(_, ref args) => {
+ trivial_dropck_outlives(tcx, args.as_closure().tupled_upvars_ty())
}
ty::Adt(def, _) => {
@@ -237,8 +237,8 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
Ok::<_, NoSolution>(())
})?,
- ty::Closure(_, substs) => {
- if !substs.as_closure().is_valid() {
+ ty::Closure(_, args) => {
+ if !args.as_closure().is_valid() {
// By the time this code runs, all type variables ought to
// be fully resolved.
@@ -250,14 +250,14 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
}
rustc_data_structures::stack::ensure_sufficient_stack(|| {
- for ty in substs.as_closure().upvar_tys() {
+ for ty in args.as_closure().upvar_tys() {
dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, ty, constraints)?;
}
Ok::<_, NoSolution>(())
})?
}
- ty::Generator(_, substs, _movability) => {
+ ty::Generator(_, args, _movability) => {
// rust-lang/rust#49918: types can be constructed, stored
// in the interior, and sit idle when generator yields
// (and is subsequently dropped).
@@ -281,7 +281,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
// derived from lifetimes attached to the upvars and resume
// argument, and we *do* incorporate those here.
- if !substs.as_generator().is_valid() {
+ if !args.as_generator().is_valid() {
// By the time this code runs, all type variables ought to
// be fully resolved.
tcx.sess.delay_span_bug(
@@ -291,29 +291,26 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
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());
+ constraints
+ .outlives
+ .extend(args.as_generator().upvar_tys().iter().map(ty::GenericArg::from));
+ constraints.outlives.push(args.as_generator().resume_ty().into());
}
- ty::Adt(def, substs) => {
+ ty::Adt(def, args) => {
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::bind(*t).subst(tcx, substs)));
+ .extend(dtorck_types.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args)));
constraints
.outlives
- .extend(outlives.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs)));
+ .extend(outlives.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args)));
constraints
.overflows
- .extend(overflows.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs)));
+ .extend(overflows.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args)));
}
// Objects must be alive in order for their destructor
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index a50644bb7..65f32b1c4 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -1,5 +1,4 @@
use rustc_infer::traits::{TraitEngine, TraitEngineExt};
-use rustc_middle::ty;
use crate::infer::canonical::OriginalQueryValues;
use crate::infer::InferCtxt;
@@ -66,17 +65,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
) -> Result<EvaluationResult, OverflowError> {
let mut _orig_values = OriginalQueryValues::default();
- let param_env = match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
- // we ignore the value set to it.
- let mut _constness = pred.constness;
- obligation
- .param_env
- .with_constness(_constness.and(obligation.param_env.constness()))
- }
- // constness has no effect on the given predicate.
- _ => obligation.param_env.without_const(),
- };
+ let param_env = obligation.param_env;
if self.next_trait_solver() {
self.probe(|snapshot| {
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 7fe79fd86..87beaddc6 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -61,8 +61,27 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
self.cause,
);
+ // This is actually a consequence by the way `normalize_erasing_regions` works currently.
+ // Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
+ // through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
+ // with trying to normalize with escaping bound vars.
+ //
+ // Here, we just add the universes that we *would* have created had we passed through the binders.
+ //
+ // We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
+ // The rest of the code is already set up to be lazy about replacing bound vars,
+ // and only when we actually have to normalize.
+ let universes = if value.has_escaping_bound_vars() {
+ let mut max_visitor =
+ MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
+ value.visit_with(&mut max_visitor);
+ vec![None; max_visitor.escaping]
+ } else {
+ vec![]
+ };
+
if self.infcx.next_trait_solver() {
- match crate::solve::deeply_normalize(self, value) {
+ match crate::solve::deeply_normalize_with_skipped_universes(self, value, universes) {
Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
Err(_errors) => {
return Err(NoSolution);
@@ -81,27 +100,9 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
obligations: vec![],
cache: SsoHashMap::new(),
anon_depth: 0,
- universes: vec![],
+ universes,
};
- // This is actually a consequence by the way `normalize_erasing_regions` works currently.
- // Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
- // through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
- // with trying to normalize with escaping bound vars.
- //
- // Here, we just add the universes that we *would* have created had we passed through the binders.
- //
- // We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
- // The rest of the code is already set up to be lazy about replacing bound vars,
- // and only when we actually have to normalize.
- if value.has_escaping_bound_vars() {
- let mut max_visitor =
- MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
- value.visit_with(&mut max_visitor);
- if max_visitor.escaping > 0 {
- normalizer.universes.extend((0..max_visitor.escaping).map(|_| None));
- }
- }
let result = value.try_fold_with(&mut normalizer);
info!(
"normalize::<{}>: result={:?} with {} obligations",
@@ -217,7 +218,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
};
// See note in `rustc_trait_selection::traits::project` about why we
- // wait to fold the substs.
+ // wait to fold the args.
// Wrap this in a closure so we don't accidentally return from the outer function
let res = match kind {
@@ -227,7 +228,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
Reveal::UserFacing => ty.try_super_fold_with(self)?,
Reveal::All => {
- let substs = data.substs.try_fold_with(self)?;
+ let args = data.args.try_fold_with(self)?;
let recursion_limit = self.interner().recursion_limit();
if !recursion_limit.value_within_limit(self.anon_depth) {
// A closure or generator may have itself as in its upvars.
@@ -243,14 +244,14 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
}
let generic_ty = self.interner().type_of(data.def_id);
- let concrete_ty = generic_ty.subst(self.interner(), substs);
+ let concrete_ty = generic_ty.instantiate(self.interner(), args);
self.anon_depth += 1;
if concrete_ty == ty {
bug!(
- "infinite recursion generic_ty: {:#?}, substs: {:#?}, \
+ "infinite recursion generic_ty: {:#?}, args: {:#?}, \
concrete_ty: {:#?}, ty: {:#?}",
generic_ty,
- substs,
+ args,
concrete_ty,
ty
);
@@ -298,7 +299,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
if !tcx.sess.opts.actually_rustdoc {
tcx.sess.delay_span_bug(
DUMMY_SP,
- format!("unexpected ambiguity: {:?} {:?}", c_data, result),
+ format!("unexpected ambiguity: {c_data:?} {result:?}"),
);
}
return Err(NoSolution);
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 44671a076..302b6016e 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
@@ -4,7 +4,7 @@ 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};
+use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserArgs, UserSelfTy, UserType};
pub use rustc_middle::traits::query::type_op::AscribeUserType;
use rustc_span::{Span, DUMMY_SP};
@@ -47,8 +47,8 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>(
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)?
+ UserType::TypeOf(def_id, user_args) => {
+ relate_mir_and_user_args(ocx, param_env, span, mir_ty, def_id, user_args)?
}
};
Ok(())
@@ -74,20 +74,19 @@ fn relate_mir_and_user_ty<'tcx>(
}
#[instrument(level = "debug", skip(ocx, param_env, span))]
-fn relate_mir_and_user_substs<'tcx>(
+fn relate_mir_and_user_args<'tcx>(
ocx: &ObligationCtxt<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
span: Span,
mir_ty: Ty<'tcx>,
def_id: DefId,
- user_substs: UserSubsts<'tcx>,
+ user_args: UserArgs<'tcx>,
) -> Result<(), NoSolution> {
- let param_env = param_env.without_const();
- let UserSubsts { user_self_ty, substs } = user_substs;
+ let UserArgs { user_self_ty, args } = user_args;
let tcx = ocx.infcx.tcx;
let cause = ObligationCause::dummy_with_span(span);
- let ty = tcx.type_of(def_id).subst(tcx, substs);
+ let ty = tcx.type_of(def_id).instantiate(tcx, args);
let ty = ocx.normalize(&cause, param_env, ty);
debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
@@ -98,7 +97,7 @@ fn relate_mir_and_user_substs<'tcx>(
// 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);
+ let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args);
debug!(?instantiated_predicates);
for (instantiated_predicate, predicate_span) in instantiated_predicates {
@@ -116,7 +115,7 @@ fn relate_mir_and_user_substs<'tcx>(
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 = tcx.type_of(impl_def_id).instantiate(tcx, args);
let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
@@ -128,9 +127,9 @@ fn relate_mir_and_user_substs<'tcx>(
// 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
+ // the WF of `ty` is predicated on the args being
// well-formed, and we haven't proven *that*. We don't
- // want to prove the WF of types from `substs` directly because they
+ // want to prove the WF of types from `args` directly because they
// haven't been normalized.
//
// FIXME(nmatsakis): Well, perhaps we should normalize
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 5420caee3..c99e018e9 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
@@ -77,8 +77,7 @@ where
let pre_obligations = infcx.take_registered_region_obligations();
assert!(
pre_obligations.is_empty(),
- "scrape_region_constraints: incoming region obligations = {:#?}",
- pre_obligations,
+ "scrape_region_constraints: incoming region obligations = {pre_obligations:#?}",
);
let value = infcx.commit_if_ok(|_| {
@@ -92,7 +91,7 @@ where
} else {
Err(infcx.tcx.sess.delay_span_bug(
DUMMY_SP,
- format!("errors selecting obligation during MIR typeck: {:?}", errors),
+ format!("errors selecting obligation during MIR typeck: {errors:?}"),
))
}
})?;
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 988942633..59f4a22ac 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
@@ -31,16 +31,6 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> {
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> 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
- // query is also used by the
- // `infcx.at(...).dropck_outlives(...)` fn. Avoiding the
- // wrapper means we don't need an infcx in this code, which is
- // good because the interface doesn't give us one (so that we
- // know we are not registering any subregion relations or
- // other things).
-
// FIXME convert to the type expected by the `dropck_outlives`
// query. This should eventually be fixed by changing the
// *underlying query*.
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 d5f6aaa7f..e3da87a22 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -124,11 +124,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_candidates_from_projected_tys(obligation, &mut candidates);
self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?;
- // Auto implementations have lower priority, so we only
- // consider triggering a default if there is no other impl that can apply.
- if candidates.vec.is_empty() {
- self.assemble_candidates_from_auto_impls(obligation, &mut candidates);
- }
+ self.assemble_candidates_from_auto_impls(obligation, &mut candidates);
}
debug!("candidate list size: {}", candidates.vec.len());
Ok(candidates)
@@ -158,9 +154,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.infcx
.probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
- candidates
- .vec
- .extend(result.into_iter().map(|(idx, constness)| ProjectionCandidate(idx, constness)));
+ // FIXME(effects) proper constness needed?
+ candidates.vec.extend(
+ result.into_iter().map(|idx| ProjectionCandidate(idx, ty::BoundConstness::NotConst)),
+ );
}
/// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller
@@ -209,7 +206,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
- // Okay to skip binder because the substs on generator types never
+ // Okay to skip binder because the args on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = obligation.self_ty().skip_binder();
@@ -261,14 +258,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return;
};
- // Okay to skip binder because the substs on closure types never
+ // Okay to skip binder because the args on closure types never
// touch bound regions, they just capture the in-scope
// type/region parameters
match *obligation.self_ty().skip_binder().kind() {
- ty::Closure(def_id, closure_substs) => {
+ ty::Closure(def_id, closure_args) => {
let is_const = self.tcx().is_const_fn_raw(def_id);
debug!(?kind, ?obligation, "assemble_unboxed_candidates");
- match self.infcx.closure_kind(closure_substs) {
+ match self.infcx.closure_kind(closure_args) {
Some(closure_kind) => {
debug!(?closure_kind, "assemble_unboxed_candidates");
if closure_kind.extends(kind) {
@@ -351,7 +348,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
- let obligation_substs = obligation.predicate.skip_binder().trait_ref.substs;
+ let obligation_args = obligation.predicate.skip_binder().trait_ref.args;
self.tcx().for_each_relevant_impl(
obligation.predicate.def_id(),
obligation.predicate.skip_binder().trait_ref.self_ty(),
@@ -360,9 +357,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// consider a "quick reject". This avoids creating more types
// and so forth that we need to.
let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
- if !drcx
- .substs_refs_may_unify(obligation_substs, impl_trait_ref.skip_binder().substs)
- {
+ if !drcx.args_refs_may_unify(obligation_args, impl_trait_ref.skip_binder().args) {
return;
}
if self.reject_fn_ptr_impls(
@@ -374,7 +369,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
self.infcx.probe(|_| {
- if let Ok(_substs) = self.match_impl(impl_def_id, impl_trait_ref, obligation) {
+ if let Ok(_args) = self.match_impl(impl_def_id, impl_trait_ref, obligation) {
candidates.vec.push(ImplCandidate(impl_def_id));
}
});
@@ -402,8 +397,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
};
for &(predicate, _) in self.tcx().predicates_of(impl_def_id).predicates {
- let ty::ClauseKind::Trait(pred)
- = predicate.kind().skip_binder() else { continue };
+ let ty::ClauseKind::Trait(pred) = predicate.kind().skip_binder() else { continue };
if fn_ptr_trait != pred.trait_ref.def_id {
continue;
}
@@ -516,7 +510,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// for an example of a test case that exercises
// this path.
}
- ty::Infer(ty::TyVar(_)) => {
+ ty::Infer(ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_)) => {
// The auto impl might apply; we don't know.
candidates.ambiguous = true;
}
@@ -536,7 +530,63 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- _ => candidates.vec.push(AutoImplCandidate),
+ ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+ bug!(
+ "asked to assemble auto trait candidates of unexpected type: {:?}",
+ self_ty
+ );
+ }
+
+ ty::Alias(_, _)
+ if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(..))) =>
+ {
+ // We do not generate an auto impl candidate for `impl Trait`s which already
+ // reference our auto trait.
+ //
+ // For example during candidate assembly for `impl Send: Send`, we don't have
+ // to look at the constituent types for this opaque types to figure out that this
+ // trivially holds.
+ //
+ // Note that this is only sound as projection candidates of opaque types
+ // are always applicable for auto traits.
+ }
+ ty::Alias(_, _) => candidates.vec.push(AutoImplCandidate),
+
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Str
+ | ty::Array(_, _)
+ | ty::Slice(_)
+ | ty::Adt(..)
+ | ty::RawPtr(_)
+ | ty::Ref(..)
+ | ty::FnDef(..)
+ | ty::FnPtr(_)
+ | ty::Closure(_, _)
+ | ty::Generator(..)
+ | ty::Never
+ | ty::Tuple(_)
+ | ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..) => {
+ // Only consider auto impls if there are no manual impls for the root of `self_ty`.
+ //
+ // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
+ // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls
+ // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined.
+ //
+ // Generally, we have to guarantee that for all `SimplifiedType`s the only crate
+ // which may define impls for that type is either the crate defining the type
+ // or the trait. This should be guaranteed by the orphan check.
+ let mut has_impl = false;
+ self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true);
+ if !has_impl {
+ candidates.vec.push(AutoImplCandidate)
+ }
+ }
+ ty::Error(_) => {} // do not add an auto trait impl for `ty::Error` for now.
}
}
}
@@ -651,7 +701,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let ty = traits::normalize_projection_type(
self,
param_env,
- tcx.mk_alias_ty(tcx.lang_items().deref_target()?, trait_ref.substs),
+ tcx.mk_alias_ty(tcx.lang_items().deref_target()?, trait_ref.args),
cause.clone(),
0,
// We're *intentionally* throwing these away,
@@ -689,13 +739,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Don't add any candidates if there are bound regions.
return;
};
- let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
+ let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
debug!(?source, ?target, "assemble_candidates_for_unsizing");
match (source.kind(), target.kind()) {
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
- (&ty::Dynamic(ref data_a, _, ty::Dyn), &ty::Dynamic(ref data_b, _, ty::Dyn)) => {
+ (
+ &ty::Dynamic(ref a_data, a_region, ty::Dyn),
+ &ty::Dynamic(ref b_data, b_region, ty::Dyn),
+ ) => {
// Upcast coercions permit several things:
//
// 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
@@ -707,19 +760,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
//
// We always perform upcasting coercions when we can because of reason
// #2 (region bounds).
- let auto_traits_compatible = data_b
+ let auto_traits_compatible = b_data
.auto_traits()
// All of a's auto traits need to be in b's auto traits.
- .all(|b| data_a.auto_traits().any(|a| a == b));
+ .all(|b| a_data.auto_traits().any(|a| a == b));
if auto_traits_compatible {
- let principal_def_id_a = data_a.principal_def_id();
- let principal_def_id_b = data_b.principal_def_id();
+ let principal_def_id_a = a_data.principal_def_id();
+ let principal_def_id_b = b_data.principal_def_id();
if principal_def_id_a == principal_def_id_b {
// no cyclic
candidates.vec.push(BuiltinUnsizeCandidate);
} else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
// not casual unsizing, now check whether this is trait upcasting coercion.
- let principal_a = data_a.principal().unwrap();
+ let principal_a = a_data.principal().unwrap();
let target_trait_did = principal_def_id_b.unwrap();
let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object(
@@ -735,9 +788,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
for (idx, upcast_trait_ref) in
util::supertraits(self.tcx(), source_trait_ref).enumerate()
{
- if upcast_trait_ref.def_id() == target_trait_did {
- candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
- }
+ self.infcx.probe(|_| {
+ if upcast_trait_ref.def_id() == target_trait_did
+ && let Ok(nested) = self.match_upcast_principal(
+ obligation,
+ upcast_trait_ref,
+ a_data,
+ b_data,
+ a_region,
+ b_region,
+ )
+ {
+ if nested.is_none() {
+ candidates.ambiguous = true;
+ }
+ candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
+ }
+ })
}
}
}
@@ -842,7 +909,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) {
// If the predicate is `~const Destruct` in a non-const environment, we don't actually need
// to check anything. We'll short-circuit checking any obligations in confirmation, too.
- if !obligation.is_const() {
+ // FIXME(effects)
+ if true {
candidates.vec.push(ConstDestructCandidate(None));
return;
}
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 7adc29bbb..88d030033 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -11,10 +11,10 @@ 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::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch};
use rustc_middle::ty::{
- self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate,
- TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt,
+ self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
+ TraitPredicate, Ty, TyCtxt, TypeVisitableExt,
};
use rustc_span::def_id::DefId;
@@ -26,9 +26,9 @@ use crate::traits::vtable::{
};
use crate::traits::{
BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
- ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized,
- Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation,
- PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented,
+ ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause,
+ OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection,
+ SelectionError, TraitNotObjectSafe, Unimplemented,
};
use super::BuiltinImplConditions;
@@ -48,18 +48,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let mut impl_src = match candidate {
BuiltinCandidate { has_nested } => {
let data = self.confirm_builtin_candidate(obligation, has_nested);
- ImplSource::Builtin(data)
+ ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
TransmutabilityCandidate => {
let data = self.confirm_transmutability_candidate(obligation)?;
- ImplSource::Builtin(data)
+ ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
ParamCandidate(param) => {
let obligations =
self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
- ImplSource::Param(obligations, param.skip_binder().constness)
+ ImplSource::Param(obligations)
}
ImplCandidate(impl_def_id) => {
@@ -68,64 +68,57 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
AutoImplCandidate => {
let data = self.confirm_auto_impl_candidate(obligation)?;
- ImplSource::Builtin(data)
+ ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
- ProjectionCandidate(idx, constness) => {
+ ProjectionCandidate(idx, _) => {
let obligations = self.confirm_projection_candidate(obligation, idx)?;
- ImplSource::Param(obligations, constness)
+ ImplSource::Param(obligations)
}
- ObjectCandidate(idx) => {
- let data = self.confirm_object_candidate(obligation, idx)?;
- ImplSource::Object(data)
- }
+ ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?,
ClosureCandidate { .. } => {
let vtable_closure = self.confirm_closure_candidate(obligation)?;
- ImplSource::Builtin(vtable_closure)
+ ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure)
}
GeneratorCandidate => {
let vtable_generator = self.confirm_generator_candidate(obligation)?;
- ImplSource::Builtin(vtable_generator)
+ ImplSource::Builtin(BuiltinImplSource::Misc, vtable_generator)
}
FutureCandidate => {
let vtable_future = self.confirm_future_candidate(obligation)?;
- ImplSource::Builtin(vtable_future)
+ ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future)
}
FnPointerCandidate { is_const } => {
let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
- ImplSource::Builtin(data)
+ ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
TraitAliasCandidate => {
let data = self.confirm_trait_alias_candidate(obligation);
- ImplSource::Builtin(data)
+ ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
BuiltinObjectCandidate => {
// This indicates something like `Trait + Send: Send`. In this case, we know that
// this holds because that's what the object type is telling us, and there's really
// no additional obligations to prove and no types in particular to unify, etc.
- ImplSource::Builtin(Vec::new())
+ ImplSource::Builtin(BuiltinImplSource::Misc, Vec::new())
}
- BuiltinUnsizeCandidate => {
- let data = self.confirm_builtin_unsize_candidate(obligation)?;
- ImplSource::Builtin(data)
- }
+ BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?,
TraitUpcastingUnsizeCandidate(idx) => {
- let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?;
- ImplSource::TraitUpcasting(data)
+ self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?
}
ConstDestructCandidate(def_id) => {
let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
- ImplSource::Builtin(data)
+ ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
};
@@ -135,14 +128,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
subobligation.set_depth_from_parent(obligation.recursion_depth);
}
- if !obligation.predicate.is_const_if_const() {
- // normalize nested predicates according to parent predicate's constness.
- impl_src = impl_src.map(|mut o| {
- o.predicate = o.predicate.without_const(self.tcx());
- o
- });
- }
-
Ok(impl_src)
}
@@ -158,15 +143,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.infcx.instantiate_binder_with_placeholders(trait_predicate).trait_ref;
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() {
+ let (def_id, args) = match *placeholder_self_ty.kind() {
// Excluding IATs and type aliases here as they don't have meaningful item bounds.
- ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- (def_id, substs)
+ ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
+ (def_id, args)
}
_ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty),
};
- let candidate_predicate = tcx.item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs);
+ let candidate_predicate =
+ tcx.item_bounds(def_id).map_bound(|i| i[idx]).instantiate(tcx, args);
let candidate = candidate_predicate
.as_trait_clause()
.expect("projection candidate is not a trait predicate")
@@ -190,7 +176,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
})?);
if let ty::Alias(ty::Projection, ..) = placeholder_self_ty.kind() {
- let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
+ let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
for (predicate, _) in predicates {
let normalized = normalize_with_depth_to(
self,
@@ -298,8 +284,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.collect(),
Condition::IfTransmutable { src, dst } => {
let trait_def_id = obligation.predicate.def_id();
- let scope = predicate.trait_ref.substs.type_at(2);
- let assume_const = predicate.trait_ref.substs.const_at(3);
+ let scope = predicate.trait_ref.args.type_at(2);
+ let assume_const = predicate.trait_ref.args.const_at(3);
let make_obl = |from_ty, to_ty| {
let trait_ref1 = ty::TraitRef::new(
tcx,
@@ -342,19 +328,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let Some(assume) = rustc_transmute::Assume::from_const(
self.infcx.tcx,
obligation.param_env,
- predicate.trait_ref.substs.const_at(3)
+ predicate.trait_ref.args.const_at(3),
) else {
return Err(Unimplemented);
};
- let dst = predicate.trait_ref.substs.type_at(0);
- let src = predicate.trait_ref.substs.type_at(1);
+ let dst = predicate.trait_ref.args.type_at(0);
+ let src = predicate.trait_ref.args.type_at(1);
debug!(?src, ?dst);
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
let maybe_transmutable = transmute_env.is_transmutable(
obligation.cause.clone(),
rustc_transmute::Types { dst, src },
- predicate.trait_ref.substs.type_at(2),
+ predicate.trait_ref.args.type_at(2),
assume,
);
@@ -402,7 +388,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.recursion_depth + 1,
obligation.param_env,
trait_def_id,
- &trait_ref.substs,
+ &trait_ref.args,
obligation.predicate,
);
@@ -433,12 +419,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// First, create the substitutions by matching the impl again,
// this time not in a probe.
- let substs = self.rematch_impl(impl_def_id, obligation);
- debug!(?substs, "impl substs");
+ let args = self.rematch_impl(impl_def_id, obligation);
+ debug!(?args, "impl args");
ensure_sufficient_stack(|| {
self.vtable_impl(
impl_def_id,
- substs,
+ args,
&obligation.cause,
obligation.recursion_depth + 1,
obligation.param_env,
@@ -450,40 +436,40 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn vtable_impl(
&mut self,
impl_def_id: DefId,
- substs: Normalized<'tcx, SubstsRef<'tcx>>,
+ args: Normalized<'tcx, GenericArgsRef<'tcx>>,
cause: &ObligationCause<'tcx>,
recursion_depth: usize,
param_env: ty::ParamEnv<'tcx>,
parent_trait_pred: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
- debug!(?impl_def_id, ?substs, ?recursion_depth, "vtable_impl");
+ debug!(?impl_def_id, ?args, ?recursion_depth, "vtable_impl");
let mut impl_obligations = self.impl_or_trait_obligations(
cause,
recursion_depth,
param_env,
impl_def_id,
- &substs.value,
+ &args.value,
parent_trait_pred,
);
debug!(?impl_obligations, "vtable_impl");
// Because of RFC447, the impl-trait-ref and obligations
- // are sufficient to determine the impl substs, without
+ // are sufficient to determine the impl args, without
// relying on projections in the impl-trait-ref.
//
// e.g., `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V`
- impl_obligations.extend(substs.obligations);
+ impl_obligations.extend(args.obligations);
- ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: impl_obligations }
+ ImplSourceUserDefinedData { impl_def_id, args: args.value, nested: impl_obligations }
}
fn confirm_object_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
index: usize,
- ) -> Result<ImplSourceObjectData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
debug!(?obligation, ?index, "confirm_object_candidate");
@@ -531,7 +517,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// will be checked in the code below.
for super_trait in tcx
.super_predicates_of(trait_predicate.def_id())
- .instantiate(tcx, trait_predicate.trait_ref.substs)
+ .instantiate(tcx, trait_predicate.trait_ref.args)
.predicates
.into_iter()
{
@@ -569,68 +555,65 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// higher-ranked things.
// Prevent, e.g., `dyn Iterator<Item = str>`.
for bound in self.tcx().item_bounds(assoc_type).transpose_iter() {
- let subst_bound =
- if defs.count() == 0 {
- bound.subst(tcx, trait_predicate.trait_ref.substs)
- } else {
- let mut substs = smallvec::SmallVec::with_capacity(defs.count());
- substs.extend(trait_predicate.trait_ref.substs.iter());
- let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
- smallvec::SmallVec::with_capacity(
- bound.skip_binder().kind().bound_vars().len() + defs.count(),
- );
- bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter());
- InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param
- .kind
- {
- GenericParamDefKind::Type { .. } => {
- let kind = ty::BoundTyKind::Param(param.def_id, param.name);
- let bound_var = ty::BoundVariableKind::Ty(kind);
- bound_vars.push(bound_var);
- Ty::new_bound(
- tcx,
- ty::INNERMOST,
- ty::BoundTy {
- var: ty::BoundVar::from_usize(bound_vars.len() - 1),
- kind,
- },
- )
- .into()
- }
- GenericParamDefKind::Lifetime => {
- let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
- let bound_var = ty::BoundVariableKind::Region(kind);
- bound_vars.push(bound_var);
- ty::Region::new_late_bound(
- tcx,
- ty::INNERMOST,
- ty::BoundRegion {
- var: ty::BoundVar::from_usize(bound_vars.len() - 1),
- kind,
- },
- )
- .into()
- }
- GenericParamDefKind::Const { .. } => {
- let bound_var = ty::BoundVariableKind::Const;
- bound_vars.push(bound_var);
- ty::Const::new_bound(
- tcx,
- ty::INNERMOST,
- ty::BoundVar::from_usize(bound_vars.len() - 1),
- tcx.type_of(param.def_id)
- .no_bound_vars()
- .expect("const parameter types cannot be generic"),
- )
- .into()
- }
- });
- let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
- let assoc_ty_substs = tcx.mk_substs(&substs);
- let bound =
- bound.map_bound(|b| b.kind().skip_binder()).subst(tcx, assoc_ty_substs);
- ty::Binder::bind_with_vars(bound, bound_vars).to_predicate(tcx)
- };
+ let subst_bound = if defs.count() == 0 {
+ bound.instantiate(tcx, trait_predicate.trait_ref.args)
+ } else {
+ let mut args = smallvec::SmallVec::with_capacity(defs.count());
+ args.extend(trait_predicate.trait_ref.args.iter());
+ let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
+ smallvec::SmallVec::with_capacity(
+ bound.skip_binder().kind().bound_vars().len() + defs.count(),
+ );
+ bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter());
+ GenericArgs::fill_single(&mut args, defs, &mut |param, _| match param.kind {
+ GenericParamDefKind::Type { .. } => {
+ let kind = ty::BoundTyKind::Param(param.def_id, param.name);
+ let bound_var = ty::BoundVariableKind::Ty(kind);
+ bound_vars.push(bound_var);
+ Ty::new_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundTy {
+ var: ty::BoundVar::from_usize(bound_vars.len() - 1),
+ kind,
+ },
+ )
+ .into()
+ }
+ GenericParamDefKind::Lifetime => {
+ let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
+ let bound_var = ty::BoundVariableKind::Region(kind);
+ bound_vars.push(bound_var);
+ ty::Region::new_late_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundRegion {
+ var: ty::BoundVar::from_usize(bound_vars.len() - 1),
+ kind,
+ },
+ )
+ .into()
+ }
+ GenericParamDefKind::Const { .. } => {
+ let bound_var = ty::BoundVariableKind::Const;
+ bound_vars.push(bound_var);
+ ty::Const::new_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundVar::from_usize(bound_vars.len() - 1),
+ tcx.type_of(param.def_id)
+ .no_bound_vars()
+ .expect("const parameter types cannot be generic"),
+ )
+ .into()
+ }
+ });
+ let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
+ let assoc_ty_args = tcx.mk_args(&args);
+ let bound =
+ bound.map_bound(|b| b.kind().skip_binder()).instantiate(tcx, assoc_ty_args);
+ ty::Binder::bind_with_vars(bound, bound_vars).to_predicate(tcx)
+ };
let normalized_bound = normalize_with_depth_to(
self,
obligation.param_env,
@@ -650,22 +633,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
(unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)),
);
- Ok(ImplSourceObjectData { vtable_base, nested })
+ Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested))
}
fn confirm_fn_pointer_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
- is_const: bool,
+ // FIXME(effects)
+ _is_const: bool,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
debug!(?obligation, "confirm_fn_pointer_candidate");
let tcx = self.tcx();
- let Some(self_ty) = self
- .infcx
- .shallow_resolve(obligation.self_ty().no_bound_vars()) else
- {
+ let Some(self_ty) = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars()) else {
// FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`,
// but we do not currently. Luckily, such a bound is not
// particularly useful, so we don't expect users to write
@@ -686,16 +667,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
let cause = obligation.derived_cause(BuiltinDerivedObligation);
- if obligation.is_const() && !is_const {
- // function is a trait method
- if let ty::FnDef(def_id, substs) = self_ty.kind() && let Some(trait_id) = tcx.trait_of_item(*def_id) {
- let trait_ref = TraitRef::from_method(tcx, trait_id, *substs);
- let poly_trait_pred = Binder::dummy(trait_ref).with_constness(ty::BoundConstness::ConstIfConst);
- let obligation = Obligation::new(tcx, cause.clone(), obligation.param_env, poly_trait_pred);
- nested.push(obligation);
- }
- }
-
// Confirm the `type Output: Sized;` bound that is present on `FnOnce`
let output_ty = self.infcx.instantiate_binder_with_placeholders(sig.output());
let output_ty = normalize_with_depth_to(
@@ -721,14 +692,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
let trait_ref = predicate.trait_ref;
let trait_def_id = trait_ref.def_id;
- let substs = trait_ref.substs;
+ let args = trait_ref.args;
let trait_obligations = self.impl_or_trait_obligations(
&obligation.cause,
obligation.recursion_depth,
obligation.param_env,
trait_def_id,
- &substs,
+ &args,
obligation.predicate,
);
@@ -741,17 +712,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
- // Okay to skip binder because the substs on generator types never
+ // Okay to skip binder because the args on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
- let ty::Generator(generator_def_id, substs, _) = *self_ty.kind() else {
+ let ty::Generator(generator_def_id, args, _) = *self_ty.kind() else {
bug!("closure candidate for non-closure {:?}", obligation);
};
- debug!(?obligation, ?generator_def_id, ?substs, "confirm_generator_candidate");
+ debug!(?obligation, ?generator_def_id, ?args, "confirm_generator_candidate");
- let gen_sig = substs.as_generator().poly_sig();
+ let gen_sig = args.as_generator().poly_sig();
// NOTE: The self-type is a generator type and hence is
// in fact unparameterized (or at least does not reference any
@@ -780,17 +751,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
- // Okay to skip binder because the substs on generator types never
+ // Okay to skip binder because the args on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
- let ty::Generator(generator_def_id, substs, _) = *self_ty.kind() else {
+ let ty::Generator(generator_def_id, args, _) = *self_ty.kind() else {
bug!("closure candidate for non-closure {:?}", obligation);
};
- debug!(?obligation, ?generator_def_id, ?substs, "confirm_future_candidate");
+ debug!(?obligation, ?generator_def_id, ?args, "confirm_future_candidate");
- let gen_sig = substs.as_generator().poly_sig();
+ let gen_sig = args.as_generator().poly_sig();
let trait_ref = super::util::future_trait_ref_and_outputs(
self.tcx(),
@@ -816,22 +787,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.fn_trait_kind_from_def_id(obligation.predicate.def_id())
.unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation));
- // Okay to skip binder because the substs on closure types never
+ // Okay to skip binder because the args on closure types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
- let ty::Closure(closure_def_id, substs) = *self_ty.kind() else {
+ let ty::Closure(closure_def_id, args) = *self_ty.kind() else {
bug!("closure candidate for non-closure {:?}", obligation);
};
- let trait_ref = self.closure_trait_ref_unnormalized(obligation, substs);
+ let trait_ref = self.closure_trait_ref_unnormalized(obligation, args);
let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
nested.push(obligation.with(
self.tcx(),
- ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)),
+ ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, args, kind)),
));
Ok(nested)
@@ -902,73 +873,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
obligation: &PolyTraitObligation<'tcx>,
idx: usize,
- ) -> Result<ImplSourceTraitUpcastingData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
// regions here. See the comment there for more details.
- let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap());
- let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
- let target = self.infcx.shallow_resolve(target);
-
- debug!(?source, ?target, "confirm_trait_upcasting_unsize_candidate");
-
- let mut nested = vec![];
- let source_trait_ref;
- let upcast_trait_ref;
- match (source.kind(), target.kind()) {
- // TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion).
- (
- &ty::Dynamic(ref data_a, r_a, repr_a @ ty::Dyn),
- &ty::Dynamic(ref data_b, r_b, ty::Dyn),
- ) => {
- // See `assemble_candidates_for_unsizing` for more info.
- // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`.
- let principal_a = data_a.principal().unwrap();
- source_trait_ref = principal_a.with_self_ty(tcx, source);
- upcast_trait_ref = util::supertraits(tcx, source_trait_ref).nth(idx).unwrap();
- assert_eq!(data_b.principal_def_id(), Some(upcast_trait_ref.def_id()));
- let existential_predicate = upcast_trait_ref.map_bound(|trait_ref| {
- ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
- tcx, trait_ref,
- ))
- });
- let iter = Some(existential_predicate)
- .into_iter()
- .chain(
- data_a
- .projection_bounds()
- .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)),
- )
- .chain(
- data_b
- .auto_traits()
- .map(ty::ExistentialPredicate::AutoTrait)
- .map(ty::Binder::dummy),
- );
- let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter);
- let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, repr_a);
-
- // Require that the traits involved in this upcast are **equal**;
- // only the **lifetime bound** is changed.
- let InferOk { obligations, .. } = self
- .infcx
- .at(&obligation.cause, obligation.param_env)
- .sup(DefineOpaqueTypes::No, target, source_trait)
- .map_err(|_| Unimplemented)?;
- nested.extend(obligations);
-
- let outlives = ty::OutlivesPredicate(r_a, r_b);
- nested.push(Obligation::with_depth(
- tcx,
- obligation.cause.clone(),
- obligation.recursion_depth + 1,
- obligation.param_env,
- obligation.predicate.rebind(outlives),
- ));
- }
- _ => bug!(),
- };
+ let predicate = obligation.predicate.no_bound_vars().unwrap();
+ let a_ty = self.infcx.shallow_resolve(predicate.self_ty());
+ let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1));
+
+ let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { bug!() };
+ let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { bug!() };
+
+ let source_principal = a_data.principal().unwrap().with_self_ty(tcx, a_ty);
+ let unnormalized_upcast_principal =
+ util::supertraits(tcx, source_principal).nth(idx).unwrap();
+
+ let nested = self
+ .match_upcast_principal(
+ obligation,
+ unnormalized_upcast_principal,
+ a_data,
+ b_data,
+ a_region,
+ b_region,
+ )?
+ .expect("did not expect ambiguity during confirmation");
let vtable_segment_callback = {
let mut vptr_offset = 0;
@@ -979,7 +909,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
vptr_offset += count_own_vtable_entries(tcx, trait_ref);
- if trait_ref == upcast_trait_ref {
+ if trait_ref == unnormalized_upcast_principal {
if emit_vptr {
return ControlFlow::Break(Some(vptr_offset));
} else {
@@ -997,27 +927,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
};
let vtable_vptr_slot =
- prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap();
+ prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap();
- Ok(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
+ Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested))
}
fn confirm_builtin_unsize_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
- ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
// regions here. See the comment there for more details.
let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap());
- let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
+ let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
let target = self.infcx.shallow_resolve(target);
-
debug!(?source, ?target, "confirm_builtin_unsize_candidate");
- let mut nested = vec![];
- match (source.kind(), target.kind()) {
+ Ok(match (source.kind(), target.kind()) {
// Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping).
(&ty::Dynamic(ref data_a, r_a, dyn_a), &ty::Dynamic(ref data_b, r_b, dyn_b))
if dyn_a == dyn_b =>
@@ -1044,22 +972,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Require that the traits involved in this upcast are **equal**;
// only the **lifetime bound** is changed.
- let InferOk { obligations, .. } = self
+ let InferOk { mut obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
.sup(DefineOpaqueTypes::No, target, source_trait)
.map_err(|_| Unimplemented)?;
- nested.extend(obligations);
// Register one obligation for 'a: 'b.
let outlives = ty::OutlivesPredicate(r_a, r_b);
- nested.push(Obligation::with_depth(
+ obligations.push(Obligation::with_depth(
tcx,
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
obligation.predicate.rebind(outlives),
));
+
+ ImplSource::Builtin(BuiltinImplSource::Misc, obligations)
}
// `T` -> `Trait`
@@ -1085,11 +1014,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// words, if the object type is `Foo + Send`, this would create an obligation for
// the `Send` check.)
// - Projection predicates
- nested.extend(
- data.iter().map(|predicate| {
- predicate_to_obligation(predicate.with_self_ty(tcx, source))
- }),
- );
+ let mut nested: Vec<_> = data
+ .iter()
+ .map(|predicate| predicate_to_obligation(predicate.with_self_ty(tcx, source)))
+ .collect();
// We can only make objects from sized types.
let tr = ty::TraitRef::from_lang_item(
@@ -1106,6 +1034,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
nested.push(predicate_to_obligation(
ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx),
));
+
+ ImplSource::Builtin(BuiltinImplSource::Misc, nested)
}
// `[T; n]` -> `[T]`
@@ -1115,11 +1045,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.at(&obligation.cause, obligation.param_env)
.eq(DefineOpaqueTypes::No, b, a)
.map_err(|_| Unimplemented)?;
- nested.extend(obligations);
+
+ ImplSource::Builtin(BuiltinImplSource::Misc, obligations)
}
// `Struct<T>` -> `Struct<U>`
- (&ty::Adt(def, substs_a), &ty::Adt(_, substs_b)) => {
+ (&ty::Adt(def, args_a), &ty::Adt(_, args_b)) => {
let unsizing_params = tcx.unsizing_params_for_adt(def.did());
if unsizing_params.is_empty() {
return Err(Unimplemented);
@@ -1128,6 +1059,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let tail_field = def.non_enum_variant().tail();
let tail_field_ty = tcx.type_of(tail_field.did);
+ let mut nested = vec![];
+
// Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`,
// normalizing in the process, since `type_of` returns something directly from
// astconv (which means it's un-normalized).
@@ -1136,7 +1069,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
- tail_field_ty.subst(tcx, substs_a),
+ tail_field_ty.instantiate(tcx, args_a),
&mut nested,
);
let target_tail = normalize_with_depth_to(
@@ -1144,16 +1077,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
- tail_field_ty.subst(tcx, substs_b),
+ tail_field_ty.instantiate(tcx, args_b),
&mut nested,
);
// Check that the source struct with the target's
// unsizing parameters is equal to the target.
- let substs = tcx.mk_substs_from_iter(substs_a.iter().enumerate().map(|(i, k)| {
- if unsizing_params.contains(i as u32) { substs_b[i] } else { k }
- }));
- let new_struct = Ty::new_adt(tcx, def, substs);
+ let args =
+ tcx.mk_args_from_iter(args_a.iter().enumerate().map(|(i, k)| {
+ if unsizing_params.contains(i as u32) { args_b[i] } else { k }
+ }));
+ let new_struct = Ty::new_adt(tcx, def, args);
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
@@ -1171,6 +1105,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
),
);
nested.push(tail_unsize_obligation);
+
+ ImplSource::Builtin(BuiltinImplSource::Misc, nested)
}
// `(.., T)` -> `(.., U)`
@@ -1185,25 +1121,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// last element is equal to the target.
let new_tuple =
Ty::new_tup_from_iter(tcx, a_mid.iter().copied().chain(iter::once(b_last)));
- let InferOk { obligations, .. } = self
+ let InferOk { mut obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
.eq(DefineOpaqueTypes::No, target, new_tuple)
.map_err(|_| Unimplemented)?;
- nested.extend(obligations);
// Add a nested `T: Unsize<U>` predicate.
let last_unsize_obligation = obligation.with(
tcx,
ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]),
);
- nested.push(last_unsize_obligation);
+ obligations.push(last_unsize_obligation);
+
+ ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, obligations)
}
_ => bug!("source: {source}, target: {target}"),
- };
-
- Ok(nested)
+ })
}
fn confirm_const_destruct_candidate(
@@ -1212,7 +1147,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
impl_def_id: Option<DefId>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// `~const Destruct` in a non-const environment is always trivially true, since our type is `Drop`
- if !obligation.is_const() {
+ // FIXME(effects)
+ if true {
return Ok(vec![]);
}
@@ -1233,8 +1169,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
trait_pred.trait_ref.def_id = drop_trait;
trait_pred
});
- let substs = self.rematch_impl(impl_def_id, &new_obligation);
- debug!(?substs, "impl substs");
+ let args = self.rematch_impl(impl_def_id, &new_obligation);
+ debug!(?args, "impl args");
let cause = obligation.derived_cause(|derived| {
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
@@ -1247,7 +1183,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let obligations = ensure_sufficient_stack(|| {
self.vtable_impl(
impl_def_id,
- substs,
+ args,
&cause,
new_obligation.recursion_depth + 1,
new_obligation.param_env,
@@ -1259,7 +1195,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// We want to confirm the ADT's fields if we have an ADT
let mut stack = match *self_ty.skip_binder().kind() {
- ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(tcx, substs)).collect(),
+ ty::Adt(def, args) => def.all_fields().map(|f| f.ty(tcx, args)).collect(),
_ => vec![self_ty.skip_binder()],
};
@@ -1292,20 +1228,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ty::Tuple(tys) => {
stack.extend(tys.iter());
}
- ty::Closure(_, substs) => {
- stack.push(substs.as_closure().tupled_upvars_ty());
+ ty::Closure(_, args) => {
+ stack.push(args.as_closure().tupled_upvars_ty());
}
- ty::Generator(_, substs, _) => {
- let generator = substs.as_generator();
+ ty::Generator(_, args, _) => {
+ let generator = args.as_generator();
stack.extend([generator.tupled_upvars_ty(), generator.witness()]);
}
ty::GeneratorWitness(tys) => {
stack.extend(tcx.erase_late_bound_regions(tys).to_vec());
}
- ty::GeneratorWitnessMIR(def_id, substs) => {
+ ty::GeneratorWitnessMIR(def_id, args) => {
let tcx = self.tcx();
stack.extend(tcx.generator_hidden_types(def_id).map(|bty| {
- let ty = bty.subst(tcx, substs);
+ let ty = bty.instantiate(tcx, args);
debug_assert!(!ty.has_late_bound_regions());
ty
}))
@@ -1314,6 +1250,7 @@ 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::Inherent, ..) => {
+ // FIXME(effects) this needs constness
let predicate = normalize_with_depth_to(
self,
obligation.param_env,
@@ -1326,7 +1263,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
cause.span,
[nested_ty],
),
- constness: ty::BoundConstness::ConstIfConst,
polarity: ty::ImplPolarity::Positive,
}),
&mut nested,
@@ -1345,6 +1281,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// since it's either not `const Drop` (and we raise an error during selection),
// or it's an ADT (and we need to check for a custom impl during selection)
_ => {
+ // FIXME(effects) this needs constness
let predicate = self_ty.rebind(ty::TraitPredicate {
trait_ref: ty::TraitRef::from_lang_item(
self.tcx(),
@@ -1352,7 +1289,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
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 7f31ab751..19385e2d7 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -40,7 +40,7 @@ use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::relate::TypeRelation;
-use rustc_middle::ty::SubstsRef;
+use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_span::symbol::sym;
@@ -74,22 +74,21 @@ impl IntercrateAmbiguityCause {
match self {
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } => {
let self_desc = if let Some(ty) = self_desc {
- format!(" for type `{}`", ty)
+ format!(" for type `{ty}`")
} else {
String::new()
};
- format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc)
+ format!("downstream crates may implement trait `{trait_desc}`{self_desc}")
}
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } => {
let self_desc = if let Some(ty) = self_desc {
- format!(" for type `{}`", ty)
+ format!(" for type `{ty}`")
} else {
String::new()
};
format!(
- "upstream crates may add a new impl of trait `{}`{} \
- in future versions",
- trait_desc, self_desc
+ "upstream crates may add a new impl of trait `{trait_desc}`{self_desc} \
+ in future versions"
)
}
IntercrateAmbiguityCause::ReservationImpl { message } => message.clone(),
@@ -119,6 +118,8 @@ pub struct SelectionContext<'cx, 'tcx> {
/// policy. In essence, canonicalized queries need their errors propagated
/// rather than immediately reported because we do not have accurate spans.
query_mode: TraitQueryMode,
+
+ treat_inductive_cycle: TreatInductiveCycleAs,
}
// A stack that walks back up the stack frame.
@@ -199,6 +200,27 @@ enum BuiltinImplConditions<'tcx> {
Ambiguous,
}
+#[derive(Copy, Clone)]
+pub enum TreatInductiveCycleAs {
+ /// This is the previous behavior, where `Recur` represents an inductive
+ /// cycle that is known not to hold. This is not forwards-compatible with
+ /// coinduction, and will be deprecated. This is the default behavior
+ /// of the old trait solver due to back-compat reasons.
+ Recur,
+ /// This is the behavior of the new trait solver, where inductive cycles
+ /// are treated as ambiguous and possibly holding.
+ Ambig,
+}
+
+impl From<TreatInductiveCycleAs> for EvaluationResult {
+ fn from(treat: TreatInductiveCycleAs) -> EvaluationResult {
+ match treat {
+ TreatInductiveCycleAs::Ambig => EvaluatedToUnknown,
+ TreatInductiveCycleAs::Recur => EvaluatedToRecur,
+ }
+ }
+}
+
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> {
SelectionContext {
@@ -206,9 +228,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
freshener: infcx.freshener(),
intercrate_ambiguity_causes: None,
query_mode: TraitQueryMode::Standard,
+ treat_inductive_cycle: TreatInductiveCycleAs::Recur,
}
}
+ // Sets the `TreatInductiveCycleAs` mode temporarily in the selection context
+ pub fn with_treat_inductive_cycle_as<T>(
+ &mut self,
+ treat_inductive_cycle: TreatInductiveCycleAs,
+ f: impl FnOnce(&mut Self) -> T,
+ ) -> T {
+ // Should be executed in a context where caching is disabled,
+ // otherwise the cache is poisoned with the temporary result.
+ assert!(self.is_intercrate());
+ let treat_inductive_cycle =
+ std::mem::replace(&mut self.treat_inductive_cycle, treat_inductive_cycle);
+ let value = f(self);
+ self.treat_inductive_cycle = treat_inductive_cycle;
+ value
+ }
+
pub fn with_query_mode(
infcx: &'cx InferCtxt<'tcx>,
query_mode: TraitQueryMode,
@@ -720,7 +759,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
stack.update_reached_depth(stack_arg.1);
return Ok(EvaluatedToOk);
} else {
- return Ok(EvaluatedToRecur);
+ return Ok(self.treat_inductive_cycle.into());
}
}
return Ok(EvaluatedToOk);
@@ -838,13 +877,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig),
- ProjectAndUnifyResult::Recursive => Ok(EvaluatedToRecur),
+ ProjectAndUnifyResult::Recursive => Ok(self.treat_inductive_cycle.into()),
ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr),
}
}
- ty::PredicateKind::ClosureKind(_, closure_substs, kind) => {
- match self.infcx.closure_kind(closure_substs) {
+ ty::PredicateKind::ClosureKind(_, closure_args, kind) => {
+ match self.infcx.closure_kind(closure_args) {
Some(closure_kind) => {
if closure_kind.extends(kind) {
Ok(EvaluatedToOk)
@@ -895,7 +934,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.infcx
.at(&obligation.cause, obligation.param_env)
.trace(c1, c2)
- .eq(DefineOpaqueTypes::No, a.substs, b.substs)
+ .eq(DefineOpaqueTypes::No, a.args, b.args)
{
return self.evaluate_predicates_recursively(
previous_stack,
@@ -1000,13 +1039,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
let stack = self.push_stack(previous_stack, &obligation);
- let mut fresh_trait_pred = stack.fresh_trait_pred;
- let mut param_env = obligation.param_env;
-
- fresh_trait_pred = fresh_trait_pred.map_bound(|mut pred| {
- pred.remap_constness(&mut param_env);
- pred
- });
+ let fresh_trait_pred = stack.fresh_trait_pred;
+ let param_env = obligation.param_env;
debug!(?fresh_trait_pred);
@@ -1157,7 +1191,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Some(EvaluatedToOk)
} else {
debug!("evaluate_stack --> recursive, inductive");
- Some(EvaluatedToRecur)
+ Some(self.treat_inductive_cycle.into())
}
} else {
None
@@ -1194,7 +1228,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// terms of `Fn` etc, but we could probably make this more
// precise still.
let unbound_input_types =
- stack.fresh_trait_pred.skip_binder().trait_ref.substs.types().any(|ty| ty.is_fresh());
+ stack.fresh_trait_pred.skip_binder().trait_ref.args.types().any(|ty| ty.is_fresh());
if unbound_input_types
&& stack.iter().skip(1).any(|prev| {
@@ -1386,8 +1420,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
(result, dep_node)
}
- /// filter_impls filters constant trait obligations and candidates that have a positive impl
- /// for a negative goal and a negative impl for a positive goal
+ /// filter_impls filters candidates that have a positive impl for a negative
+ /// goal and a negative impl for a positive goal
#[instrument(level = "debug", skip(self, candidates))]
fn filter_impls(
&mut self,
@@ -1399,42 +1433,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let mut result = Vec::with_capacity(candidates.len());
for candidate in candidates {
- // Respect const trait obligations
- if obligation.is_const() {
- match candidate {
- // const impl
- ImplCandidate(def_id) if tcx.constness(def_id) == hir::Constness::Const => {}
- // const param
- ParamCandidate(trait_pred) if trait_pred.is_const_if_const() => {}
- // const projection
- ProjectionCandidate(_, ty::BoundConstness::ConstIfConst)
- // auto trait impl
- | AutoImplCandidate
- // generator / future, this will raise error in other places
- // or ignore error with const_async_blocks feature
- | GeneratorCandidate
- | FutureCandidate
- // FnDef where the function is const
- | FnPointerCandidate { is_const: true }
- | ConstDestructCandidate(_)
- | ClosureCandidate { is_const: true } => {}
-
- FnPointerCandidate { is_const: false } => {
- if let ty::FnDef(def_id, _) = obligation.self_ty().skip_binder().kind() && tcx.trait_of_item(*def_id).is_some() {
- // Trait methods are not seen as const unless the trait is implemented as const.
- // We do not filter that out in here, but nested obligations will be needed to confirm this.
- } else {
- continue
- }
- }
-
- _ => {
- // reject all other types of candidates
- continue;
- }
- }
- }
-
if let ImplCandidate(def_id) = candidate {
if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id)
|| obligation.polarity() == tcx.impl_polarity(def_id)
@@ -1487,7 +1485,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Result<(), Conflict> {
debug!("is_knowable(intercrate={:?})", self.is_intercrate());
- if !self.is_intercrate() || stack.obligation.polarity() == ty::ImplPolarity::Negative {
+ if !self.is_intercrate() {
return Ok(());
}
@@ -1499,7 +1497,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// bound regions.
let trait_ref = predicate.skip_binder().trait_ref;
- coherence::trait_ref_is_knowable(self.tcx(), trait_ref)
+ coherence::trait_ref_is_knowable::<!>(self.tcx(), trait_ref, |ty| Ok(ty)).unwrap()
}
/// Returns `true` if the global caches can be used.
@@ -1528,7 +1526,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn check_candidate_cache(
&mut self,
- mut param_env: ty::ParamEnv<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>> {
// Neither the global nor local cache is aware of intercrate
@@ -1539,8 +1537,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return None;
}
let tcx = self.tcx();
- let mut pred = cache_fresh_trait_pred.skip_binder();
- pred.remap_constness(&mut param_env);
+ let pred = cache_fresh_trait_pred.skip_binder();
if self.can_use_global_caches(param_env) {
if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) {
@@ -1586,15 +1583,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(skip(self, param_env, cache_fresh_trait_pred, dep_node), level = "debug")]
fn insert_candidate_cache(
&mut self,
- mut param_env: ty::ParamEnv<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
dep_node: DepNodeIndex,
candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>,
) {
let tcx = self.tcx();
- let mut pred = cache_fresh_trait_pred.skip_binder();
-
- pred.remap_constness(&mut param_env);
+ let pred = cache_fresh_trait_pred.skip_binder();
if !self.can_cache_candidate(&candidate) {
debug!(?pred, ?candidate, "insert_candidate_cache - candidate is not cacheable");
@@ -1628,16 +1623,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn match_projection_obligation_against_definition_bounds(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
- ) -> smallvec::SmallVec<[(usize, ty::BoundConstness); 2]> {
+ ) -> smallvec::SmallVec<[usize; 2]> {
let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
let placeholder_trait_predicate =
self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate);
debug!(?placeholder_trait_predicate);
let tcx = self.infcx.tcx;
- let (def_id, substs) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
- ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- (def_id, substs)
+ let (def_id, args) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
+ ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
+ (def_id, args)
}
_ => {
span_bug!(
@@ -1648,7 +1643,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
}
};
- let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
+ let bounds = tcx.item_bounds(def_id).instantiate(tcx, args);
// The bounds returned by `item_bounds` may contain duplicates after
// normalization, so try to deduplicate when possible to avoid
@@ -1677,7 +1672,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
_ => false,
}
}) {
- return Some((idx, pred.constness));
+ return Some(idx);
}
}
None
@@ -1785,11 +1780,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if is_match {
let generics = self.tcx().generics_of(obligation.predicate.def_id);
// FIXME(generic-associated-types): Addresses aggressive inference in #92917.
- // If this type is a GAT, and of the GAT substs resolve to something new,
+ // If this type is a GAT, and of the GAT args resolve to something new,
// that means that we must have newly inferred something about the GAT.
// We should give up in that case.
if !generics.params.is_empty()
- && obligation.predicate.substs[generics.parent_count..]
+ && obligation.predicate.args[generics.parent_count..]
.iter()
.any(|&p| p.has_non_region_infer() && self.infcx.shallow_resolve(p) != p)
{
@@ -1827,6 +1822,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
/// candidates and prefer where-clause candidates.
///
/// See the comment for "SelectionCandidate" for more details.
+ #[instrument(level = "debug", skip(self))]
fn candidate_should_be_dropped_in_favor_of(
&mut self,
victim: &EvaluatedCandidate<'tcx>,
@@ -1850,13 +1846,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// This is a fix for #53123 and prevents winnowing from accidentally extending the
// lifetime of a variable.
match (&other.candidate, &victim.candidate) {
- (_, AutoImplCandidate) | (AutoImplCandidate, _) => {
- bug!(
- "default implementations shouldn't be recorded \
- when there are other valid candidates"
- );
- }
-
// FIXME(@jswrenn): this should probably be more sophisticated
(TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No,
@@ -1871,7 +1860,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
(ParamCandidate(other), ParamCandidate(victim)) => {
let same_except_bound_vars = other.skip_binder().trait_ref
== victim.skip_binder().trait_ref
- && other.skip_binder().constness == victim.skip_binder().constness
&& other.skip_binder().polarity == victim.skip_binder().polarity
&& !other.skip_binder().trait_ref.has_escaping_bound_vars();
if same_except_bound_vars {
@@ -1881,12 +1869,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// probably best characterized as a "hack", since we might prefer to just do our
// best to *not* create essentially duplicate candidates in the first place.
DropVictim::drop_if(other.bound_vars().len() <= victim.bound_vars().len())
- } else if other.skip_binder().trait_ref == victim.skip_binder().trait_ref
- && victim.skip_binder().constness == ty::BoundConstness::NotConst
- && other.skip_binder().polarity == victim.skip_binder().polarity
- {
- // Drop otherwise equivalent non-const candidates in favor of const candidates.
- DropVictim::Yes
} else {
DropVictim::No
}
@@ -1898,6 +1880,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
(
ParamCandidate(ref other_cand),
ImplCandidate(..)
+ | AutoImplCandidate
| ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
@@ -1925,6 +1908,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
(
ImplCandidate(_)
+ | AutoImplCandidate
| ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
@@ -1958,6 +1942,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
(
ObjectCandidate(_) | ProjectionCandidate(..),
ImplCandidate(..)
+ | AutoImplCandidate
| ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
@@ -1971,6 +1956,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
(
ImplCandidate(..)
+ | AutoImplCandidate
| ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
@@ -2061,6 +2047,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
}
+ (AutoImplCandidate, ImplCandidate(_)) | (ImplCandidate(_), AutoImplCandidate) => {
+ DropVictim::No
+ }
+
+ (AutoImplCandidate, _) | (_, AutoImplCandidate) => {
+ bug!(
+ "default implementations shouldn't be recorded \
+ when there are other global candidates: {:?} {:?}",
+ other,
+ victim
+ );
+ }
+
// Everything else is ambiguous
(
ImplCandidate(_)
@@ -2127,13 +2126,13 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])),
),
- ty::Adt(def, substs) => {
+ ty::Adt(def, args) => {
let sized_crit = def.sized_constraint(self.tcx());
// (*) binder moved here
Where(
obligation
.predicate
- .rebind(sized_crit.subst_iter_copied(self.tcx(), substs).collect()),
+ .rebind(sized_crit.iter_instantiated(self.tcx(), args).collect()),
)
}
@@ -2159,14 +2158,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
use self::BuiltinImplConditions::{Ambiguous, None, Where};
match *self_ty.kind() {
- ty::Infer(ty::IntVar(_))
- | ty::Infer(ty::FloatVar(_))
- | ty::FnDef(..)
- | ty::FnPtr(_)
- | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())),
+ ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())),
ty::Uint(_)
| ty::Int(_)
+ | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Bool
| ty::Float(_)
| ty::Char
@@ -2190,20 +2186,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
Where(obligation.predicate.rebind(tys.iter().collect()))
}
- ty::Generator(_, substs, hir::Movability::Movable) => {
+ ty::Generator(_, args, hir::Movability::Movable) => {
if self.tcx().features().generator_clone {
let resolved_upvars =
- self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty());
+ self.infcx.shallow_resolve(args.as_generator().tupled_upvars_ty());
let resolved_witness =
- self.infcx.shallow_resolve(substs.as_generator().witness());
+ self.infcx.shallow_resolve(args.as_generator().witness());
if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() {
// Not yet resolved.
Ambiguous
} else {
- let all = substs
+ let all = args
.as_generator()
.upvar_tys()
- .chain(iter::once(substs.as_generator().witness()))
+ .iter()
+ .chain([args.as_generator().witness()])
.collect::<Vec<_>>();
Where(obligation.predicate.rebind(all))
}
@@ -2227,24 +2224,24 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
Where(ty::Binder::bind_with_vars(witness_tys.to_vec(), all_vars))
}
- ty::GeneratorWitnessMIR(def_id, ref substs) => {
+ ty::GeneratorWitnessMIR(def_id, ref args) => {
let hidden_types = bind_generator_hidden_types_above(
self.infcx,
def_id,
- substs,
+ args,
obligation.predicate.bound_vars(),
);
Where(hidden_types)
}
- ty::Closure(_, substs) => {
+ ty::Closure(_, args) => {
// (*) binder moved here
- let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty());
+ let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty());
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
// Not yet resolved.
Ambiguous
} else {
- Where(obligation.predicate.rebind(substs.as_closure().upvar_tys().collect()))
+ Where(obligation.predicate.rebind(args.as_closure().upvar_tys().to_vec()))
}
}
@@ -2321,14 +2318,14 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
t.rebind(tys.iter().collect())
}
- ty::Closure(_, ref substs) => {
- let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty());
+ ty::Closure(_, ref args) => {
+ let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty());
t.rebind(vec![ty])
}
- ty::Generator(_, ref substs, _) => {
- let ty = self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty());
- let witness = substs.as_generator().witness();
+ ty::Generator(_, ref args, _) => {
+ let ty = self.infcx.shallow_resolve(args.as_generator().tupled_upvars_ty());
+ let witness = args.as_generator().witness();
t.rebind([ty].into_iter().chain(iter::once(witness)).collect())
}
@@ -2337,18 +2334,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
types.map_bound(|types| types.to_vec())
}
- ty::GeneratorWitnessMIR(def_id, ref substs) => {
- bind_generator_hidden_types_above(self.infcx, def_id, substs, t.bound_vars())
+ ty::GeneratorWitnessMIR(def_id, ref args) => {
+ bind_generator_hidden_types_above(self.infcx, def_id, args, t.bound_vars())
}
// For `PhantomData<T>`, we pass `T`.
- ty::Adt(def, substs) if def.is_phantom_data() => t.rebind(substs.types().collect()),
+ ty::Adt(def, args) if def.is_phantom_data() => t.rebind(args.types().collect()),
- ty::Adt(def, substs) => {
- t.rebind(def.all_fields().map(|f| f.ty(self.tcx(), substs)).collect())
+ ty::Adt(def, args) => {
+ t.rebind(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect())
}
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
let ty = self.tcx().type_of(def_id);
if ty.skip_binder().references_error() {
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
@@ -2356,7 +2353,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
- t.rebind(vec![ty.subst(self.tcx(), substs)])
+ t.rebind(vec![ty.instantiate(self.tcx(), args)])
}
})
}
@@ -2428,13 +2425,13 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
&mut self,
impl_def_id: DefId,
obligation: &PolyTraitObligation<'tcx>,
- ) -> Normalized<'tcx, SubstsRef<'tcx>> {
+ ) -> Normalized<'tcx, GenericArgsRef<'tcx>> {
let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
match self.match_impl(impl_def_id, impl_trait_ref, obligation) {
- Ok(substs) => substs,
+ Ok(args) => args,
Err(()) => {
// FIXME: A rematch may fail when a candidate cache hit occurs
- // on thefreshened form of the trait predicate, but the match
+ // on the freshened form of the trait predicate, but the match
// fails for some reason that is not captured in the freshened
// cache key. For example, equating an impl trait ref against
// the placeholder trait ref may fail due the Generalizer relation
@@ -2443,11 +2440,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
let guar = self.infcx.tcx.sess.delay_span_bug(
obligation.cause.span,
format!(
- "Impl {:?} was matchable against {:?} but now is not",
- impl_def_id, obligation
+ "Impl {impl_def_id:?} was matchable against {obligation:?} but now is not"
),
);
- let value = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id);
+ let value = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id);
let err = Ty::new_error(self.tcx(), guar);
let value = value.fold_with(&mut BottomUpFolder {
tcx: self.tcx(),
@@ -2466,14 +2462,14 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
impl_def_id: DefId,
impl_trait_ref: EarlyBinder<ty::TraitRef<'tcx>>,
obligation: &PolyTraitObligation<'tcx>,
- ) -> Result<Normalized<'tcx, SubstsRef<'tcx>>, ()> {
+ ) -> Result<Normalized<'tcx, GenericArgsRef<'tcx>>, ()> {
let placeholder_obligation =
self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref;
- let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id);
+ let impl_args = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id);
- let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs);
+ let impl_trait_ref = impl_trait_ref.instantiate(self.tcx(), impl_args);
if impl_trait_ref.references_error() {
return Err(());
}
@@ -2515,7 +2511,99 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
return Err(());
}
- Ok(Normalized { value: impl_substs, obligations: nested_obligations })
+ Ok(Normalized { value: impl_args, obligations: nested_obligations })
+ }
+
+ fn match_upcast_principal(
+ &mut self,
+ obligation: &PolyTraitObligation<'tcx>,
+ unnormalized_upcast_principal: ty::PolyTraitRef<'tcx>,
+ a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+ b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+ a_region: ty::Region<'tcx>,
+ b_region: ty::Region<'tcx>,
+ ) -> SelectionResult<'tcx, Vec<PredicateObligation<'tcx>>> {
+ let tcx = self.tcx();
+ let mut nested = vec![];
+
+ let upcast_principal = normalize_with_depth_to(
+ self,
+ obligation.param_env,
+ obligation.cause.clone(),
+ obligation.recursion_depth + 1,
+ unnormalized_upcast_principal,
+ &mut nested,
+ );
+
+ for bound in b_data {
+ match bound.skip_binder() {
+ // Check that a_ty's supertrait (upcast_principal) is compatible
+ // with the target (b_ty).
+ ty::ExistentialPredicate::Trait(target_principal) => {
+ nested.extend(
+ self.infcx
+ .at(&obligation.cause, obligation.param_env)
+ .sup(
+ DefineOpaqueTypes::No,
+ upcast_principal.map_bound(|trait_ref| {
+ ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
+ }),
+ bound.rebind(target_principal),
+ )
+ .map_err(|_| SelectionError::Unimplemented)?
+ .into_obligations(),
+ );
+ }
+ // Check that b_ty's projection is satisfied by exactly one of
+ // a_ty's projections. First, we look through the list to see if
+ // any match. If not, error. Then, if *more* than one matches, we
+ // return ambiguity. Otherwise, if exactly one matches, equate
+ // it with b_ty's projection.
+ ty::ExistentialPredicate::Projection(target_projection) => {
+ let target_projection = bound.rebind(target_projection);
+ let mut matching_projections =
+ a_data.projection_bounds().filter(|source_projection| {
+ // Eager normalization means that we can just use can_eq
+ // here instead of equating and processing obligations.
+ source_projection.item_def_id() == target_projection.item_def_id()
+ && self.infcx.can_eq(
+ obligation.param_env,
+ *source_projection,
+ target_projection,
+ )
+ });
+ let Some(source_projection) = matching_projections.next() else {
+ return Err(SelectionError::Unimplemented);
+ };
+ if matching_projections.next().is_some() {
+ return Ok(None);
+ }
+ nested.extend(
+ self.infcx
+ .at(&obligation.cause, obligation.param_env)
+ .sup(DefineOpaqueTypes::No, source_projection, target_projection)
+ .map_err(|_| SelectionError::Unimplemented)?
+ .into_obligations(),
+ );
+ }
+ // Check that b_ty's auto traits are present in a_ty's bounds.
+ ty::ExistentialPredicate::AutoTrait(def_id) => {
+ if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
+ return Err(SelectionError::Unimplemented);
+ }
+ }
+ }
+ }
+
+ nested.push(Obligation::with_depth(
+ tcx,
+ obligation.cause.clone(),
+ obligation.recursion_depth + 1,
+ obligation.param_env,
+ ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
+ ));
+
+ Ok(Some(nested))
}
/// Normalize `where_clause_trait_ref` and try to match it against
@@ -2580,9 +2668,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn closure_trait_ref_unnormalized(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
- substs: SubstsRef<'tcx>,
+ args: GenericArgsRef<'tcx>,
) -> ty::PolyTraitRef<'tcx> {
- let closure_sig = substs.as_closure().sig();
+ let closure_sig = args.as_closure().sig();
debug!(?closure_sig);
@@ -2615,8 +2703,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
cause: &ObligationCause<'tcx>,
recursion_depth: usize,
param_env: ty::ParamEnv<'tcx>,
- def_id: DefId, // of impl or trait
- substs: SubstsRef<'tcx>, // for impl or trait
+ def_id: DefId, // of impl or trait
+ args: GenericArgsRef<'tcx>, // for impl or trait
parent_trait_pred: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
) -> Vec<PredicateObligation<'tcx>> {
let tcx = self.tcx();
@@ -2637,7 +2725,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// that order.
let predicates = tcx.predicates_of(def_id);
assert_eq!(predicates.parent, None);
- let predicates = predicates.instantiate_own(tcx, substs);
+ let predicates = predicates.instantiate_own(tcx, args);
let mut obligations = Vec::with_capacity(predicates.len());
for (index, (predicate, span)) in predicates.into_iter().enumerate() {
let cause =
@@ -2990,7 +3078,7 @@ pub enum ProjectionMatchesProjection {
fn bind_generator_hidden_types_above<'tcx>(
infcx: &InferCtxt<'tcx>,
def_id: DefId,
- substs: ty::SubstsRef<'tcx>,
+ args: ty::GenericArgsRef<'tcx>,
bound_vars: &ty::List<ty::BoundVariableKind>,
) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
let tcx = infcx.tcx;
@@ -3006,7 +3094,7 @@ fn bind_generator_hidden_types_above<'tcx>(
// Deduplicate tys to avoid repeated work.
.filter(|bty| seen_tys.insert(*bty))
.map(|bty| {
- let mut ty = bty.subst(tcx, substs);
+ let mut ty = bty.instantiate(tcx, args);
// Only remap erased regions if we use them.
if considering_regions {
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 96f128741..729cf2f33 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -23,7 +23,7 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{error_code, DelayDm, Diagnostic};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt};
-use rustc_middle::ty::{InternalSubsts, SubstsRef};
+use rustc_middle::ty::{GenericArgs, GenericArgsRef};
use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
use rustc_span::{Span, DUMMY_SP};
@@ -48,7 +48,7 @@ pub struct OverlapError<'tcx> {
/// When we have selected one impl, but are actually using item definitions from
/// a parent impl providing a default, we need a way to translate between the
/// type parameters of the two impls. Here the `source_impl` is the one we've
-/// selected, and `source_substs` is a substitution of its generics.
+/// selected, and `source_args` is a substitution of its generics.
/// And `target_node` is the impl/trait we're actually going to get the
/// definition from. The resulting substitution will map from `target_node`'s
/// generics to `source_impl`'s generics as instantiated by `source_subst`.
@@ -76,51 +76,46 @@ pub struct OverlapError<'tcx> {
/// through associated type projection. We deal with such cases by using
/// *fulfillment* to relate the two impls, requiring that all projections are
/// resolved.
-pub fn translate_substs<'tcx>(
+pub fn translate_args<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
source_impl: DefId,
- source_substs: SubstsRef<'tcx>,
+ source_args: GenericArgsRef<'tcx>,
target_node: specialization_graph::Node,
-) -> SubstsRef<'tcx> {
- translate_substs_with_cause(
- infcx,
- param_env,
- source_impl,
- source_substs,
- target_node,
- |_, _| ObligationCause::dummy(),
- )
+) -> GenericArgsRef<'tcx> {
+ translate_args_with_cause(infcx, param_env, source_impl, source_args, target_node, |_, _| {
+ ObligationCause::dummy()
+ })
}
-/// Like [translate_substs], but obligations from the parent implementation
+/// Like [translate_args], 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>(
+pub fn translate_args_with_cause<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
source_impl: DefId,
- source_substs: SubstsRef<'tcx>,
+ source_args: GenericArgsRef<'tcx>,
target_node: specialization_graph::Node,
cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
-) -> SubstsRef<'tcx> {
+) -> GenericArgsRef<'tcx> {
debug!(
- "translate_substs({:?}, {:?}, {:?}, {:?})",
- param_env, source_impl, source_substs, target_node
+ "translate_args({:?}, {:?}, {:?}, {:?})",
+ param_env, source_impl, source_args, target_node
);
let source_trait_ref =
- infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs);
+ infcx.tcx.impl_trait_ref(source_impl).unwrap().instantiate(infcx.tcx, &source_args);
// translate the Self and Param parts of the substitution, since those
// vary across impls
- let target_substs = match target_node {
+ let target_args = match target_node {
specialization_graph::Node::Impl(target_impl) => {
// no need to translate if we're targeting the impl we started with
if source_impl == target_impl {
- return source_substs;
+ return source_args;
}
fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl, cause)
@@ -131,11 +126,11 @@ pub fn translate_substs_with_cause<'tcx>(
)
})
}
- specialization_graph::Node::Trait(..) => source_trait_ref.substs,
+ specialization_graph::Node::Trait(..) => source_trait_ref.args,
};
// directly inherent the method generics, since those do not vary across impls
- source_substs.rebase_onto(infcx.tcx, source_impl, target_substs)
+ source_args.rebase_onto(infcx.tcx, source_impl, target_args)
}
/// Is `impl1` a specialization of `impl2`?
@@ -172,7 +167,7 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
let penv = tcx.param_env(impl1_def_id);
- let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap().subst_identity();
+ let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap().instantiate_identity();
// Create an infcx, taking the predicates of impl1 as assumptions:
let infcx = tcx.infer_ctxt().build();
@@ -196,7 +191,7 @@ fn fulfill_implication<'tcx>(
source_impl: DefId,
target_impl: DefId,
error_cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
-) -> Result<SubstsRef<'tcx>, ()> {
+) -> Result<GenericArgsRef<'tcx>, ()> {
debug!(
"fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
param_env, source_trait_ref, target_impl
@@ -221,18 +216,16 @@ fn fulfill_implication<'tcx>(
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_args = infcx.fresh_args_for_item(DUMMY_SP, target_impl);
let (target_trait, obligations) =
- util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs, error_cause);
+ util::impl_subject_and_oblig(selcx, param_env, target_impl, target_args, 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)
+ let Ok(InferOk { obligations: more_obligations, .. }) = infcx
+ .at(&ObligationCause::dummy(), param_env)
+ .eq(DefineOpaqueTypes::No, source_trait, target_trait)
else {
- debug!(
- "fulfill_implication: {:?} does not unify with {:?}",
- source_trait, target_trait
- );
+ debug!("fulfill_implication: {:?} does not unify with {:?}", source_trait, target_trait);
return Err(());
};
@@ -261,7 +254,7 @@ fn fulfill_implication<'tcx>(
// Now resolve the *substitution* we built for the target earlier, replacing
// the inference variables inside with whatever we got from fulfillment.
- Ok(infcx.resolve_vars_if_possible(target_substs))
+ Ok(infcx.resolve_vars_if_possible(target_args))
}
/// Query provider for `specialization_graph_of`.
@@ -395,16 +388,16 @@ fn report_conflicting_impls<'tcx>(
impl_span,
format!(
"conflicting implementation{}",
- overlap.self_ty.map_or_else(String::new, |ty| format!(" for `{}`", ty))
+ overlap.self_ty.map_or_else(String::new, |ty| format!(" for `{ty}`"))
),
);
}
Err(cname) => {
let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
Some(s) => {
- format!("conflicting implementation in crate `{}`:\n- {}", cname, s)
+ format!("conflicting implementation in crate `{cname}`:\n- {s}")
}
- None => format!("conflicting implementation in crate `{}`", cname),
+ None => format!("conflicting implementation in crate `{cname}`"),
};
err.note(msg);
}
@@ -469,21 +462,21 @@ fn report_conflicting_impls<'tcx>(
pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
use std::fmt::Write;
- let trait_ref = tcx.impl_trait_ref(impl_def_id)?.subst_identity();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity();
let mut w = "impl".to_owned();
- let substs = InternalSubsts::identity_for_item(tcx, impl_def_id);
+ let args = GenericArgs::identity_for_item(tcx, impl_def_id);
// FIXME: Currently only handles ?Sized.
// Needs to support ?Move and ?DynSized when they are implemented.
let mut types_without_default_bounds = FxIndexSet::default();
let sized_trait = tcx.lang_items().sized_trait();
- if !substs.is_empty() {
- types_without_default_bounds.extend(substs.types());
+ if !args.is_empty() {
+ types_without_default_bounds.extend(args.types());
w.push('<');
w.push_str(
- &substs
+ &args
.iter()
.map(|k| k.to_string())
.filter(|k| k != "'_")
@@ -497,7 +490,7 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
w,
" {} for {}",
trait_ref.print_only_trait_path(),
- tcx.type_of(impl_def_id).subst_identity()
+ tcx.type_of(impl_def_id).instantiate_identity()
)
.unwrap();
@@ -507,22 +500,17 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
let mut pretty_predicates =
Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
- for (mut p, _) in predicates {
+ for (p, _) in predicates {
if let Some(poly_trait_ref) = p.as_trait_clause() {
if Some(poly_trait_ref.def_id()) == sized_trait {
types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder());
continue;
}
-
- if ty::BoundConstness::ConstIfConst == poly_trait_ref.skip_binder().constness {
- p = p.without_const(tcx);
- }
}
pretty_predicates.push(p.to_string());
}
- pretty_predicates
- .extend(types_without_default_bounds.iter().map(|ty| format!("{}: ?Sized", ty)));
+ pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized")));
if !pretty_predicates.is_empty() {
write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap();
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
index aa5c624f4..e9a592bde 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
@@ -180,7 +180,7 @@ impl<'tcx> ChildrenExt<'tcx> for Children {
if le && !ge {
debug!(
"descending as child of TraitRef {:?}",
- tcx.impl_trait_ref(possible_sibling).unwrap().subst_identity()
+ tcx.impl_trait_ref(possible_sibling).unwrap().instantiate_identity()
);
// The impl specializes `possible_sibling`.
@@ -188,7 +188,7 @@ impl<'tcx> ChildrenExt<'tcx> for Children {
} else if ge && !le {
debug!(
"placing as parent of TraitRef {:?}",
- tcx.impl_trait_ref(possible_sibling).unwrap().subst_identity()
+ tcx.impl_trait_ref(possible_sibling).unwrap().instantiate_identity()
);
replace_children.push(possible_sibling);
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index 420f8c5dc..0864e4dc8 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -62,8 +62,8 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> {
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!("Search visiting ty: {:?}", ty);
- let (adt_def, substs) = match *ty.kind() {
- ty::Adt(adt_def, substs) => (adt_def, substs),
+ let (adt_def, args) = match *ty.kind() {
+ ty::Adt(adt_def, args) => (adt_def, args),
ty::Param(_) => {
return ControlFlow::Break(ty);
}
@@ -157,15 +157,15 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> {
// instead looks directly at its fields outside
// this match), so we skip super_visit_with.
//
- // (Must not recur on substs for `PhantomData<T>` cf
+ // (Must not recur on args for `PhantomData<T>` cf
// rust-lang/rust#55028 and rust-lang/rust#55837; but also
- // want to skip substs when only uses of generic are
+ // want to skip args when only uses of generic are
// behind unsafe pointers `*const T`/`*mut T`.)
// even though we skip super_visit_with, we must recur on
// fields of ADT.
let tcx = self.tcx;
- adt_def.all_fields().map(|field| field.ty(tcx, substs)).try_for_each(|field_ty| {
+ adt_def.all_fields().map(|field| field.ty(tcx, args)).try_for_each(|field_ty| {
let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty);
debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty);
ty.visit_with(self)
diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
index 84746eba3..d3c4dc459 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
@@ -22,7 +22,9 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
assert!(!ty.is_ty_var(), "should have resolved vars before calling");
if self.infcx.next_trait_solver() {
- while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() {
+ while let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, projection_ty) =
+ *ty.kind()
+ {
let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::NormalizeProjectionType,
span: self.cause.span,
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 302b6cacf..a76272e9d 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Diagnostic;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::InferOk;
-use rustc_middle::ty::SubstsRef;
+use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
use smallvec::SmallVec;
@@ -50,7 +50,7 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> {
diag.span_label(self.top().1, top_label);
if self.path.len() > 1 {
for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) {
- diag.span_label(*sp, format!("referenced here ({})", use_desc));
+ diag.span_label(*sp, format!("referenced here ({use_desc})"));
}
}
if self.top().1 != self.bottom().1 {
@@ -58,7 +58,7 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> {
// redundant labels.
diag.span_label(
self.bottom().1,
- format!("trait alias used in trait object type ({})", use_desc),
+ format!("trait alias used in trait object type ({use_desc})"),
);
}
}
@@ -101,7 +101,7 @@ impl<'tcx> TraitAliasExpander<'tcx> {
fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool {
let tcx = self.tcx;
let trait_ref = item.trait_ref();
- let pred = trait_ref.without_const().to_predicate(tcx);
+ let pred = trait_ref.to_predicate(tcx);
debug!("expand_trait_aliases: trait_ref={:?}", trait_ref);
@@ -113,9 +113,13 @@ impl<'tcx> TraitAliasExpander<'tcx> {
// Don't recurse if this trait alias is already on the stack for the DFS search.
let anon_pred = anonymize_predicate(tcx, pred);
- if item.path.iter().rev().skip(1).any(|&(tr, _)| {
- anonymize_predicate(tcx, tr.without_const().to_predicate(tcx)) == anon_pred
- }) {
+ if item
+ .path
+ .iter()
+ .rev()
+ .skip(1)
+ .any(|&(tr, _)| anonymize_predicate(tcx, tr.to_predicate(tcx)) == anon_pred)
+ {
return false;
}
@@ -194,24 +198,24 @@ impl Iterator for SupertraitDefIds<'_> {
// Other
///////////////////////////////////////////////////////////////////////////
-/// Instantiate all bound parameters of the impl subject with the given substs,
+/// Instantiate all bound parameters of the impl subject with the given args,
/// returning the resulting subject and all obligations that arise.
/// The obligations are closed under normalization.
pub fn impl_subject_and_oblig<'a, 'tcx>(
selcx: &mut SelectionContext<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
impl_def_id: DefId,
- impl_substs: SubstsRef<'tcx>,
+ impl_args: GenericArgsRef<'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);
+ let subject = subject.instantiate(selcx.tcx(), impl_args);
let InferOk { value: subject, obligations: normalization_obligations1 } =
selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(subject);
let predicates = selcx.tcx().predicates_of(impl_def_id);
- let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
+ let predicates = predicates.instantiate(selcx.tcx(), impl_args);
let InferOk { value: predicates, obligations: normalization_obligations2 } =
selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates);
let impl_obligations = super::predicates_for_generics(cause, param_env, predicates);
@@ -241,9 +245,9 @@ pub fn upcast_choices<'tcx>(
/// Given an upcast trait object described by `object`, returns the
/// index of the method `method_def_id` (which should be part of
/// `object.upcast_trait_ref`) within the vtable for `object`.
-pub fn get_vtable_index_of_object_method<'tcx, N>(
+pub fn get_vtable_index_of_object_method<'tcx>(
tcx: TyCtxt<'tcx>,
- object: &super::ImplSourceObjectData<N>,
+ vtable_base: usize,
method_def_id: DefId,
) -> Option<usize> {
// Count number of methods preceding the one we are selecting and
@@ -252,7 +256,7 @@ pub fn get_vtable_index_of_object_method<'tcx, N>(
.iter()
.copied()
.position(|def_id| def_id == method_def_id)
- .map(|index| object.vtable_base + index)
+ .map(|index| vtable_base + index)
}
pub fn closure_trait_ref_and_return_type<'tcx>(
@@ -303,13 +307,13 @@ pub enum TupleArgumentsFlag {
No,
}
-// Verify that the trait item and its implementation have compatible substs lists
-pub fn check_substs_compatible<'tcx>(
+// Verify that the trait item and its implementation have compatible args lists
+pub fn check_args_compatible<'tcx>(
tcx: TyCtxt<'tcx>,
assoc_item: ty::AssocItem,
- substs: ty::SubstsRef<'tcx>,
+ args: ty::GenericArgsRef<'tcx>,
) -> bool {
- fn check_substs_compatible_inner<'tcx>(
+ fn check_args_compatible_inner<'tcx>(
tcx: TyCtxt<'tcx>,
generics: &'tcx ty::Generics,
args: &'tcx [ty::GenericArg<'tcx>],
@@ -322,7 +326,7 @@ pub fn check_substs_compatible<'tcx>(
if let Some(parent) = generics.parent
&& let parent_generics = tcx.generics_of(parent)
- && !check_substs_compatible_inner(tcx, parent_generics, parent_args) {
+ && !check_args_compatible_inner(tcx, parent_generics, parent_args) {
return false;
}
@@ -339,7 +343,7 @@ pub fn check_substs_compatible<'tcx>(
}
let generics = tcx.generics_of(assoc_item.def_id);
- // Chop off any additional substs (RPITIT) substs
- let substs = &substs[0..generics.count().min(substs.len())];
- check_substs_compatible_inner(tcx, generics, substs)
+ // Chop off any additional args (RPITIT) args
+ let args = &args[0..generics.count().min(args.len())];
+ check_args_compatible_inner(tcx, generics, args)
}
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index 1f83f1f44..427ac3684 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -5,8 +5,9 @@ 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::traits::BuiltinImplSource;
use rustc_middle::ty::visit::TypeVisitableExt;
-use rustc_middle::ty::InternalSubsts;
+use rustc_middle::ty::GenericArgs;
use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry};
use rustc_span::{sym, Span};
use smallvec::SmallVec;
@@ -24,8 +25,18 @@ pub enum VtblSegment<'tcx> {
pub fn prepare_vtable_segments<'tcx, T>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
- mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
+ segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
) -> Option<T> {
+ prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value()
+}
+
+/// Helper for [`prepare_vtable_segments`] that returns `ControlFlow`,
+/// such that we can use `?` in the body.
+fn prepare_vtable_segments_inner<'tcx, T>(
+ tcx: TyCtxt<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
+) -> ControlFlow<T> {
// The following constraints holds for the final arrangement.
// 1. The whole virtual table of the first direct super trait is included as the
// the prefix. If this trait doesn't have any super traits, then this step
@@ -71,20 +82,18 @@ pub fn prepare_vtable_segments<'tcx, T>(
// N, N-vptr, O
// emit dsa segment first.
- if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::MetadataDSA) {
- return Some(v);
- }
+ segment_visitor(VtblSegment::MetadataDSA)?;
let mut emit_vptr_on_new_entry = false;
let mut visited = PredicateSet::new(tcx);
- let predicate = trait_ref.without_const().to_predicate(tcx);
+ let predicate = trait_ref.to_predicate(tcx);
let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> =
- smallvec![(trait_ref, emit_vptr_on_new_entry, None)];
+ smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))];
visited.insert(predicate);
// the main traversal loop:
// basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes
- // that each node is emitted after all its descendents have been emitted.
+ // such that each node is emitted after all its descendants have been emitted.
// so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set.
// this is done on the fly.
// Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it
@@ -105,98 +114,107 @@ pub fn prepare_vtable_segments<'tcx, T>(
// Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node.
// Loop run #1: Stack after exiting out is []. Now the function exits.
- loop {
+ 'outer: loop {
// dive deeper into the stack, recording the path
'diving_in: loop {
- if let Some((inner_most_trait_ref, _, _)) = stack.last() {
- let inner_most_trait_ref = *inner_most_trait_ref;
- let mut direct_super_traits_iter = tcx
- .super_predicates_of(inner_most_trait_ref.def_id())
- .predicates
- .into_iter()
- .filter_map(move |(pred, _)| {
- pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause()
- });
+ let &(inner_most_trait_ref, _, _) = stack.last().unwrap();
+
+ let mut direct_super_traits_iter = tcx
+ .super_predicates_of(inner_most_trait_ref.def_id())
+ .predicates
+ .into_iter()
+ .filter_map(move |(pred, _)| {
+ pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause()
+ });
- 'diving_in_skip_visited_traits: loop {
- if let Some(next_super_trait) = direct_super_traits_iter.next() {
- if visited.insert(next_super_trait.to_predicate(tcx)) {
- // We're throwing away potential constness of super traits here.
- // FIXME: handle ~const super traits
- let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref);
- stack.push((
- next_super_trait,
- emit_vptr_on_new_entry,
- Some(direct_super_traits_iter),
- ));
- break 'diving_in_skip_visited_traits;
- } else {
- continue 'diving_in_skip_visited_traits;
- }
- } else {
- break 'diving_in;
- }
+ // Find an unvisited supertrait
+ match direct_super_traits_iter
+ .find(|&super_trait| visited.insert(super_trait.to_predicate(tcx)))
+ {
+ // Push it to the stack for the next iteration of 'diving_in to pick up
+ Some(unvisited_super_trait) => {
+ // We're throwing away potential constness of super traits here.
+ // FIXME: handle ~const super traits
+ let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref);
+ stack.push((
+ next_super_trait,
+ emit_vptr_on_new_entry,
+ maybe_iter(Some(direct_super_traits_iter)),
+ ))
}
+
+ // There are no more unvisited direct super traits, dive-in finished
+ None => break 'diving_in,
}
}
- // Other than the left-most path, vptr should be emitted for each trait.
- emit_vptr_on_new_entry = true;
-
// emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
- 'exiting_out: loop {
- if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() {
- if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries {
- trait_ref: *inner_most_trait_ref,
- emit_vptr: *emit_vptr,
- }) {
- return Some(v);
- }
+ while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
+ segment_visitor(VtblSegment::TraitOwnEntries {
+ trait_ref: inner_most_trait_ref,
+ emit_vptr,
+ })?;
+
+ // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable,
+ // we'll need to emit vptrs from now on.
+ if !emit_vptr_on_new_entry
+ && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id())
+ {
+ emit_vptr_on_new_entry = true;
+ }
- 'exiting_out_skip_visited_traits: loop {
- if let Some(siblings) = siblings_opt {
- if let Some(next_inner_most_trait_ref) = siblings.next() {
- if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) {
- // We're throwing away potential constness of super traits here.
- // FIXME: handle ~const super traits
- let next_inner_most_trait_ref =
- next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
- *inner_most_trait_ref = next_inner_most_trait_ref;
- *emit_vptr = emit_vptr_on_new_entry;
- break 'exiting_out;
- } else {
- continue 'exiting_out_skip_visited_traits;
- }
- }
- }
- stack.pop();
- continue 'exiting_out;
- }
+ if let Some(next_inner_most_trait_ref) =
+ siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx)))
+ {
+ // We're throwing away potential constness of super traits here.
+ // FIXME: handle ~const super traits
+ let next_inner_most_trait_ref =
+ next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
+
+ stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings));
+
+ // just pushed a new trait onto the stack, so we need to go through its super traits
+ continue 'outer;
}
- // all done
- return None;
}
+
+ // the stack is empty, all done
+ return ControlFlow::Continue(());
}
}
+/// Turns option of iterator into an iterator (this is just flatten)
+fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> {
+ // Flatten is bad perf-vise, we could probably implement a special case here that is better
+ i.into_iter().flatten()
+}
+
fn dump_vtable_entries<'tcx>(
tcx: TyCtxt<'tcx>,
sp: Span,
trait_ref: ty::PolyTraitRef<'tcx>,
entries: &[VtblEntry<'tcx>],
) {
- tcx.sess.emit_err(DumpVTableEntries {
- span: sp,
- trait_ref,
- entries: format!("{:#?}", entries),
- });
+ tcx.sess.emit_err(DumpVTableEntries { span: sp, trait_ref, entries: format!("{entries:#?}") });
+}
+
+fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
+ own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some()
}
fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[DefId] {
+ tcx.arena.alloc_from_iter(own_existential_vtable_entries_iter(tcx, trait_def_id))
+}
+
+fn own_existential_vtable_entries_iter(
+ tcx: TyCtxt<'_>,
+ trait_def_id: DefId,
+) -> impl Iterator<Item = DefId> + '_ {
let trait_methods = tcx
.associated_items(trait_def_id)
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Fn);
+
// Now list each method's DefId (for within its trait).
let own_entries = trait_methods.filter_map(move |&trait_method| {
debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
@@ -211,7 +229,7 @@ fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[Def
Some(def_id)
});
- tcx.arena.alloc_from_iter(own_entries.into_iter())
+ own_entries
}
/// Given a trait `trait_ref`, iterates the vtable entries
@@ -241,12 +259,12 @@ fn vtable_entries<'tcx>(
debug!("vtable_entries: trait_method={:?}", def_id);
// The method may have some early-bound lifetimes; add regions for those.
- let substs = trait_ref.map_bound(|trait_ref| {
- InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind {
+ let args = trait_ref.map_bound(|trait_ref| {
+ GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
GenericParamDefKind::Type { .. }
| GenericParamDefKind::Const { .. } => {
- trait_ref.substs[param.index as usize]
+ trait_ref.args[param.index as usize]
}
})
});
@@ -254,14 +272,14 @@ fn vtable_entries<'tcx>(
// The trait type may have higher-ranked lifetimes in it;
// erase them if they appear, so that we get the type
// at some particular call site.
- let substs = tcx
- .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), substs);
+ let args =
+ tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), args);
// It's possible that the method relies on where-clauses that
// do not hold for this particular set of type parameters.
// Note that this method could then never be called, so we
// do not want to try and codegen it, in that case (see #23435).
- let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
+ let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
if impossible_predicates(
tcx,
predicates.map(|(predicate, _)| predicate).collect(),
@@ -274,7 +292,7 @@ fn vtable_entries<'tcx>(
tcx,
ty::ParamEnv::reveal_all(),
def_id,
- substs,
+ args,
)
.expect("resolution failed during building vtable representation");
VtblEntry::Method(instance)
@@ -363,8 +381,8 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]);
match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
- Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => {
- implsrc_traitcasting.vtable_vptr_slot
+ Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => {
+ *vtable_vptr_slot
}
otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"),
}
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index d81722ce2..f26310665 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -2,8 +2,8 @@ use crate::infer::InferCtxt;
use crate::traits;
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_span::{Span, DUMMY_SP};
@@ -341,18 +341,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let trait_ref = &trait_pred.trait_ref;
// Negative trait predicates don't require supertraits to hold, just
- // that their substs are WF.
+ // that their args 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)
- } else {
- self.nominal_obligations(trait_ref.def_id, trait_ref.substs)
- };
+ let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.args);
debug!("compute_trait_pred obligations {:?}", obligations);
let param_env = self.param_env;
@@ -383,7 +379,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
self.out.extend(
trait_ref
- .substs
+ .args
.iter()
.enumerate()
.filter(|(_, arg)| {
@@ -416,7 +412,7 @@ impl<'a, 'tcx> WfPredicates<'a, '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 {
+ for arg in trait_ref.args {
self.compute(arg);
}
}
@@ -427,7 +423,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// A projection is well-formed if
//
// (a) its predicates hold (*)
- // (b) its substs are wf
+ // (b) its args are wf
//
// (*) The predicates of an associated type include the predicates of
// the trait that it's contained in. For example, given
@@ -445,18 +441,17 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// `i32: Clone`
// `i32: Copy`
// ]
- // Projection types do not require const predicates.
- let obligations = self.nominal_obligations_without_const(data.def_id, data.substs);
+ let obligations = self.nominal_obligations(data.def_id, data.args);
self.out.extend(obligations);
- self.compute_projection_substs(data.substs);
+ self.compute_projection_args(data.args);
}
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
+ // (b) its args are wf
//
// (*) The predicates of an inherent associated type include the
// predicates of the impl that it's contained in.
@@ -464,7 +459,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
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(
+ let args = traits::project::compute_inherent_assoc_ty_args(
&mut traits::SelectionContext::new(self.infcx),
self.param_env,
data,
@@ -472,23 +467,21 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
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);
+ let obligations = self.nominal_obligations(data.def_id, args);
self.out.extend(obligations);
}
- self.compute_projection_substs(data.substs);
+ self.compute_projection_args(data.args);
}
- fn compute_projection_substs(&mut self, substs: SubstsRef<'tcx>) {
+ fn compute_projection_args(&mut self, args: GenericArgsRef<'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(
- substs
- .iter()
+ args.iter()
.filter(|arg| {
matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
})
@@ -517,7 +510,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
self.recursion_depth,
self.param_env,
- ty::Binder::dummy(trait_ref).without_const(),
+ ty::Binder::dummy(trait_ref),
));
}
}
@@ -541,7 +534,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
match ct.kind() {
ty::ConstKind::Unevaluated(uv) => {
if !ct.has_escaping_bound_vars() {
- let obligations = self.nominal_obligations(uv.def, uv.substs);
+ let obligations = self.nominal_obligations(uv.def, uv.args);
self.out.extend(obligations);
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
@@ -571,7 +564,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
));
}
ty::ConstKind::Expr(_) => {
- // FIXME(generic_const_exprs): this doesnt verify that given `Expr(N + 1)` the
+ // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
// trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
// as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
// which means that the `DefId` would have been typeck'd elsewhere. However in
@@ -661,14 +654,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
self.compute_inherent_projection(data);
}
- ty::Adt(def, substs) => {
+ ty::Adt(def, args) => {
// WfNominalType
- let obligations = self.nominal_obligations(def.did(), substs);
+ let obligations = self.nominal_obligations(def.did(), args);
self.out.extend(obligations);
}
- ty::FnDef(did, substs) => {
- let obligations = self.nominal_obligations_without_const(did, substs);
+ ty::FnDef(did, args) => {
+ let obligations = self.nominal_obligations(did, args);
self.out.extend(obligations);
}
@@ -688,7 +681,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
}
}
- ty::Generator(did, substs, ..) => {
+ ty::Generator(did, args, ..) => {
// Walk ALL the types in the generator: this will
// include the upvar types as well as the yield
// type. Note that this is mildly distinct from
@@ -696,11 +689,11 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// about the signature of the closure. We don't
// have the problem of implied bounds here since
// generators don't take arguments.
- let obligations = self.nominal_obligations(did, substs);
+ let obligations = self.nominal_obligations(did, args);
self.out.extend(obligations);
}
- ty::Closure(did, substs) => {
+ ty::Closure(did, args) => {
// Only check the upvar types for WF, not the rest
// of the types within. This is needed because we
// capture the signature and it may not be WF
@@ -723,7 +716,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// fn(&'a T) }`, as discussed in #25860.
walker.skip_current_subtree(); // subtree handled below
// FIXME(eddyb) add the type to `walker` instead of recursing.
- self.compute(substs.as_closure().tupled_upvars_ty().into());
+ self.compute(args.as_closure().tupled_upvars_ty().into());
// Note that we cannot skip the generic types
// types. Normally, within the fn
// body where they are created, the generics will
@@ -739,7 +732,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// can cause compiler crashes when the user abuses unsafe
// code to procure such a closure.
// See tests/ui/type-alias-impl-trait/wf_check_closures.rs
- let obligations = self.nominal_obligations(did, substs);
+ let obligations = self.nominal_obligations(did, args);
self.out.extend(obligations);
}
@@ -748,18 +741,18 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// types appearing in the fn signature
}
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
// 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) {
- let obligations = self.nominal_obligations(def_id, substs);
+ let obligations = self.nominal_obligations(def_id, args);
self.out.extend(obligations);
}
}
- ty::Alias(ty::Weak, ty::AliasTy { def_id, substs, .. }) => {
- let obligations = self.nominal_obligations(def_id, substs);
+ ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => {
+ let obligations = self.nominal_obligations(def_id, args);
self.out.extend(obligations);
}
@@ -823,11 +816,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
}
#[instrument(level = "debug", skip(self))]
- fn nominal_obligations_inner(
+ fn nominal_obligations(
&mut self,
def_id: DefId,
- substs: SubstsRef<'tcx>,
- remap_constness: bool,
+ args: GenericArgsRef<'tcx>,
) -> Vec<traits::PredicateObligation<'tcx>> {
let predicates = self.tcx().predicates_of(def_id);
let mut origins = vec![def_id; predicates.predicates.len()];
@@ -837,21 +829,18 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
origins.extend(iter::repeat(parent).take(head.predicates.len()));
}
- let predicates = predicates.instantiate(self.tcx(), substs);
+ let predicates = predicates.instantiate(self.tcx(), args);
trace!("{:#?}", predicates);
debug_assert_eq!(predicates.predicates.len(), origins.len());
iter::zip(predicates, origins.into_iter().rev())
- .map(|((mut pred, span), origin_def_id)| {
+ .map(|((pred, span), origin_def_id)| {
let code = if span.is_dummy() {
traits::ItemObligation(origin_def_id)
} else {
traits::BindingObligation(origin_def_id, span)
};
let cause = self.cause(code);
- if remap_constness {
- pred = pred.without_const(self.tcx());
- }
traits::Obligation::with_depth(
self.tcx(),
cause,
@@ -864,22 +853,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
.collect()
}
- fn nominal_obligations(
- &mut self,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- ) -> Vec<traits::PredicateObligation<'tcx>> {
- self.nominal_obligations_inner(def_id, substs, false)
- }
-
- fn nominal_obligations_without_const(
- &mut self,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- ) -> Vec<traits::PredicateObligation<'tcx>> {
- self.nominal_obligations_inner(def_id, substs, true)
- }
-
fn from_object_ty(
&mut self,
ty: Ty<'tcx>,