diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:24 +0000 |
commit | 023939b627b7dc93b01471f7d41fb8553ddb4ffa (patch) | |
tree | 60fc59477c605c72b0a1051409062ddecc43f877 /compiler/rustc_trait_selection | |
parent | Adding debian version 1.72.1+dfsg1-1. (diff) | |
download | rustc-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')
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(¤t_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>, |