diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
commit | 9918693037dce8aa4bb6f08741b6812923486c18 (patch) | |
tree | 21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /compiler/rustc_trait_selection/src | |
parent | Releasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff) | |
download | rustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip |
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_trait_selection/src')
58 files changed, 2512 insertions, 1844 deletions
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index c1fb287d6..b0ec8b3a4 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,6 +1,6 @@ use crate::fluent_generated as fluent; use rustc_errors::{ - AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic, + AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, ErrorGuaranteed, IntoDiagnostic, SubdiagnosticMessage, }; use rustc_macros::Diagnostic; @@ -61,9 +61,9 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> { #[track_caller] fn into_diagnostic( self, - handler: &Handler, + dcx: &DiagCtxt, ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { - let mut diag = handler.struct_err(fluent::trait_selection_negative_positive_conflict); + let mut diag = dcx.struct_err(fluent::trait_selection_negative_positive_conflict); diag.set_arg("trait_desc", self.trait_desc.print_only_trait_path().to_string()); diag.set_arg( "self_desc", diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 38153cccf..251f0628a 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,8 +1,10 @@ +use crate::solve::FulfillmentCtxt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::{self, DefiningAnchor, ObligationCtxt}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_infer::traits::{TraitEngine, TraitEngineExt}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; use rustc_middle::traits::query::NoSolution; @@ -27,7 +29,7 @@ pub trait InferCtxtExt<'tcx> { /// - the parameter environment /// /// Invokes `evaluate_obligation`, so in the event that evaluating - /// `Ty: Trait` causes overflow, EvaluatedToRecur (or EvaluatedToUnknown) + /// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent (or EvaluatedToAmbigStackDependent) /// will be returned. fn type_implements_trait( &self, @@ -35,6 +37,13 @@ pub trait InferCtxtExt<'tcx> { params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>, param_env: ty::ParamEnv<'tcx>, ) -> traits::EvaluationResult; + + fn could_impl_trait( + &self, + trait_def_id: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Option<Vec<traits::FulfillmentError<'tcx>>>; } impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { @@ -76,6 +85,68 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { }; self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr) } + + fn could_impl_trait( + &self, + trait_def_id: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Option<Vec<traits::FulfillmentError<'tcx>>> { + self.probe(|_snapshot| { + if let ty::Adt(def, args) = ty.kind() + && let Some((impl_def_id, _)) = self + .tcx + .all_impls(trait_def_id) + .filter_map(|impl_def_id| { + self.tcx.impl_trait_ref(impl_def_id).map(|r| (impl_def_id, r)) + }) + .map(|(impl_def_id, imp)| (impl_def_id, imp.skip_binder())) + .find(|(_, imp)| match imp.self_ty().peel_refs().kind() { + ty::Adt(i_def, _) if i_def.did() == def.did() => true, + _ => false, + }) + { + let mut fulfill_cx = FulfillmentCtxt::new(self); + // We get all obligations from the impl to talk about specific + // trait bounds. + let obligations = self + .tcx + .predicates_of(impl_def_id) + .instantiate(self.tcx, args) + .into_iter() + .map(|(clause, span)| { + traits::Obligation::new( + self.tcx, + traits::ObligationCause::dummy_with_span(span), + param_env, + clause, + ) + }) + .collect::<Vec<_>>(); + fulfill_cx.register_predicate_obligations(self, obligations); + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty]); + let obligation = traits::Obligation::new( + self.tcx, + traits::ObligationCause::dummy(), + param_env, + trait_ref, + ); + fulfill_cx.register_predicate_obligation(self, obligation); + let mut errors = fulfill_cx.select_all_or_error(self); + // We remove the last predicate failure, which corresponds to + // the top-level obligation, because most of the type we only + // care about the other ones, *except* when it is the only one. + // This seems to only be relevant for arbitrary self-types. + // Look at `tests/ui/moves/move-fn-self-receiver.rs`. + if errors.len() > 1 { + errors.truncate(errors.len() - 1); + } + Some(errors) + } else { + None + } + }) + } } pub trait InferCtxtBuilderExt<'tcx> { diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 5ba29f878..de2577cca 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -11,9 +11,9 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(control_flow_enum)] @@ -21,7 +21,6 @@ #![feature(let_chains)] #![feature(if_let_guard)] #![feature(never_type)] -#![feature(result_option_inspect)] #![feature(type_alias_impl_trait)] #![feature(min_specialization)] #![recursion_limit = "512"] // For rustdoc @@ -38,12 +37,9 @@ extern crate rustc_middle; #[macro_use] extern crate smallvec; -use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; -use rustc_fluent_macro::fluent_messages; - pub mod errors; pub mod infer; pub mod solve; pub mod traits; -fluent_messages! { "../messages.ftl" } +rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index f7031c5f4..626569fb4 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -11,18 +11,12 @@ //! * 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 super::{EvalCtxt, GoalSource}; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty; -/// We may need to invert the alias relation direction if dealing an alias on the RHS. -#[derive(Debug)] -enum Invert { - No, - Yes, -} - impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] pub(super) fn compute_alias_relate_goal( @@ -31,187 +25,128 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> QueryResult<'tcx> { let tcx = self.tcx(); let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; - if lhs.is_infer() || rhs.is_infer() { - bug!( - "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated" - ); - } - - match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { - (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), - // RHS is not a projection, only way this is true is if LHS normalizes-to RHS - (Some(alias_lhs), None) => self.assemble_normalizes_to_candidate( - param_env, - alias_lhs, - rhs, - direction, - Invert::No, - ), + let Some(lhs) = self.try_normalize_term(param_env, lhs)? else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; - // LHS is not a projection, only way this is true is if RHS normalizes-to LHS - (None, Some(alias_rhs)) => self.assemble_normalizes_to_candidate( - param_env, - alias_rhs, - lhs, - direction, - Invert::Yes, - ), + let Some(rhs) = self.try_normalize_term(param_env, rhs)? else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; - (Some(alias_lhs), Some(alias_rhs)) => { - debug!("both sides are aliases"); + let variance = match direction { + ty::AliasRelationDirection::Equate => ty::Variance::Invariant, + ty::AliasRelationDirection::Subtype => ty::Variance::Covariant, + }; - let mut candidates = Vec::new(); - // LHS normalizes-to RHS - candidates.extend(self.assemble_normalizes_to_candidate( - param_env, - alias_lhs, - rhs, - direction, - Invert::No, - )); - // RHS normalizes-to RHS - candidates.extend(self.assemble_normalizes_to_candidate( - param_env, - alias_rhs, - lhs, - direction, - Invert::Yes, - )); - // Relate via args - candidates.extend( - self.assemble_subst_relate_candidate( - param_env, alias_lhs, alias_rhs, direction, - ), - ); - debug!(?candidates); + match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { + (None, None) => { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } - if let Some(merged) = self.try_merge_responses(&candidates) { - Ok(merged) + (Some(alias), None) => { + if rhs.is_infer() { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else if alias.is_opaque(tcx) { + self.define_opaque(param_env, alias, rhs) } else { - // 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(bidirectional_normalizes_to_response) = self - .assemble_bidirectional_normalizes_to_candidate( - param_env, lhs, rhs, direction, - ) - { - Ok(bidirectional_normalizes_to_response) - } else { - self.flounder(&candidates) - } - } - SolverMode::Coherence => self.flounder(&candidates), - } + Err(NoSolution) + } + } + (None, Some(alias)) => { + if lhs.is_infer() { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else if alias.is_opaque(tcx) { + self.define_opaque(param_env, alias, lhs) + } else { + Err(NoSolution) } } - } - } - #[instrument(level = "debug", skip(self), ret)] - fn assemble_normalizes_to_candidate( - &mut self, - param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTy<'tcx>, - other: ty::Term<'tcx>, - direction: ty::AliasRelationDirection, - invert: Invert, - ) -> QueryResult<'tcx> { - self.probe_misc_candidate("normalizes-to").enter(|ecx| { - ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) + (Some(alias_lhs), Some(alias_rhs)) => { + self.relate_rigid_alias_or_opaque(param_env, alias_lhs, variance, alias_rhs) + } + } } - // 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( + /// Normalize the `term` to equate it later. This does not define opaque types. + #[instrument(level = "debug", skip(self, param_env), ret)] + fn try_normalize_term( &mut self, param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTy<'tcx>, - other: ty::Term<'tcx>, - direction: ty::AliasRelationDirection, - invert: Invert, - ) -> Result<(), NoSolution> { - let other = match direction { - // This is purely an optimization. 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 { - Invert::No => (fresh, other), - Invert::Yes => (other, fresh), - }; - self.sub(param_env, sub, sup)?; - fresh + term: ty::Term<'tcx>, + ) -> Result<Option<ty::Term<'tcx>>, NoSolution> { + match term.unpack() { + ty::TermKind::Ty(ty) => { + // We do no define opaque types here but instead do so in `relate_rigid_alias_or_opaque`. + Ok(self + .try_normalize_ty_recur(param_env, DefineOpaqueTypes::No, 0, ty) + .map(Into::into)) } - }; - self.add_goal(Goal::new( - self.tcx(), - param_env, - ty::ProjectionPredicate { projection_ty: alias, term: other }, - )); - - Ok(()) + ty::TermKind::Const(_) => { + if let Some(alias) = term.to_alias_ty(self.tcx()) { + let term = self.next_term_infer_of_kind(term); + self.add_goal( + GoalSource::Misc, + Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }), + ); + self.try_evaluate_added_goals()?; + Ok(Some(self.resolve_vars_if_possible(term))) + } else { + Ok(Some(term)) + } + } + } } - fn assemble_subst_relate_candidate( + fn define_opaque( &mut self, param_env: ty::ParamEnv<'tcx>, - alias_lhs: ty::AliasTy<'tcx>, - alias_rhs: ty::AliasTy<'tcx>, - direction: ty::AliasRelationDirection, + opaque: ty::AliasTy<'tcx>, + term: ty::Term<'tcx>, ) -> QueryResult<'tcx> { - self.probe_misc_candidate("args relate").enter(|ecx| { - match direction { - ty::AliasRelationDirection::Equate => { - ecx.eq(param_env, alias_lhs, alias_rhs)?; - } - ty::AliasRelationDirection::Subtype => { - ecx.sub(param_env, alias_lhs, alias_rhs)?; - } - } - - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) + self.add_goal( + GoalSource::Misc, + Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }), + ); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - fn assemble_bidirectional_normalizes_to_candidate( + fn relate_rigid_alias_or_opaque( &mut self, param_env: ty::ParamEnv<'tcx>, - lhs: ty::Term<'tcx>, - rhs: ty::Term<'tcx>, - direction: ty::AliasRelationDirection, + lhs: ty::AliasTy<'tcx>, + variance: ty::Variance, + rhs: ty::AliasTy<'tcx>, ) -> QueryResult<'tcx> { - self.probe_misc_candidate("bidir normalizes-to").enter(|ecx| { - ecx.normalizes_to_inner( - param_env, - lhs.to_alias_ty(ecx.tcx()).unwrap(), - rhs, - direction, - Invert::No, - )?; - ecx.normalizes_to_inner( - param_env, - rhs.to_alias_ty(ecx.tcx()).unwrap(), - lhs, - direction, - Invert::Yes, - )?; + let tcx = self.tcx(); + let mut candidates = vec![]; + if lhs.is_opaque(tcx) { + candidates.extend( + self.probe_misc_candidate("define-lhs-opaque") + .enter(|ecx| ecx.define_opaque(param_env, lhs, rhs.to_ty(tcx).into())), + ); + } + + if rhs.is_opaque(tcx) { + candidates.extend( + self.probe_misc_candidate("define-rhs-opaque") + .enter(|ecx| ecx.define_opaque(param_env, rhs, lhs.to_ty(tcx).into())), + ); + } + + candidates.extend(self.probe_misc_candidate("args-relate").enter(|ecx| { + ecx.relate(param_env, lhs, variance, rhs)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) + })); + + if let Some(result) = self.try_merge_responses(&candidates) { + Ok(result) + } else { + self.flounder(&candidates) + } } } diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 27d2bdead..81a766f24 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -1,6 +1,7 @@ //! Code shared by trait and projection goals for candidate assembly. use super::{EvalCtxt, SolverMode}; +use crate::solve::GoalSource; use crate::traits::coherence; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; @@ -37,8 +38,6 @@ pub(super) trait GoalKind<'tcx>: fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>; - fn polarity(self) -> ty::ImplPolarity; - fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; @@ -64,7 +63,9 @@ pub(super) trait GoalKind<'tcx>: requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, ) -> QueryResult<'tcx> { Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { - ecx.add_goals(requirements); + // FIXME(-Znext-solver=coinductive): check whether this should be + // `GoalSource::ImplWhereBound` for any caller. + ecx.add_goals(GoalSource::Misc, requirements); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -96,12 +97,16 @@ pub(super) trait GoalKind<'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, - )); + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goals( + GoalSource::Misc, + 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) }) } @@ -110,7 +115,7 @@ pub(super) trait GoalKind<'tcx>: ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, impl_def_id: DefId, - ) -> QueryResult<'tcx>; + ) -> Result<Candidate<'tcx>, NoSolution>; /// 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 @@ -209,6 +214,11 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + fn consider_builtin_async_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + /// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to /// implement `Coroutine<R, Yield = Y, Return = O>`, given the resume, yield, /// and return types of the coroutine computed during type-checking. @@ -263,7 +273,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> 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; + return vec![ambig]; } let mut candidates = self.assemble_candidates_via_self_ty(goal, 0); @@ -288,15 +298,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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(), - }] - }) + ) -> Option<Candidate<'tcx>> { + if goal.predicate.self_ty().is_ty_var() { + debug!("adding self_ty_infer_ambiguity_response"); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + let result = self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + .unwrap(); + let mut dummy_probe = self.inspect.new_probe(); + dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) }); + self.inspect.finish_probe(dummy_probe); + Some(Candidate { source, result }) + } else { + None + } } /// Assemble candidates which apply to the self type. This only looks at candidate which @@ -310,7 +325,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> 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; + return vec![ambig]; } let mut candidates = Vec::new(); @@ -349,16 +364,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { num_steps: usize, ) { let tcx = self.tcx(); - let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return }; + let &ty::Alias(_, alias) = goal.predicate.self_ty().kind() else { return }; candidates.extend(self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { - if num_steps < ecx.local_overflow_limit() { + if tcx.recursion_limit().value_within_limit(num_steps) { 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); + let normalizes_to_goal = + goal.with(tcx, ty::NormalizesTo { alias, term: normalized_ty.into() }); + ecx.add_goal(GoalSource::Misc, normalizes_to_goal); if let Err(NoSolution) = ecx.try_evaluate_added_goals() { debug!("self type normalization failed"); return vec![]; @@ -395,8 +408,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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 }), + Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } } @@ -488,6 +500,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + #[instrument(level = "debug", skip_all)] fn assemble_unsize_to_dyn_candidate<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, @@ -505,6 +518,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + #[instrument(level = "debug", skip_all)] fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, @@ -514,8 +528,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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 }), + Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } } @@ -564,6 +577,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_future_candidate(self, goal) } else if lang_items.iterator_trait() == Some(trait_def_id) { G::consider_builtin_iterator_candidate(self, goal) + } else if lang_items.async_iterator_trait() == Some(trait_def_id) { + G::consider_builtin_async_iterator_candidate(self, goal) } else if lang_items.coroutine_trait() == Some(trait_def_id) { G::consider_builtin_coroutine_candidate(self, goal) } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { @@ -864,23 +879,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| { let trait_ref = goal.predicate.trait_ref(tcx); - #[derive(Debug)] - enum FailureKind { - Overflow, - NoSolution(NoSolution), - } + struct Overflow; 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)), + Some(ty) => Ok(ty), + None => Err(Overflow), }; match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) { - Err(FailureKind::Overflow) => { + Err(Overflow) => { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) } - Err(FailureKind::NoSolution(NoSolution)) | Ok(Ok(())) => Err(NoSolution), + Ok(Ok(())) => Err(NoSolution), Ok(Err(_)) => { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } 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 839968b25..f442e2a08 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -50,14 +50,14 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]), - ty::Tuple(ref tys) => { + ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet Ok(tys.iter().collect()) } - ty::Closure(_, ref args) => Ok(vec![args.as_closure().tupled_upvars_ty()]), + ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]), - ty::Coroutine(_, ref args, _) => { + ty::Coroutine(_, args, _) => { let coroutine_args = args.as_coroutine(); Ok(vec![coroutine_args.tupled_upvars_ty(), coroutine_args.witness()]) } @@ -91,13 +91,13 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> ty::Binder<'tcx, Ty<'tcx>> { - debug_assert!(!ty.has_late_bound_regions()); + debug_assert!(!ty.has_bound_regions()); let mut counter = 0; let ty = tcx.fold_regions(ty, |r, current_depth| match r.kind() { ty::ReErased => { let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon }; counter += 1; - ty::Region::new_late_bound(tcx, current_depth, br) + ty::Region::new_bound(tcx, current_depth, br) } // All free regions should be erased here. r => bug!("unexpected region: {r:?}"), diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs deleted file mode 100644 index 377ae1b4e..000000000 --- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs +++ /dev/null @@ -1,428 +0,0 @@ -use std::cmp::Ordering; - -use crate::infer::InferCtxt; -use rustc_middle::infer::canonical::Canonical; -use rustc_middle::infer::canonical::CanonicalTyVarKind; -use rustc_middle::infer::canonical::CanonicalVarInfo; -use rustc_middle::infer::canonical::CanonicalVarInfos; -use rustc_middle::infer::canonical::CanonicalVarKind; -use rustc_middle::ty::BoundRegionKind::BrAnon; -use rustc_middle::ty::BoundTyKind; -use rustc_middle::ty::TyCtxt; -use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, Ty}; -use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; - -/// Whether we're canonicalizing a query input or the query response. -/// -/// When canonicalizing an input we're in the context of the caller -/// while canonicalizing the response happens in the context of the -/// query. -#[derive(Debug, Clone, Copy)] -pub enum CanonicalizeMode { - Input, - /// FIXME: We currently return region constraints referring to - /// placeholders and inference variables from a binder instantiated - /// inside of the query. - /// - /// In the long term we should eagerly deal with these constraints - /// inside of the query and only propagate constraints which are - /// actually nameable by the caller. - Response { - /// The highest universe nameable by the caller. - /// - /// All variables in a universe nameable by the caller get mapped - /// to the root universe in the response and then mapped back to - /// their correct universe when applying the query response in the - /// context of the caller. - /// - /// This doesn't work for universes created inside of the query so - /// we do remember their universe in the response. - max_input_universe: ty::UniverseIndex, - }, -} - -pub struct Canonicalizer<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, - canonicalize_mode: CanonicalizeMode, - - variables: &'a mut Vec<ty::GenericArg<'tcx>>, - primitive_var_infos: Vec<CanonicalVarInfo<'tcx>>, - binder_index: ty::DebruijnIndex, -} - -impl<'a, 'tcx> Canonicalizer<'a, 'tcx> { - #[instrument(level = "debug", skip(infcx), ret)] - pub fn canonicalize<T: TypeFoldable<TyCtxt<'tcx>>>( - infcx: &'a InferCtxt<'tcx>, - canonicalize_mode: CanonicalizeMode, - variables: &'a mut Vec<ty::GenericArg<'tcx>>, - value: T, - ) -> Canonical<'tcx, T> { - let mut canonicalizer = Canonicalizer { - infcx, - canonicalize_mode, - - variables, - primitive_var_infos: Vec::new(), - binder_index: ty::INNERMOST, - }; - - let value = value.fold_with(&mut canonicalizer); - assert!(!value.has_infer()); - assert!(!value.has_placeholders()); - - let (max_universe, variables) = canonicalizer.finalize(); - - Canonical { max_universe, variables, value } - } - - fn finalize(self) -> (ty::UniverseIndex, CanonicalVarInfos<'tcx>) { - let mut var_infos = self.primitive_var_infos; - // See the rustc-dev-guide section about how we deal with universes - // during canonicalization in the new solver. - match self.canonicalize_mode { - // We try to deduplicate as many query calls as possible and hide - // all information which should not matter for the solver. - // - // For this we compress universes as much as possible. - CanonicalizeMode::Input => {} - // When canonicalizing a response we map a universes already entered - // by the caller to the root universe and only return useful universe - // information for placeholders and inference variables created inside - // of the query. - CanonicalizeMode::Response { max_input_universe } => { - for var in var_infos.iter_mut() { - let uv = var.universe(); - let new_uv = ty::UniverseIndex::from( - uv.index().saturating_sub(max_input_universe.index()), - ); - *var = var.with_updated_universe(new_uv); - } - let max_universe = var_infos - .iter() - .map(|info| info.universe()) - .max() - .unwrap_or(ty::UniverseIndex::ROOT); - - let var_infos = self.infcx.tcx.mk_canonical_var_infos(&var_infos); - return (max_universe, var_infos); - } - } - - // Given a `var_infos` with existentials `En` and universals `Un` in - // universes `n`, this algorithm compresses them in place so that: - // - // - the new universe indices are as small as possible - // - we only create a new universe if we would otherwise put a placeholder in - // the same compressed universe as an existential which cannot name it - // - // Let's walk through an example: - // - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0 - // - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1 - // - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2 - // - var_infos: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5 - // - var_infos: [E0, U1, E1, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 6 - // - var_infos: [E0, U1, E1, U1, E1, E2, U2], curr_compressed_uv: 2, next_orig_uv: - - // - // This algorithm runs in `O(n²)` where `n` is the number of different universe - // indices in the input. This should be fine as `n` is expected to be small. - let mut curr_compressed_uv = ty::UniverseIndex::ROOT; - let mut existential_in_new_uv = false; - let mut next_orig_uv = Some(ty::UniverseIndex::ROOT); - while let Some(orig_uv) = next_orig_uv.take() { - let mut update_uv = |var: &mut CanonicalVarInfo<'tcx>, orig_uv, is_existential| { - let uv = var.universe(); - match uv.cmp(&orig_uv) { - Ordering::Less => (), // Already updated - Ordering::Equal => { - if is_existential { - existential_in_new_uv = true; - } else if existential_in_new_uv { - // `var` is a placeholder from a universe which is not nameable - // by an existential which we already put into the compressed - // universe `curr_compressed_uv`. We therefore have to create a - // new universe for `var`. - curr_compressed_uv = curr_compressed_uv.next_universe(); - existential_in_new_uv = false; - } - - *var = var.with_updated_universe(curr_compressed_uv); - } - Ordering::Greater => { - // We can ignore this variable in this iteration. We only look at - // universes which actually occur in the input for performance. - // - // For this we set `next_orig_uv` to the next smallest, not yet compressed, - // universe of the input. - if next_orig_uv.map_or(true, |curr_next_uv| uv.cannot_name(curr_next_uv)) { - next_orig_uv = Some(uv); - } - } - } - }; - - // For each universe which occurs in the input, we first iterate over all - // placeholders and then over all inference variables. - // - // Whenever we compress the universe of a placeholder, no existential with - // an already compressed universe can name that placeholder. - for is_existential in [false, true] { - for var in var_infos.iter_mut() { - // We simply put all regions from the input into the highest - // compressed universe, so we only deal with them at the end. - if !var.is_region() { - if is_existential == var.is_existential() { - update_uv(var, orig_uv, is_existential) - } - } - } - } - } - - for var in var_infos.iter_mut() { - if var.is_region() { - assert!(var.is_existential()); - *var = var.with_updated_universe(curr_compressed_uv); - } - } - - let var_infos = self.infcx.tcx.mk_canonical_var_infos(&var_infos); - (curr_compressed_uv, var_infos) - } -} - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> - where - T: TypeFoldable<TyCtxt<'tcx>>, - { - self.binder_index.shift_in(1); - let t = t.super_fold_with(self); - self.binder_index.shift_out(1); - t - } - - 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 { - ty::ReLateBound(..) => return r, - - // We may encounter `ReStatic` in item signatures or the hidden type - // of an opaque. `ReErased` should only be encountered in the hidden - // type of an opaque for regions that are ignored for the purposes of - // captures. - // - // FIXME: We should investigate the perf implications of not uniquifying - // `ReErased`. We may be able to short-circuit registering region - // obligations if we encounter a `ReErased` on one side, for example. - ty::ReStatic | ty::ReErased => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { .. } => return r, - }, - - ty::ReFree(_) | ty::ReEarlyBound(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { .. } => bug!("unexpected region in response: {r:?}"), - }, - - ty::RePlaceholder(placeholder) => match self.canonicalize_mode { - // We canonicalize placeholder regions as existentials in query inputs. - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { max_input_universe } => { - // If we have a placeholder region inside of a query, it must be from - // a new universe. - if max_input_universe.can_name(placeholder.universe) { - bug!("new placeholder in universe {max_input_universe:?}: {r:?}"); - } - CanonicalVarKind::PlaceholderRegion(placeholder) - } - }, - - ty::ReVar(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { .. } => { - CanonicalVarKind::Region(self.infcx.universe_of_region(r)) - } - }, - - ty::ReError(_) => return r, - }; - - let existing_bound_var = match self.canonicalize_mode { - CanonicalizeMode::Input => None, - CanonicalizeMode::Response { .. } => { - self.variables.iter().position(|&v| v == r.into()).map(ty::BoundVar::from) - } - }; - let var = existing_bound_var.unwrap_or_else(|| { - let var = ty::BoundVar::from(self.variables.len()); - self.variables.push(r.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }); - let br = ty::BoundRegion { var, kind: BrAnon }; - ty::Region::new_late_bound(self.interner(), self.binder_index, br) - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - let kind = match *t.kind() { - 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)) => { - assert_eq!(self.infcx.opportunistic_resolve_int_var(vid), t); - CanonicalVarKind::Ty(CanonicalTyVarKind::Int) - } - ty::Infer(ty::FloatVar(vid)) => { - 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:?}") - } - ty::Placeholder(placeholder) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::PlaceholderTy(ty::Placeholder { - universe: placeholder.universe, - bound: ty::BoundTy { - var: ty::BoundVar::from_usize(self.variables.len()), - kind: ty::BoundTyKind::Anon, - }, - }), - CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder), - }, - ty::Param(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::PlaceholderTy(ty::Placeholder { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundTy { - var: ty::BoundVar::from_usize(self.variables.len()), - kind: ty::BoundTyKind::Anon, - }, - }), - CanonicalizeMode::Response { .. } => bug!("param ty in response: {t:?}"), - }, - 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::Coroutine(_, _, _) - | ty::CoroutineWitness(..) - | ty::Never - | ty::Tuple(_) - | ty::Alias(_, _) - | ty::Bound(_, _) - | ty::Error(_) => return t.super_fold_with(self), - }; - - let var = ty::BoundVar::from( - self.variables.iter().position(|&v| v == t.into()).unwrap_or_else(|| { - let var = self.variables.len(); - self.variables.push(t.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }), - ); - let bt = ty::BoundTy { var, kind: BoundTyKind::Anon }; - Ty::new_bound(self.infcx.tcx, self.binder_index, bt) - } - - fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { - let kind = match c.kind() { - 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::EffectVar(vid)) => { - assert_eq!( - self.infcx.root_effect_var(vid), - vid, - "effect var should have been resolved" - ); - let None = self.infcx.probe_effect_var(vid) else { - bug!("effect var should have been resolved"); - }; - CanonicalVarKind::Effect - } - ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => { - bug!("fresh var during canonicalization: {c:?}") - } - ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( - ty::Placeholder { - universe: placeholder.universe, - bound: ty::BoundVar::from(self.variables.len()), - }, - c.ty(), - ), - CanonicalizeMode::Response { .. } => { - CanonicalVarKind::PlaceholderConst(placeholder, c.ty()) - } - }, - ty::ConstKind::Param(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( - ty::Placeholder { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundVar::from(self.variables.len()), - }, - c.ty(), - ), - CanonicalizeMode::Response { .. } => bug!("param ty in response: {c:?}"), - }, - ty::ConstKind::Bound(_, _) - | ty::ConstKind::Unevaluated(_) - | ty::ConstKind::Value(_) - | ty::ConstKind::Error(_) - | ty::ConstKind::Expr(_) => return c.super_fold_with(self), - }; - - let var = ty::BoundVar::from( - self.variables.iter().position(|&v| v == c.into()).unwrap_or_else(|| { - let var = self.variables.len(); - self.variables.push(c.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }), - ); - ty::Const::new_bound(self.infcx.tcx, self.binder_index, var, c.ty()) - } -} 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 b3f9218d7..ecdae2521 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -9,7 +9,6 @@ //! //! [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::{ inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response, }; @@ -18,6 +17,7 @@ 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::resolve::EagerResolver; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_middle::infer::canonical::Canonical; use rustc_middle::traits::query::NoSolution; @@ -25,10 +25,8 @@ use rustc_middle::traits::solve::{ ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, }; use rustc_middle::traits::ObligationCause; -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_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer}; use rustc_span::DUMMY_SP; use std::iter; use std::ops::Deref; @@ -58,7 +56,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> (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 }); + (goal, opaque_types).fold_with(&mut EagerResolver::new(self.infcx)); let mut orig_values = Default::default(); let canonical_goal = Canonicalizer::canonicalize( @@ -96,26 +94,12 @@ 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 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 }); + (var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx)); // Remove any trivial region constraints once we've resolved regions external_constraints .region_constraints @@ -262,7 +246,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } GenericArgKind::Lifetime(r) => { - if let ty::ReLateBound(debruijn, br) = *r { + if let ty::ReBound(debruijn, br) = *r { assert_eq!(debruijn, ty::INNERMOST); opt_values[br.var] = Some(*original_value); } @@ -364,86 +348,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } -/// 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()) - } - } - } - ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => { - debug_assert_eq!(c.ty(), self.infcx.tcx.types.bool); - match self.infcx.probe_effect_var(vid) { - Some(c) => c.as_const(self.infcx.tcx), - None => ty::Const::new_infer( - self.infcx.tcx, - ty::InferConst::EffectVar(self.infcx.root_effect_var(vid)), - self.infcx.tcx.types.bool, - ), - } - } - _ => { - if c.has_infer() { - c.super_fold_with(self) - } else { - c - } - } - } - } -} - impl<'tcx> inspect::ProofTreeBuilder<'tcx> { pub fn make_canonical_state<T: TypeFoldable<TyCtxt<'tcx>>>( ecx: &EvalCtxt<'_, 'tcx>, data: T, ) -> inspect::CanonicalState<'tcx, T> { let state = inspect::State { var_values: ecx.var_values, data }; - let state = state.fold_with(&mut EagerResolver { infcx: ecx.infcx }); + let state = state.fold_with(&mut EagerResolver::new(ecx.infcx)); Canonicalizer::canonicalize( ecx.infcx, CanonicalizeMode::Response { max_input_universe: ecx.max_input_universe }, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs new file mode 100644 index 000000000..67b680105 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs @@ -0,0 +1,45 @@ +use super::{EvalCtxt, NestedGoals}; +use crate::solve::inspect; +use rustc_middle::traits::query::NoSolution; + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + pub(in crate::solve) fn commit_if_ok<T>( + &mut self, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result<T, NoSolution>, + ) -> Result<T, NoSolution> { + let mut nested_ecx = EvalCtxt { + infcx: self.infcx, + variables: self.variables, + var_values: self.var_values, + predefined_opaques_in_body: self.predefined_opaques_in_body, + max_input_universe: self.max_input_universe, + search_graph: self.search_graph, + nested_goals: NestedGoals::new(), + tainted: self.tainted, + inspect: self.inspect.new_probe(), + }; + + let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx)); + if result.is_ok() { + let EvalCtxt { + infcx: _, + variables: _, + var_values: _, + predefined_opaques_in_body: _, + max_input_universe: _, + search_graph: _, + nested_goals, + tainted, + inspect, + } = nested_ecx; + self.nested_goals.extend(nested_goals); + self.tainted = tainted; + self.inspect.integrate_snapshot(inspect); + } else { + nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk); + self.inspect.finish_probe(nested_ecx.inspect); + } + + result + } +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index 70235b710..76c50a111 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -4,7 +4,7 @@ use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt, + BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt, }; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; @@ -23,17 +23,19 @@ use rustc_middle::ty::{ use rustc_session::config::DumpSolverProofTree; use rustc_span::DUMMY_SP; use std::io::Write; +use std::iter; use std::ops::ControlFlow; use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use super::inspect::ProofTreeBuilder; -use super::SolverMode; use super::{search_graph, GoalEvaluationKind}; use super::{search_graph::SearchGraph, Goal}; +use super::{GoalSource, SolverMode}; pub use select::InferCtxtSelectExt; mod canonical; +mod commit_if_ok; mod probe; mod select; @@ -102,12 +104,12 @@ pub(super) struct NestedGoals<'tcx> { /// with a fresh inference variable when we evaluate this goal. That can result /// in a trait solver cycle. This would currently result in overflow but can be /// can be unsound with more powerful coinduction in the future. - pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::ProjectionPredicate<'tcx>>>, + pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::NormalizesTo<'tcx>>>, /// The rest of the goals which have not yet processed or remain ambiguous. - pub(super) goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, + pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>, } -impl NestedGoals<'_> { +impl<'tcx> NestedGoals<'tcx> { pub(super) fn new() -> Self { Self { normalizes_to_hack_goal: None, goals: Vec::new() } } @@ -115,6 +117,11 @@ impl NestedGoals<'_> { pub(super) fn is_empty(&self) -> bool { self.normalizes_to_hack_goal.is_none() && self.goals.is_empty() } + + pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) { + assert_eq!(other.normalizes_to_hack_goal, None); + self.goals.extend(other.goals) + } } #[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)] @@ -140,7 +147,7 @@ pub trait InferCtxtEvalExt<'tcx> { } impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { - #[instrument(level = "debug", skip(self), ret)] + #[instrument(level = "debug", skip(self))] fn evaluate_root_goal( &self, goal: Goal<'tcx, ty::Predicate<'tcx>>, @@ -150,7 +157,7 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { Option<inspect::GoalEvaluation<'tcx>>, ) { EvalCtxt::enter_root(self, generate_proof_tree, |ecx| { - ecx.evaluate_goal(GoalEvaluationKind::Root, goal) + ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal) }) } } @@ -194,9 +201,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let result = f(&mut ecx); let tree = ecx.inspect.finalize(); - if let (Some(tree), DumpSolverProofTree::Always) = - (&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree) - { + if let (Some(tree), DumpSolverProofTree::Always) = ( + &tree, + infcx.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default(), + ) { let mut lock = std::io::stdout().lock(); let _ = lock.write_fmt(format_args!("{tree:?}\n")); let _ = lock.flush(); @@ -327,12 +335,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { fn evaluate_goal( &mut self, goal_evaluation_kind: GoalEvaluationKind, + source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>, ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); - let encountered_overflow = self.search_graph.encountered_overflow(); let canonical_response = EvalCtxt::evaluate_canonical_goal( self.tcx(), self.search_graph, @@ -347,13 +355,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { Ok(response) => response, }; - 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, - orig_values, - canonical_response, - ) { + let (certainty, has_changed, nested_goals) = match self + .instantiate_response_discarding_overflow( + goal.param_env, + source, + orig_values, + canonical_response, + ) { Err(e) => { self.inspect.goal_evaluation(goal_evaluation); return Err(e); @@ -367,72 +375,54 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { bug!("an unchanged goal shouldn't have any side-effects on instantiation"); } - // Check that rerunning this query with its inference constraints applied - // doesn't result in new inference constraints and has the same result. + // FIXME: We previously had an assert here that checked that recomputing + // a goal after applying its constraints did not change its response. // - // If we have projection goals like `<T as Trait>::Assoc == u32` we recursively - // call `exists<U> <T as Trait>::Assoc == U` to enable better caching. This goal - // could constrain `U` to `u32` which would cause this check to result in a - // solver cycle. - if cfg!(debug_assertions) - && has_changed - && !matches!( - goal_evaluation_kind, - GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes } - ) - && !self.search_graph.in_cycle() - { - // 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); - } + // This assert was removed as it did not hold for goals constraining + // an inference variable to a recursive alias, e.g. in + // tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs. + // + // Once we have decided on how to handle trait-system-refactor-initiative#75, + // we should re-add an assert here. Ok((has_changed, certainty, nested_goals)) } - fn check_evaluate_goal_stable_result( + fn instantiate_response_discarding_overflow( &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(), - ); + param_env: ty::ParamEnv<'tcx>, + source: GoalSource, + original_values: Vec<ty::GenericArg<'tcx>>, + response: CanonicalResponse<'tcx>, + ) -> Result<(Certainty, bool, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { + // The old solver did not evaluate nested goals when normalizing. + // It returned the selection constraints allowing a `Projection` + // obligation to not hold in coherence while avoiding the fatal error + // from overflow. + // + // We match this behavior here by considering all constraints + // from nested goals which are not from where-bounds. We will already + // need to track which nested goals are required by impl where-bounds + // for coinductive cycles, so we simply reuse that here. + // + // While we could consider overflow constraints in more cases, this should + // not be necessary for backcompat and results in better perf. It also + // avoids a potential inconsistency which would otherwise require some + // tracking for root goals as well. See #119071 for an example. + let keep_overflow_constraints = || { + self.search_graph.current_goal_is_normalizes_to() + && source != GoalSource::ImplWhereBound + }; - 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; - }}; - } + if response.value.certainty == Certainty::OVERFLOW && !keep_overflow_constraints() { + Ok((Certainty::OVERFLOW, false, Vec::new())) + } else { + let has_changed = !response.value.var_values.is_identity_modulo_regions() + || !response.value.external_constraints.opaque_types.is_empty(); - 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") + let (certainty, nested_goals) = + self.instantiate_and_apply_query_response(param_env, original_values, response)?; + Ok((certainty, has_changed, nested_goals)) } } @@ -462,8 +452,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::Coerce(predicate) => { self.compute_coerce_goal(Goal { param_env, predicate }) } - 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) } @@ -474,7 +462,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) } ty::PredicateKind::ConstEquate(_, _) => { - bug!("ConstEquate should not be emitted when `-Ztrait-solver=next` is active") + bug!("ConstEquate should not be emitted when `-Znext-solver` is active") + } + ty::PredicateKind::NormalizesTo(predicate) => { + self.compute_normalizes_to_goal(Goal { param_env, predicate }) } ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self .compute_alias_relate_goal(Goal { @@ -488,7 +479,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } else { let kind = self.infcx.instantiate_binder_with_placeholders(kind); let goal = goal.with(self.tcx(), ty::Binder::dummy(kind)); - self.add_goal(goal); + self.add_goal(GoalSource::Misc, goal); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } @@ -537,6 +528,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); self.inspect.evaluate_added_goals_loop_start(); + + fn with_misc_source<'tcx>( + it: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, + ) -> impl Iterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)> { + iter::zip(iter::repeat(GoalSource::Misc), it) + } + // 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() { @@ -545,17 +543,15 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { 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, - }, + ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, ); let (_, certainty, instantiate_goals) = self.evaluate_goal( GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes }, + GoalSource::Misc, unconstrained_goal, )?; - self.nested_goals.goals.extend(instantiate_goals); + self.nested_goals.goals.extend(with_misc_source(instantiate_goals)); // Finally, equate the goal's RHS with the unconstrained var. // We put the nested goals from this into goals instead of @@ -564,15 +560,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // 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); + goals.goals.extend(with_misc_source(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) - { + if goal.predicate.alias != self.resolve_vars_if_possible(goal.predicate.alias) { unchanged_certainty = None; } @@ -587,12 +581,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } - for goal in goals.goals.drain(..) { + for (source, goal) in goals.goals.drain(..) { let (has_changed, certainty, instantiate_goals) = self.evaluate_goal( GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No }, + source, goal, )?; - self.nested_goals.goals.extend(instantiate_goals); + self.nested_goals.goals.extend(with_misc_source(instantiate_goals)); if has_changed { unchanged_certainty = None; } @@ -600,7 +595,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { match certainty { Certainty::Yes => {} Certainty::Maybe(_) => { - self.nested_goals.goals.push(goal); + self.nested_goals.goals.push((source, goal)); unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); } } @@ -642,9 +637,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// /// This is the case if the `term` is an inference variable in the innermost universe /// and does not occur in any other part of the predicate. + #[instrument(level = "debug", skip(self), ret)] pub(super) fn term_is_fully_unconstrained( &self, - goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, ) -> bool { let term_is_infer = match goal.predicate.term.unpack() { ty::TermKind::Ty(ty) => { @@ -708,7 +704,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let mut visitor = ContainsTerm { infcx: self.infcx, term: goal.predicate.term }; term_is_infer - && goal.predicate.projection_ty.visit_with(&mut visitor).is_continue() + && goal.predicate.alias.visit_with(&mut visitor).is_continue() && goal.param_env.visit_with(&mut visitor).is_continue() } @@ -723,7 +719,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .at(&ObligationCause::dummy(), param_env) .eq(DefineOpaqueTypes::No, lhs, rhs) .map(|InferOk { value: (), obligations }| { - self.add_goals(obligations.into_iter().map(|o| o.into())); + self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); }) .map_err(|e| { debug!(?e, "failed to equate"); @@ -742,7 +738,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .at(&ObligationCause::dummy(), param_env) .sub(DefineOpaqueTypes::No, sub, sup) .map(|InferOk { value: (), obligations }| { - self.add_goals(obligations.into_iter().map(|o| o.into())); + self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); }) .map_err(|e| { debug!(?e, "failed to subtype"); @@ -750,6 +746,26 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + #[instrument(level = "debug", skip(self, param_env), ret)] + pub(super) fn relate<T: ToTrace<'tcx>>( + &mut self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + variance: ty::Variance, + rhs: T, + ) -> Result<(), NoSolution> { + self.infcx + .at(&ObligationCause::dummy(), param_env) + .relate(DefineOpaqueTypes::No, lhs, variance, rhs) + .map(|InferOk { value: (), obligations }| { + self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); + }) + .map_err(|e| { + debug!(?e, "failed to relate"); + NoSolution + }) + } + /// Equates two values returning the nested goals without adding them /// to the nested goals of the `EvalCtxt`. /// @@ -780,7 +796,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> T { self.infcx.instantiate_binder_with_fresh_vars( DUMMY_SP, - LateBoundRegionConversionTime::HigherRankedType, + BoundRegionConversionTime::HigherRankedType, value, ) } @@ -875,7 +891,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { true, &mut obligations, )?; - self.add_goals(obligations.into_iter().map(|o| o.into())); + self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); Ok(()) } @@ -895,7 +911,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { hidden_ty, &mut obligations, ); - self.add_goals(obligations.into_iter().map(|o| o.into())); + self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); } // Do something for each opaque/hidden pair defined with `def_id` in the 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 6087b9167..91fd48807 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -1,5 +1,10 @@ +use crate::solve::assembly::Candidate; + use super::EvalCtxt; -use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult}; +use rustc_middle::traits::{ + query::NoSolution, + solve::{inspect, CandidateSource, QueryResult}, +}; use std::marker::PhantomData; pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> { @@ -36,6 +41,23 @@ where } } +pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, 'tcx, F> { + cx: ProbeCtxt<'me, 'a, 'tcx, F, QueryResult<'tcx>>, + source: CandidateSource, +} + +impl<'tcx, F> TraitProbeCtxt<'_, '_, 'tcx, F> +where + F: FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, +{ + pub(in crate::solve) fn enter( + self, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> Result<Candidate<'tcx>, NoSolution> { + self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result }) + } +} + impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// `probe_kind` is only called when proof tree building is enabled so it can be /// as expensive as necessary to output the desired information. @@ -69,20 +91,18 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { pub(in crate::solve) fn probe_trait_candidate( &mut self, source: CandidateSource, - ) -> ProbeCtxt< - '_, - 'a, - 'tcx, - impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, - QueryResult<'tcx>, - > { - ProbeCtxt { - ecx: self, - probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { - source, - result: *result, + ) -> TraitProbeCtxt<'_, 'a, 'tcx, impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>> + { + TraitProbeCtxt { + cx: ProbeCtxt { + ecx: self, + probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { + source, + result: *result, + }, + _result: PhantomData, }, - _result: PhantomData, + source, } } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index f1d309122..2139210b8 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -108,6 +108,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { MismatchedProjectionTypes { err: TypeError::Mismatch }, ) } + ty::PredicateKind::NormalizesTo(..) => { + FulfillmentErrorCode::CodeProjectionError( + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } ty::PredicateKind::AliasRelate(_, _, _) => { FulfillmentErrorCode::CodeProjectionError( MismatchedProjectionTypes { err: TypeError::Mismatch }, @@ -135,7 +140,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { } ty::PredicateKind::Clause(_) | ty::PredicateKind::ObjectSafe(_) - | ty::PredicateKind::ClosureKind(_, _, _) | ty::PredicateKind::Ambiguous => { FulfillmentErrorCode::CodeSelectionError( SelectionError::Unimplemented, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 69bfdd468..6db53d6dd 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -58,7 +58,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { visitor: &mut V, ) -> ControlFlow<V::BreakTy> { // HACK: An arbitrary cutoff to avoid dealing with overflow and cycles. - if self.goal.depth >= 10 { + if self.goal.depth <= 10 { let infcx = self.goal.infcx; infcx.probe(|_| { let mut instantiated_goals = vec![]; @@ -119,8 +119,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { ) { for step in &probe.steps { match step { - &inspect::ProbeStep::AddGoal(goal) => nested_goals.push(goal), - inspect::ProbeStep::EvaluateGoals(_) => (), + &inspect::ProbeStep::AddGoal(_source, goal) => nested_goals.push(goal), inspect::ProbeStep::NestedProbe(ref probe) => { // Nested probes have to prove goals added in their parent // but do not leak them, so we truncate the added goals @@ -129,13 +128,17 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { self.candidates_recur(candidates, nested_goals, probe); nested_goals.truncate(num_goals); } + inspect::ProbeStep::EvaluateGoals(_) + | inspect::ProbeStep::CommitIfOkStart + | inspect::ProbeStep::CommitIfOkSuccess => (), } } match probe.kind { inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly - | inspect::ProbeKind::UpcastProjectionCompatibility => (), + | inspect::ProbeKind::UpcastProjectionCompatibility + | inspect::ProbeKind::CommitIfOk => (), // We add a candidate for the root evaluation if there // is only one way to prove a given goal, e.g. for `WellFormed`. // @@ -172,7 +175,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { warn!("unexpected root evaluation: {:?}", self.evaluation); return vec![]; } - inspect::CanonicalGoalEvaluationKind::Evaluation { ref revisions } => { + inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } => { if let Some(last) = revisions.last() { last } else { diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index 088455b38..d8caef5b0 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -7,7 +7,7 @@ use std::mem; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ - CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult, + CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack, QueryInput, QueryResult, }; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::DumpSolverProofTree; @@ -216,17 +216,21 @@ impl<'tcx> WipProbe<'tcx> { #[derive(Eq, PartialEq, Debug)] enum WipProbeStep<'tcx> { - AddGoal(inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>), + AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>), EvaluateGoals(WipAddedGoalsEvaluation<'tcx>), NestedProbe(WipProbe<'tcx>), + CommitIfOkStart, + CommitIfOkSuccess, } impl<'tcx> WipProbeStep<'tcx> { fn finalize(self) -> inspect::ProbeStep<'tcx> { match self { - WipProbeStep::AddGoal(goal) => inspect::ProbeStep::AddGoal(goal), + WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal), WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()), WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()), + WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart, + WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess, } } } @@ -261,7 +265,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { GenerateProofTree::Never => ProofTreeBuilder::new_noop(), GenerateProofTree::IfEnabled => { let opts = &tcx.sess.opts.unstable_opts; - match opts.dump_solver_proof_tree { + match opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() { DumpSolverProofTree::Always => ProofTreeBuilder::new_root(), // `OnError` is handled by reevaluating goals in error // reporting with `GenerateProofTree::Yes`. @@ -424,7 +428,11 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } - pub fn add_goal(ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ty::Predicate<'tcx>>) { + pub fn add_goal( + ecx: &mut EvalCtxt<'_, 'tcx>, + source: GoalSource, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) { // Can't use `if let Some(this) = ecx.inspect.as_mut()` here because // we have to immutably use the `EvalCtxt` for `make_canonical_state`. if ecx.inspect.is_noop() { @@ -438,7 +446,9 @@ impl<'tcx> ProofTreeBuilder<'tcx> { evaluation: WipProbe { steps, .. }, .. }) - | DebugSolver::Probe(WipProbe { steps, .. }) => steps.push(WipProbeStep::AddGoal(goal)), + | DebugSolver::Probe(WipProbe { steps, .. }) => { + steps.push(WipProbeStep::AddGoal(source, goal)) + } s => unreachable!("tried to add {goal:?} to {s:?}"), } } @@ -459,6 +469,29 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } + /// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside + /// of the probe into the parent. + pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, *probe.state.unwrap()) { + ( + DebugSolver::Probe(WipProbe { steps, .. }) + | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + evaluation: WipProbe { steps, .. }, + .. + }), + DebugSolver::Probe(probe), + ) => { + steps.push(WipProbeStep::CommitIfOkStart); + assert_eq!(probe.kind, None); + steps.extend(probe.steps); + steps.push(WipProbeStep::CommitIfOkSuccess); + } + _ => unreachable!(), + } + } + } + pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None }) } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index dba5369fa..2f3111a24 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -1,7 +1,6 @@ //! The next-generation trait solver, currently still WIP. //! -//! 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 user of rust, you can use `-Znext-solver` to enable the new trait solver. //! //! As a developer of rustc, you shouldn't be using the new trait //! solver without asking the trait-system-refactor-initiative, but it can @@ -16,31 +15,33 @@ //! about it on zulip. use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::query::NoSolution; use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::traits::solve::{ - CanonicalResponse, Certainty, ExternalConstraintsData, Goal, IsNormalizesToHack, QueryResult, - Response, + CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack, + QueryResult, Response, }; -use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex}; +use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; mod alias_relate; mod assembly; -mod canonicalize; mod eval_ctxt; mod fulfill; pub mod inspect; mod normalize; +mod normalizes_to; mod project_goals; mod search_graph; mod trait_goals; pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt}; pub use fulfill::FulfillmentCtxt; -pub(crate) use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; +pub(crate) use normalize::deeply_normalize_for_diagnostics; +pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; #[derive(Debug, Clone, Copy)] enum SolverMode { @@ -63,8 +64,6 @@ enum GoalEvaluationKind { trait CanonicalResponseExt { fn has_no_inference_or_external_constraints(&self) -> bool; - - fn has_only_region_constraints(&self) -> bool; } impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> { @@ -73,11 +72,6 @@ impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> { && self.value.var_values.is_identity() && self.value.external_constraints.opaque_types.is_empty() } - - fn has_only_region_constraints(&self) -> bool { - self.value.var_values.is_identity_modulo_regions() - && self.value.external_constraints.opaque_types.is_empty() - } } impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { @@ -163,7 +157,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ) -> QueryResult<'tcx> { match self.well_formed_goals(goal.param_env, goal.predicate) { Some(goals) => { - self.add_goals(goals); + self.add_goals(GoalSource::Misc, goals); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS), @@ -220,7 +214,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self))] - fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>) { + fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { assert!( self.nested_goals.normalizes_to_hack_goal.is_none(), "attempted to set the projection eq hack goal when one already exists" @@ -229,15 +223,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) { - inspect::ProofTreeBuilder::add_goal(self, goal); - self.nested_goals.goals.push(goal); + fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) { + inspect::ProofTreeBuilder::add_goal(self, source, goal); + self.nested_goals.goals.push((source, goal)); } #[instrument(level = "debug", skip(self, goals))] - fn add_goals(&mut self, goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>) { + fn add_goals( + &mut self, + source: GoalSource, + goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, + ) { for goal in goals { - self.add_goal(goal); + self.add_goal(source, goal); } } @@ -253,7 +251,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return None; } - // FIXME(-Ztrait-solver=next): We should instead try to find a `Certainty::Yes` response with + // FIXME(-Znext-solver): We should instead try to find a `Certainty::Yes` response with // a subset of the constraints that all the other responses have. let one = responses[0]; if responses[1..].iter().all(|&resp| resp == one) { @@ -294,28 +292,61 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// 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. + #[instrument(level = "debug", skip(self), ret)] 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(); + ty: Ty<'tcx>, + ) -> Option<Ty<'tcx>> { + self.try_normalize_ty_recur(param_env, DefineOpaqueTypes::Yes, 0, ty) + } + + fn try_normalize_ty_recur( + &mut self, + param_env: ty::ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + depth: usize, + ty: Ty<'tcx>, + ) -> Option<Ty<'tcx>> { + if !self.tcx().recursion_limit().value_within_limit(depth) { + return None; + } + + let ty::Alias(kind, alias) = *ty.kind() else { + return Some(ty); + }; + + // We do no always define opaque types eagerly to allow non-defining uses in the defining scope. + if let (DefineOpaqueTypes::No, ty::AliasKind::Opaque) = (define_opaque_types, kind) { + if let Some(def_id) = alias.def_id.as_local() { + if self + .unify_existing_opaque_tys( + param_env, + OpaqueTypeKey { def_id, args: alias.args }, + self.next_ty_infer(), + ) + .is_empty() + { + return Some(ty); + } + } + } + + match self.commit_if_ok(|this| { + let normalized_ty = this.next_ty_infer(); let normalizes_to_goal = Goal::new( - self.tcx(), + this.tcx(), param_env, - ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, + ty::NormalizesTo { alias, term: normalized_ty.into() }, ); - self.add_goal(normalizes_to_goal); - self.try_evaluate_added_goals()?; - ty = self.resolve_vars_if_possible(normalized_ty); + this.add_goal(GoalSource::Misc, normalizes_to_goal); + this.try_evaluate_added_goals()?; + let ty = this.resolve_vars_if_possible(normalized_ty); + Ok(this.try_normalize_ty_recur(param_env, define_opaque_types, depth + 1, ty)) + }) { + Ok(ty) => ty, + Err(NoSolution) => Some(ty), } - - Ok(None) } } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index b0a348985..55b79e6fc 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -4,19 +4,20 @@ use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::InferCtxt; use rustc_infer::traits::TraitEngineExt; use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::traits::Reveal; +use rustc_middle::traits::{ObligationCause, Reveal}; use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex}; -use rustc_middle::ty::{FallibleTypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; use super::FulfillmentCtxt; /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. -pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( +pub fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( at: At<'_, 'tcx>, value: T, ) -> Result<T, Vec<FulfillmentError<'tcx>>> { @@ -30,7 +31,7 @@ pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( /// 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>>>( +pub fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( at: At<'_, 'tcx>, value: T, universes: Vec<Option<UniverseIndex>>, @@ -75,7 +76,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { tcx, self.at.cause.clone(), self.at.param_env, - ty::ProjectionPredicate { projection_ty: alias, term: new_infer_ty.into() }, + ty::NormalizesTo { alias, term: new_infer_ty.into() }, ); // Do not emit an error if normalization is known to fail but instead @@ -128,8 +129,8 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { tcx, self.at.cause.clone(), self.at.param_env, - ty::ProjectionPredicate { - projection_ty: AliasTy::new(tcx, uv.def, uv.args), + ty::NormalizesTo { + alias: AliasTy::new(tcx, uv.def, uv.args), term: new_infer_ct.into(), }, ); @@ -230,3 +231,42 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { } } } + +// Deeply normalize a value and return it +pub(crate) fn deeply_normalize_for_diagnostics<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + t: T, +) -> T { + t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder { + at: infcx.at(&ObligationCause::dummy(), param_env), + }) +} + +struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> { + at: At<'a, 'tcx>, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.at.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + deeply_normalize_with_skipped_universes( + self.at, + ty, + vec![None; ty.outer_exclusive_binder().as_usize()], + ) + .unwrap_or_else(|_| ty.super_fold_with(self)) + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + deeply_normalize_with_skipped_universes( + self.at, + ct, + vec![None; ct.outer_exclusive_binder().as_usize()], + ) + .unwrap_or_else(|_| ct.super_fold_with(self)) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/inherent_projection.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs index 28fe59b7f..b2dff9b48 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/inherent_projection.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs @@ -4,18 +4,18 @@ //! 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::traits::solve::{Certainty, Goal, GoalSource, QueryResult}; use rustc_middle::ty; -use super::EvalCtxt; +use crate::solve::EvalCtxt; impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn normalize_inherent_associated_type( &mut self, - goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, ) -> QueryResult<'tcx> { let tcx = self.tcx(); - let inherent = goal.predicate.projection_ty; + let inherent = goal.predicate.alias; let expected = goal.predicate.term.ty().expect("inherent consts are treated separately"); let impl_def_id = tcx.parent(inherent.def_id); @@ -38,7 +38,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .expect("expected goal term to be fully unconstrained"); // Check both where clauses on the impl and IAT + // + // FIXME(-Znext-solver=coinductive): I think this should be split + // and we tag the impl bounds with `GoalSource::ImplWhereBound`? + // Right not this includes both the impl and the assoc item where bounds, + // and I don't think the assoc item where-bounds are allowed to be coinductive. self.add_goals( + GoalSource::Misc, tcx.predicates_of(inherent.def_id) .instantiate(tcx, inherent_substs) .into_iter() diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 240141065..0e9656a1e 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -1,7 +1,7 @@ use crate::traits::{check_args_compatible, specialization_graph}; -use super::assembly::{self, structural_traits}; -use super::EvalCtxt; +use super::assembly::{self, structural_traits, Candidate}; +use super::{EvalCtxt, GoalSource}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; @@ -13,20 +13,20 @@ use rustc_middle::traits::solve::{ }; use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::ProjectionPredicate; +use rustc_middle::ty::NormalizesTo; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; -mod inherent_projection; +mod inherent; mod opaques; mod weak_types; impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] - pub(super) fn compute_projection_goal( + pub(super) fn compute_normalizes_to_goal( &mut self, - goal: Goal<'tcx, ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, NormalizesTo<'tcx>>, ) -> QueryResult<'tcx> { let def_id = goal.predicate.def_id(); match self.tcx().def_kind(def_id) { @@ -71,16 +71,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] fn normalize_anon_const( &mut self, - goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, ) -> QueryResult<'tcx> { if let Some(normalized_const) = self.try_const_eval_resolve( goal.param_env, - ty::UnevaluatedConst::new( - goal.predicate.projection_ty.def_id, - goal.predicate.projection_ty.args, - ), + ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args), self.tcx() - .type_of(goal.predicate.projection_ty.def_id) + .type_of(goal.predicate.alias.def_id) .no_bound_vars() .expect("const ty should not rely on other generics"), ) { @@ -92,17 +89,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } -impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { +impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { fn self_ty(self) -> Ty<'tcx> { self.self_ty() } fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { - self.projection_ty.trait_ref(tcx) - } - - fn polarity(self) -> ty::ImplPolarity { - ty::ImplPolarity::Positive + self.alias.trait_ref(tcx) } fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { @@ -127,7 +120,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx.instantiate_binder_with_infer(projection_pred); ecx.eq( goal.param_env, - goal.predicate.projection_ty, + goal.predicate.alias, assumption_projection_pred.projection_ty, )?; ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) @@ -135,8 +128,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { // Add GAT where clauses from the trait's definition ecx.add_goals( + GoalSource::Misc, tcx.predicates_of(goal.predicate.def_id()) - .instantiate_own(tcx, goal.predicate.projection_ty.args) + .instantiate_own(tcx, goal.predicate.alias.args) .map(|(pred, _)| goal.with(tcx, pred)), ); @@ -152,15 +146,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, NormalizesTo<'tcx>>, impl_def_id: DefId, - ) -> QueryResult<'tcx> { + ) -> Result<Candidate<'tcx>, NoSolution> { let tcx = ecx.tcx(); - let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); + let goal_trait_ref = goal.predicate.alias.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.args_refs_may_unify(goal_trait_ref.args, impl_trait_ref.skip_binder().args) { + if !drcx.args_may_unify(goal_trait_ref.args, impl_trait_ref.skip_binder().args) { return Err(NoSolution); } @@ -176,12 +170,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { .predicates .into_iter() .map(|pred| goal.with(tcx, pred)); - ecx.add_goals(where_clause_bounds); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); // Add GAT where clauses from the trait's definition ecx.add_goals( + GoalSource::Misc, tcx.predicates_of(goal.predicate.def_id()) - .instantiate_own(tcx, goal.predicate.projection_ty.args) + .instantiate_own(tcx, goal.predicate.alias.args) .map(|(pred, _)| goal.with(tcx, pred)), ); @@ -200,17 +195,21 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }; 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 guar = tcx.sess.span_delayed_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), + .instantiate(tcx, goal.predicate.alias.args), ) .into(), ty::AssocKind::Type => Ty::new_error(tcx, guar).into(), - ty::AssocKind::Fn => unreachable!(), + // This makes no sense... + ty::AssocKind::Fn => span_bug!( + tcx.def_span(assoc_def.item.def_id), + "cannot project to an associated function" + ), }; ecx.eq(goal.param_env, goal.predicate.term, error_term) .expect("expected goal term to be fully unconstrained"); @@ -231,11 +230,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { // // 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 impl_args_with_gat = + goal.predicate.alias.args.rebase_onto(tcx, goal_trait_ref.def_id, impl_args); let args = ecx.translate_args( goal.param_env, impl_def_id, @@ -290,7 +286,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - ecx.tcx().sess.delay_span_bug( + ecx.tcx().sess.span_delayed_bug( ecx.tcx().def_span(goal.predicate.def_id()), "associated types not allowed on auto traits", ); @@ -419,7 +415,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { DUMMY_SP, [ty::GenericArg::from(goal.predicate.self_ty())], ); - ecx.add_goal(goal.with(tcx, sized_predicate)); + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goal(GoalSource::Misc, goal.with(tcx, sized_predicate)); tcx.types.unit } @@ -427,7 +424,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { 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))); + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goal( + GoalSource::Misc, + goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)), + ); return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } @@ -437,7 +438,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ty::Tuple(elements) => match elements.last() { None => tcx.types.unit, Some(&self_ty) => { - ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty))); + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goal( + GoalSource::Misc, + goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)), + ); return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } @@ -520,6 +525,40 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) } + fn consider_builtin_async_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let ty::Coroutine(def_id, args, _) = *self_ty.kind() else { + return Err(NoSolution); + }; + + // Coroutines are not AsyncIterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_async_gen(def_id) { + return Err(NoSolution); + } + + ecx.probe_misc_candidate("builtin AsyncIterator kind").enter(|ecx| { + // Take `AsyncIterator<Item = I>` and turn it into the corresponding + // coroutine yield ty `Poll<Option<I>>`. + let expected_ty = Ty::new_adt( + tcx, + tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)), + tcx.mk_args(&[Ty::new_adt( + tcx, + tcx.adt_def(tcx.require_lang_item(LangItem::Option, None)), + tcx.mk_args(&[goal.predicate.term.into()]), + ) + .into()]), + ); + let yield_ty = args.as_coroutine().yield_ty(); + ecx.eq(goal.param_env, expected_ty, yield_ty)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs index ebd129f32..b5d1aa06e 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs @@ -12,10 +12,10 @@ use crate::solve::{EvalCtxt, SolverMode}; impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn normalize_opaque_type( &mut self, - goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, ) -> QueryResult<'tcx> { let tcx = self.tcx(); - let opaque_ty = goal.predicate.projection_ty; + let opaque_ty = goal.predicate.alias; let expected = goal.predicate.term.ty().expect("no such thing as an opaque const"); match (goal.param_env.reveal(), self.solver_mode()) { @@ -44,6 +44,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Prefer opaques registered already. let opaque_type_key = ty::OpaqueTypeKey { def_id: opaque_ty_def_id, args: opaque_ty.args }; + // FIXME: This also unifies the previous hidden type with the expected. + // + // If that fails, we insert `expected` as a new hidden type instead of + // eagerly emitting an error. let matches = self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected); if !matches.is_empty() { @@ -53,6 +57,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return self.flounder(&matches); } } + + let expected = match self.try_normalize_ty(goal.param_env, expected) { + Some(ty) => { + if ty.is_ty_var() { + return self.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } else { + ty + } + } + None => { + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + } + }; + // Otherwise, define a new opaque type self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?; self.add_item_bounds_for_hidden_type( diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs index 54de32cf6..6d5728797 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/weak_types.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs @@ -3,18 +3,18 @@ //! //! 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::traits::solve::{Certainty, Goal, GoalSource, QueryResult}; use rustc_middle::ty; -use super::EvalCtxt; +use crate::solve::EvalCtxt; impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn normalize_weak_type( &mut self, - goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, ) -> QueryResult<'tcx> { let tcx = self.tcx(); - let weak_ty = goal.predicate.projection_ty; + let weak_ty = goal.predicate.alias; let expected = goal.predicate.term.ty().expect("no such thing as a const alias"); let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args); @@ -22,6 +22,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Check where clauses self.add_goals( + GoalSource::Misc, tcx.predicates_of(weak_ty.def_id) .instantiate(tcx, weak_ty.args) .predicates diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs new file mode 100644 index 000000000..30ae385a8 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -0,0 +1,38 @@ +use crate::solve::GoalSource; + +use super::EvalCtxt; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::ty::{self, ProjectionPredicate}; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn compute_projection_goal( + &mut self, + goal: Goal<'tcx, ProjectionPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let projection_term = match goal.predicate.term.unpack() { + ty::TermKind::Ty(_) => goal.predicate.projection_ty.to_ty(tcx).into(), + ty::TermKind::Const(_) => ty::Const::new_unevaluated( + tcx, + ty::UnevaluatedConst::new( + goal.predicate.projection_ty.def_id, + goal.predicate.projection_ty.args, + ), + tcx.type_of(goal.predicate.projection_ty.def_id) + .instantiate(tcx, goal.predicate.projection_ty.args), + ) + .into(), + }; + let goal = goal.with( + tcx, + ty::PredicateKind::AliasRelate( + projection_term, + goal.predicate.term, + ty::AliasRelationDirection::Equate, + ), + ); + self.add_goal(GoalSource::Misc, goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index 7ffa1d7d3..2a161c2d9 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -8,11 +8,13 @@ use rustc_index::IndexVec; use rustc_middle::dep_graph::dep_kinds; use rustc_middle::traits::solve::CacheData; use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; +use rustc_middle::ty; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; use std::collections::hash_map::Entry; rustc_index::newtype_index! { + #[orderable] pub struct StackDepth {} } @@ -37,7 +39,7 @@ struct StackEntry<'tcx> { /// 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 + /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for /// an example of where this is needed. cycle_participants: FxHashSet<CanonicalInput<'tcx>>, } @@ -110,37 +112,13 @@ impl<'tcx> SearchGraph<'tcx> { self.stack.is_empty() } - /// Whether we're currently in a cycle. This should only be used - /// for debug assertions. - pub(super) fn in_cycle(&self) -> bool { - if let Some(stack_depth) = self.stack.last_index() { - // Either the current goal on the stack is the root of a cycle - // or it depends on a goal with a lower depth. - self.stack[stack_depth].has_been_used - || self.stack[stack_depth].cycle_root_depth != stack_depth - } else { - false - } - } - - /// 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 - } + pub(super) fn current_goal_is_normalizes_to(&self) -> bool { + self.stack.raw.last().map_or(false, |e| { + matches!( + e.input.value.goal.predicate.kind().skip_binder(), + ty::PredicateKind::NormalizesTo(..) + ) + }) } /// Returns the remaining depth allowed for nested goals. @@ -269,7 +247,7 @@ impl<'tcx> SearchGraph<'tcx> { // in unstable results due to incompleteness. // // However, a test for this would be an even more complex version of - // tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.rs. + // tests/ui/traits/next-solver/coinduction/incompleteness-unstable-result.rs. // I did not bother to write such a test and we have no regression test // for this. It would be good to have such a test :) #[allow(rustc::potential_query_instability)] @@ -280,7 +258,7 @@ impl<'tcx> SearchGraph<'tcx> { // until we reach a fixpoint. It is not enough to simply retry the // `root` goal of this cycle. // - // See tests/ui/traits/new-solver/cycles/fixpoint-rerun-all-cycle-heads.rs + // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs // for an example. self.stack[stack_depth].has_been_used = true; return if let Some(result) = self.stack[stack_depth].provisional_result { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index a0e2ad6e2..ac3ffd2d6 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,12 +1,14 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use super::assembly::{self, structural_traits}; -use super::{EvalCtxt, SolverMode}; +use super::assembly::{self, structural_traits, Candidate}; +use super::{EvalCtxt, GoalSource, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::inspect::ProbeKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::solve::{ + CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, +}; use rustc_middle::traits::{BuiltinImplSource, Reveal}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -22,10 +24,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { self.trait_ref } - fn polarity(self) -> ty::ImplPolarity { - self.polarity - } - fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { self.with_self_ty(tcx, self_ty) } @@ -38,14 +36,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, TraitPredicate<'tcx>>, impl_def_id: DefId, - ) -> QueryResult<'tcx> { + ) -> Result<Candidate<'tcx>, NoSolution> { let tcx = ecx.tcx(); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; - if !drcx - .args_refs_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) - { + if !drcx.args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) { return Err(NoSolution); } @@ -65,7 +61,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }, }; - ecx.probe_misc_candidate("impl").enter(|ecx| { + ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { let impl_args = ecx.fresh_args_for_item(impl_def_id); let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args); @@ -76,7 +72,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { .predicates .into_iter() .map(|pred| goal.with(tcx, pred)); - ecx.add_goals(where_clause_bounds); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty) }) @@ -176,7 +172,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let nested_obligations = tcx .predicates_of(goal.predicate.def_id()) .instantiate(tcx, goal.predicate.trait_ref.args); - ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p))); + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goals( + GoalSource::Misc, + nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)), + ); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -260,7 +260,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { Err(NoSolution) } } - ty::ImplPolarity::Reservation => bug!(), + // FIXME: Goal polarity should be split from impl polarity + ty::ImplPolarity::Reservation => { + bug!("we never expect a `Reservation` polarity in a trait goal") + } } } @@ -374,6 +377,30 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + fn consider_builtin_async_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + let ty::Coroutine(def_id, _, _) = *goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + // Coroutines are not iterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_async_gen(def_id) { + return Err(NoSolution); + } + + // Gen coroutines unconditionally implement `Iterator` + // Technically, we need to check that the iterator output type is Sized, + // but that's already proven by the coroutines being WF. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -425,7 +452,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return Err(NoSolution); } - // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver + // FIXME(-Znext-solver): Implement this when we get const working in the new solver // `Destruct` is automatically implemented for every type in // non-const environments. @@ -471,7 +498,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { 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))? + 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); }; @@ -489,17 +516,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // 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)))); + ecx.add_goals( + GoalSource::ImplWhereBound, + 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]))); + ecx.add_goal( + GoalSource::ImplWhereBound, + 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.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region))); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -538,9 +571,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { 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![], + Some(b_ty) => b_ty, + None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)], }; let goal = goal.with(ecx.tcx(), (a_ty, b_ty)); @@ -727,11 +759,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } // 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.add_goal( + GoalSource::ImplWhereBound, + 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) } @@ -804,14 +839,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // 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( + self.add_goal( + GoalSource::ImplWhereBound, + goal.with( tcx, - tcx.lang_items().unsize_trait().unwrap(), - [a_tail_ty, b_tail_ty], + 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) } @@ -843,14 +881,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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( + self.add_goal( + GoalSource::ImplWhereBound, + goal.with( tcx, - tcx.lang_items().unsize_trait().unwrap(), - [a_last_ty, b_last_ty], + 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) } @@ -959,6 +1000,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> QueryResult<'tcx> { self.probe_misc_candidate("constituent tys").enter(|ecx| { ecx.add_goals( + GoalSource::ImplWhereBound, constituent_tys(ecx, goal.predicate.self_ty())? .into_iter() .map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty))) diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 8096d7969..13a09917c 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -5,11 +5,9 @@ use super::*; use crate::errors::UnableToConstructConstantValue; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; -use crate::infer::InferCtxt; use crate::traits::project::ProjectAndUnifyResult; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ImplPolarity, Region, RegionVid}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; @@ -252,7 +250,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { fresh_preds.insert(self.clean_pred(infcx, predicate.as_predicate())); } - let mut select = SelectionContext::new(&infcx); + let mut select = SelectionContext::new(infcx); let mut already_visited = FxHashSet::default(); let mut predicates = VecDeque::new(); @@ -410,11 +408,11 @@ impl<'tcx> AutoTraitFinder<'tcx> { iter::zip(new_args.regions(), old_args.regions()) { match (*new_region, *old_region) { - // If both predicates have an `ReLateBound` (a HRTB) in the + // If both predicates have an `ReBound` (a HRTB) in the // same spot, we do nothing. - (ty::ReLateBound(_, _), ty::ReLateBound(_, _)) => {} + (ty::ReBound(_, _), ty::ReBound(_, _)) => {} - (ty::ReLateBound(_, _), _) | (_, ty::ReVar(_)) => { + (ty::ReBound(_, _), _) | (_, ty::ReVar(_)) => { // One of these is true: // The new predicate has a HRTB in a spot where the old // predicate does not (if they both had a HRTB, the previous @@ -440,7 +438,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // `user_computed_preds`. return false; } - (_, ty::ReLateBound(_, _)) | (ty::ReVar(_), _) => { + (_, ty::ReBound(_, _)) | (ty::ReVar(_), _) => { // This is the opposite situation as the previous arm. // One of these is true: // @@ -820,9 +818,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { // the `ParamEnv`. ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) // FIXME(generic_const_exprs): you can absolutely add this as a where clauses | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index dcf5fd869..533fe32f7 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -6,8 +6,8 @@ use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::InferOk; -use crate::solve::inspect; use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; +use crate::solve::{deeply_normalize_for_diagnostics, inspect}; use crate::traits::engine::TraitEngineExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs}; @@ -20,15 +20,15 @@ use crate::traits::{ }; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, TraitEngine}; use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, Goal}; +use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal}; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE; @@ -53,7 +53,7 @@ pub enum Conflict { pub struct OverlapResult<'tcx> { pub impl_header: ty::ImplHeader<'tcx>, - pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause>, + pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>, /// `true` if the overlap might've been permitted before the shift /// to universes. @@ -100,7 +100,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.args_refs_may_unify(a.skip_binder().args, b.skip_binder().args), + (Some(a), Some(b)) => drcx.args_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(); @@ -245,7 +245,7 @@ fn overlap<'tcx>( let trait_ref = infcx.resolve_vars_if_possible(trait_ref); format!( "of `{}` for `{}`", - trait_ref.print_only_trait_path(), + trait_ref.print_trait_sugared(), trait_ref.self_ty() ) } @@ -273,7 +273,6 @@ fn overlap<'tcx>( causing the impls to overlap", infcx.resolve_vars_if_possible(failing_obligation.predicate) )); - lint }, ); } @@ -308,7 +307,13 @@ fn overlap<'tcx>( .iter() .any(|c| c.0.involves_placeholders()); - let impl_header = selcx.infcx.resolve_vars_if_possible(impl1_header); + let mut impl_header = infcx.resolve_vars_if_possible(impl1_header); + + // Deeply normalize the impl header for diagnostics, ignoring any errors if this fails. + if infcx.next_trait_solver() { + impl_header = deeply_normalize_for_diagnostics(&infcx, param_env, impl_header); + } + Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) } @@ -329,7 +334,7 @@ fn equate_impl_headers<'tcx>( impl1.self_ty, impl2.self_ty, ), - _ => bug!("mk_eq_impl_headers given mismatched impl kinds"), + _ => bug!("equate_impl_headers given mismatched impl kinds"), }; result.map(|infer_ok| infer_ok.obligations).ok() @@ -360,15 +365,23 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( let infcx = selcx.infcx; obligations.iter().find(|obligation| { - if infcx.next_trait_solver() { - infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply()) + let evaluation_result = if infcx.next_trait_solver() { + infcx.evaluate_obligation(obligation) } 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(), - ) + selcx.evaluate_root_obligation(obligation) + }; + + match evaluation_result { + Ok(result) => !result.may_apply(), + // If overflow occurs, we need to conservatively treat the goal as possibly holding, + // since there can be instantiations of this goal that don't overflow and result in + // success. This isn't much of a problem in the old solver, since we treat overflow + // fatally (this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>), + // but in the new solver, this is very important for correctness, since overflow + // *must* be treated as ambiguity for completeness. + Err(_overflow) => false, } }) } @@ -396,8 +409,11 @@ fn impl_intersection_has_negative_obligation( ) -> bool { debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); + // N.B. We need to unify impl headers *with* intercrate mode, even if proving negative predicates + // do not need intercrate mode enabled. let ref infcx = tcx.infer_ctxt().intercrate(true).with_next_trait_solver(true).build(); - let universe = infcx.universe(); + let root_universe = infcx.universe(); + assert_eq!(root_universe, ty::UniverseIndex::ROOT); let impl1_header = fresh_impl_header(infcx, impl1_def_id); let param_env = @@ -407,13 +423,25 @@ fn impl_intersection_has_negative_obligation( // Equate the headers to find their intersection (the general type, with infer vars, // that may apply both impls). - let Some(_equate_obligations) = + let Some(equate_obligations) = equate_impl_headers(infcx, param_env, &impl1_header, &impl2_header) else { return false; }; - plug_infer_with_placeholders(infcx, universe, (impl1_header.impl_args, impl2_header.impl_args)); + // FIXME(with_negative_coherence): the infcx has constraints from equating + // the impl headers. We should use these constraints as assumptions, not as + // requirements, when proving the negated where clauses below. + drop(equate_obligations); + drop(infcx.take_registered_region_obligations()); + drop(infcx.take_and_reset_region_constraints()); + + plug_infer_with_placeholders( + infcx, + root_universe, + (impl1_header.impl_args, impl2_header.impl_args), + ); + let param_env = infcx.resolve_vars_if_possible(param_env); util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args)) .any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env)) @@ -458,7 +486,7 @@ fn plug_infer_with_placeholders<'tcx>( ), ) else { - bug!() + bug!("we always expect to be able to plug an infer var with placeholder") }; assert_eq!(obligations, &[]); ControlFlow::Continue(()) @@ -481,7 +509,7 @@ fn plug_infer_with_placeholders<'tcx>( ), ) else { - bug!() + bug!("we always expect to be able to plug an infer var with placeholder") }; assert_eq!(obligations, &[]); ControlFlow::Continue(()) @@ -515,7 +543,7 @@ fn plug_infer_with_placeholders<'tcx>( ), ) else { - bug!() + bug!("we always expect to be able to plug an infer var with placeholder") }; assert_eq!(obligations, &[]); } @@ -540,15 +568,11 @@ fn try_prove_negated_where_clause<'tcx>( return false; }; - // FIXME(with_negative_coherence): the infcx has region contraints from equating - // the impl headers as requirements. Given that the only region constraints we - // get are involving inference regions in the root, it shouldn't matter, but - // still sus. - // - // We probably should just throw away the region obligations registered up until - // now, or ideally use them as assumptions when proving the region obligations - // that we get from proving the negative predicate below. - let ref infcx = root_infcx.fork(); + // N.B. We don't need to use intercrate mode here because we're trying to prove + // the *existence* of a negative goal, not the non-existence of a positive goal. + // Without this, we over-eagerly register coherence ambiguity candidates when + // impl candidates do exist. + let ref infcx = root_infcx.fork_with_intercrate(false); let ocx = ObligationCtxt::new(infcx); ocx.register_obligation(Obligation::new( @@ -830,7 +854,7 @@ where } fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - // Need to lazily normalize here in with `-Ztrait-solver=next-coherence`. + // Need to lazily normalize here in with `-Znext-solver=coherence`. let ty = match (self.lazily_normalize_ty)(ty) { Ok(ty) => ty, Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), @@ -967,8 +991,8 @@ where fn compute_intercrate_ambiguity_causes<'tcx>( infcx: &InferCtxt<'tcx>, obligations: &[PredicateObligation<'tcx>], -) -> FxIndexSet<IntercrateAmbiguityCause> { - let mut causes: FxIndexSet<IntercrateAmbiguityCause> = Default::default(); +) -> FxIndexSet<IntercrateAmbiguityCause<'tcx>> { + let mut causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>> = Default::default(); for obligation in obligations { search_ambiguity_causes(infcx, obligation.clone().into(), &mut causes); @@ -977,11 +1001,11 @@ fn compute_intercrate_ambiguity_causes<'tcx>( causes } -struct AmbiguityCausesVisitor<'a> { - causes: &'a mut FxIndexSet<IntercrateAmbiguityCause>, +struct AmbiguityCausesVisitor<'a, 'tcx> { + causes: &'a mut FxIndexSet<IntercrateAmbiguityCause<'tcx>>, } -impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> { +impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { type BreakTy = !; fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> ControlFlow<Self::BreakTy> { let infcx = goal.infcx(); @@ -998,26 +1022,53 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> { let Goal { param_env, predicate } = goal.goal(); - // For bound predicates we simply call `infcx.replace_bound_vars_with_placeholders` + // For bound predicates we simply call `infcx.instantiate_binder_with_placeholders` // and then prove the resulting predicate as a nested goal. let trait_ref = match predicate.kind().no_bound_vars() { Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref, - Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))) => { + Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))) + if matches!( + infcx.tcx.def_kind(proj.projection_ty.def_id), + DefKind::AssocTy | DefKind::AssocConst + ) => + { proj.projection_ty.trait_ref(infcx.tcx) } _ => return ControlFlow::Continue(()), }; + // Add ambiguity causes for reservation impls. + for cand in goal.candidates() { + if let inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(def_id), + result: Ok(_), + } = cand.kind() + { + if let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { + let message = infcx + .tcx + .get_attr(def_id, sym::rustc_reservation_impl) + .and_then(|a| a.value_str()); + if let Some(message) = message { + self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message }); + } + } + } + } + + // Add ambiguity causes for unknowable goals. let mut ambiguity_cause = None; for cand in goal.candidates() { // FIXME: boiiii, using string comparisions here sure is scuffed. - if let inspect::ProbeKind::MiscCandidate { name: "coherence unknowable", result: _ } = - cand.kind() + if let inspect::ProbeKind::MiscCandidate { + name: "coherence unknowable", + result: Ok(_), + } = cand.kind() { let lazily_normalize_ty = |ty: Ty<'tcx>| { let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(infcx); if matches!(ty.kind(), ty::Alias(..)) { - // FIXME(-Ztrait-solver=next-coherence): we currently don't + // FIXME(-Znext-solver=coherence): we currently don't // normalize opaque types here, resulting in diverging behavior // for TAITs. match infcx @@ -1038,25 +1089,23 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> { Ok(Ok(())) => warn!("expected an unknowable trait ref: {trait_ref:?}"), Ok(Err(conflict)) => { if !trait_ref.references_error() { + // Normalize the trait ref for diagnostics, ignoring any errors if this fails. + let trait_ref = + deeply_normalize_for_diagnostics(infcx, param_env, trait_ref); + let self_ty = trait_ref.self_ty(); - let (trait_desc, self_desc) = with_no_trimmed_paths!({ - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = self_ty - .has_concrete_skeleton() - .then(|| self_ty.to_string()); - (trait_desc, self_desc) - }); + let self_ty = self_ty.has_concrete_skeleton().then(|| self_ty); ambiguity_cause = Some(match conflict { Conflict::Upstream => { IntercrateAmbiguityCause::UpstreamCrateUpdate { - trait_desc, - self_desc, + trait_ref, + self_ty, } } Conflict::Downstream => { IntercrateAmbiguityCause::DownstreamCrate { - trait_desc, - self_desc, + trait_ref, + self_ty, } } }); @@ -1093,7 +1142,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> { fn search_ambiguity_causes<'tcx>( infcx: &InferCtxt<'tcx>, goal: Goal<'tcx, ty::Predicate<'tcx>>, - causes: &mut FxIndexSet<IntercrateAmbiguityCause>, + causes: &mut FxIndexSet<IntercrateAmbiguityCause<'tcx>>, ) { infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { causes }); } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 62ab1e104..aee513207 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -65,7 +65,7 @@ pub fn is_const_evaluatable<'tcx>( // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but // currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it // is evaluatable or not. For now we just ICE until this is implemented. - Err(NotConstEvaluatable::Error(tcx.sess.delay_span_bug( + Err(NotConstEvaluatable::Error(tcx.sess.span_delayed_bug( span, "evaluating `ConstKind::Expr` is not currently supported", ))) @@ -74,7 +74,7 @@ pub fn is_const_evaluatable<'tcx>( let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); match concrete { Err(ErrorHandled::TooGeneric(_)) => { - Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug( + Err(NotConstEvaluatable::Error(infcx.tcx.sess.span_delayed_bug( span, "Missing value for constant, but no error reported?", ))) @@ -138,10 +138,10 @@ pub fn is_const_evaluatable<'tcx>( } else if uv.has_non_region_param() { NotConstEvaluatable::MentionsParam } else { - let guar = infcx - .tcx - .sess - .delay_span_bug(span, "Missing value for constant, but no error reported?"); + let guar = infcx.tcx.sess.span_delayed_bug( + span, + "Missing value for constant, but no error reported?", + ); NotConstEvaluatable::Error(guar) }; diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index d9a1a9819..013a50f9f 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -25,7 +25,6 @@ use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::Variance; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::config::TraitSolver; pub trait TraitEngineExt<'tcx> { fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>; @@ -33,18 +32,16 @@ pub trait TraitEngineExt<'tcx> { impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> { - match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) { - (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => { - Box::new(FulfillmentContext::new(infcx)) - } - (TraitSolver::Classic | TraitSolver::Next | TraitSolver::NextCoherence, true) => { - Box::new(NextFulfillmentCtxt::new(infcx)) - } - (TraitSolver::Next, false) => bug!( - "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})", - infcx.tcx.sess.opts.unstable_opts.trait_solver, - infcx.next_trait_solver() - ), + if infcx.next_trait_solver() { + Box::new(NextFulfillmentCtxt::new(infcx)) + } else { + let new_solver_globally = + infcx.tcx.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.globally); + assert!( + !new_solver_globally, + "using old solver even though new solver is enabled globally" + ); + Box::new(FulfillmentContext::new(infcx)) } } } @@ -108,7 +105,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, value: T, ) -> T { - let infer_ok = self.infcx.at(&cause, param_env).normalize(value); + let infer_ok = self.infcx.at(cause, param_env).normalize(value); self.register_infer_ok_obligations(infer_ok) } @@ -204,7 +201,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { generic_param_scope: LocalDefId, outlives_env: &OutlivesEnvironment<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let errors = self.infcx.resolve_regions(&outlives_env); + let errors = self.infcx.resolve_regions(outlives_env); if errors.is_empty() { Ok(()) } else { 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 5bc5a12a8..b246e476b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs @@ -1,5 +1,5 @@ use rustc_hir::def_id::DefId; -use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime}; +use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; use rustc_infer::traits::util::elaborate; use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation}; use rustc_middle::ty; @@ -53,7 +53,7 @@ pub fn recompute_applicable_impls<'tcx>( let param_env_predicate = infcx.instantiate_binder_with_fresh_vars( DUMMY_SP, - LateBoundRegionConversionTime::HigherRankedType, + BoundRegionConversionTime::HigherRankedType, poly_trait_predicate, ); let param_env_trait_ref = diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs index b4835b011..0190d5ab4 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs @@ -61,8 +61,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { .params .iter() .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = - *arg.pat + if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat { Some(ArgKind::Tuple( Some(span), @@ -92,7 +91,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { .inputs .iter() .map(|arg| match arg.kind { - hir::TyKind::Tup(ref tys) => ArgKind::Tuple( + hir::TyKind::Tup(tys) => ArgKind::Tuple( Some(arg.span), vec![("_".to_owned(), "_".to_owned()); tys.len()], ), @@ -100,7 +99,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { }) .collect::<Vec<ArgKind>>(), ), - Node::Ctor(ref variant_data) => { + Node::Ctor(variant_data) => { let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) } 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 5fbfedd3e..61e97dde5 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 @@ -103,7 +103,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { /// to be the enclosing (async) block/function/closure fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { let hir = self.tcx.hir(); - let node = hir.find(hir_id)?; + let node = self.tcx.opt_hir_node(hir_id)?; match &node { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { self.describe_coroutine(*body_id).or_else(|| { @@ -184,18 +184,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { flags.push((sym::cause, Some("MainFunctionType".to_string()))); } - if let Some(kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id) - && let ty::Tuple(args) = trait_ref.args.type_at(1).kind() - { - let args = args - .iter() - .map(|ty| ty.to_string()) - .collect::<Vec<_>>() - .join(", "); - flags.push((sym::Trait, Some(format!("{}({args})", kind.as_str())))); - } else { - flags.push((sym::Trait, Some(trait_ref.print_only_trait_path().to_string()))); - } + flags.push((sym::Trait, Some(trait_ref.print_trait_sugared().to_string()))); // Add all types without trimmed paths or visible paths, ensuring they end up with // their "canonical" def path. @@ -325,7 +314,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } #[derive(Clone, Debug)] -pub struct OnUnimplementedFormatString(Symbol); +pub struct OnUnimplementedFormatString { + symbol: Symbol, + span: Span, + is_diagnostic_namespace_variant: bool, +} #[derive(Debug)] pub struct OnUnimplementedDirective { @@ -354,7 +347,7 @@ pub struct OnUnimplementedNote { pub enum AppendConstMessage { #[default] Default, - Custom(Symbol), + Custom(Symbol, Span), } #[derive(LintDiagnostic)] @@ -376,6 +369,43 @@ impl MalformedOnUnimplementedAttrLint { #[help] pub struct MissingOptionsForOnUnimplementedAttr; +#[derive(LintDiagnostic)] +#[diag(trait_selection_ignored_diagnostic_option)] +pub struct IgnoredDiagnosticOption { + pub option_name: &'static str, + #[label] + pub span: Span, + #[label(trait_selection_other_label)] + pub prev_span: Span, +} + +impl IgnoredDiagnosticOption { + fn maybe_emit_warning<'tcx>( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + new: Option<Span>, + old: Option<Span>, + option_name: &'static str, + ) { + if let (Some(new_item), Some(old_item)) = (new, old) { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + new_item, + IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, + ); + } + } +} + +#[derive(LintDiagnostic)] +#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)] +#[help] +pub struct UnknownFormatParameterForOnUnimplementedAttr { + argument_name: Symbol, + trait_name: Symbol, +} + impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -388,8 +418,15 @@ impl<'tcx> OnUnimplementedDirective { let mut errored = None; let mut item_iter = items.iter(); - let parse_value = |value_str| { - OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some) + let parse_value = |value_str, value_span| { + OnUnimplementedFormatString::try_parse( + tcx, + item_def_id, + value_str, + value_span, + is_diagnostic_namespace_variant, + ) + .map(Some) }; let condition = if is_root { @@ -402,7 +439,7 @@ impl<'tcx> OnUnimplementedDirective { .ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?; attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| { if let Some(value) = cfg.value - && let Err(guar) = parse_value(value) + && let Err(guar) = parse_value(value, cfg.span) { errored = Some(guar); } @@ -421,17 +458,17 @@ impl<'tcx> OnUnimplementedDirective { for item in item_iter { if item.has_name(sym::message) && message.is_none() { if let Some(message_) = item.value_str() { - message = parse_value(message_)?; + message = parse_value(message_, item.span())?; continue; } } else if item.has_name(sym::label) && label.is_none() { if let Some(label_) = item.value_str() { - label = parse_value(label_)?; + label = parse_value(label_, item.span())?; continue; } } else if item.has_name(sym::note) { if let Some(note_) = item.value_str() { - if let Some(note) = parse_value(note_)? { + if let Some(note) = parse_value(note_, item.span())? { notes.push(note); continue; } @@ -441,7 +478,7 @@ impl<'tcx> OnUnimplementedDirective { && !is_diagnostic_namespace_variant { if let Some(parent_label_) = item.value_str() { - parent_label = parse_value(parent_label_)?; + parent_label = parse_value(parent_label_, item.span())?; continue; } } else if item.has_name(sym::on) @@ -456,7 +493,7 @@ impl<'tcx> OnUnimplementedDirective { match Self::parse( tcx, item_def_id, - &items, + items, item.span(), false, is_diagnostic_namespace_variant, @@ -474,7 +511,7 @@ impl<'tcx> OnUnimplementedDirective { && !is_diagnostic_namespace_variant { if let Some(msg) = item.value_str() { - append_const_msg = Some(AppendConstMessage::Custom(msg)); + append_const_msg = Some(AppendConstMessage::Custom(msg, item.span())); continue; } else if item.is_word() { append_const_msg = Some(AppendConstMessage::Default); @@ -485,7 +522,7 @@ impl<'tcx> OnUnimplementedDirective { if is_diagnostic_namespace_variant { tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), vec![item.span()], MalformedOnUnimplementedAttrLint::new(item.span()), ); @@ -523,6 +560,54 @@ impl<'tcx> OnUnimplementedDirective { subcommands.extend(directive.subcommands); let mut notes = aggr.notes; notes.extend(directive.notes); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.message.as_ref().map(|f| f.span), + aggr.message.as_ref().map(|f| f.span), + "message", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.label.as_ref().map(|f| f.span), + aggr.label.as_ref().map(|f| f.span), + "label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.condition.as_ref().map(|i| i.span), + aggr.condition.as_ref().map(|i| i.span), + "condition", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.parent_label.as_ref().map(|f| f.span), + aggr.parent_label.as_ref().map(|f| f.span), + "parent_label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { + Some(*s) + } else { + None + } + }), + aggr.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { + Some(*s) + } else { + None + } + }), + "append_const_msg", + ); + Ok(Some(Self { condition: aggr.condition.or(directive.condition), subcommands, @@ -560,6 +645,7 @@ impl<'tcx> OnUnimplementedDirective { item_def_id, value, attr.span, + is_diagnostic_namespace_variant, )?), notes: Vec::new(), parent_label: None, @@ -576,7 +662,7 @@ impl<'tcx> OnUnimplementedDirective { tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), report_span, MalformedOnUnimplementedAttrLint::new(report_span), ); @@ -587,14 +673,14 @@ impl<'tcx> OnUnimplementedDirective { AttrKind::Normal(p) if !matches!(p.item.args, AttrArgs::Empty) => { tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), attr.span, MalformedOnUnimplementedAttrLint::new(attr.span), ); } _ => tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), attr.span, MissingOptionsForOnUnimplementedAttr, ), @@ -602,8 +688,9 @@ impl<'tcx> OnUnimplementedDirective { Ok(None) } else { - let reported = - tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str"); + let reported = tcx + .sess + .span_delayed_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str"); return Err(reported); }; debug!("of_item({:?}) = {:?}", item_def_id, result); @@ -636,7 +723,18 @@ impl<'tcx> OnUnimplementedDirective { let value = cfg.value.map(|v| { // `with_no_visible_paths` is also used when generating the options, // so we need to match it here. - ty::print::with_no_visible_paths!(OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)) + ty::print::with_no_visible_paths!( + OnUnimplementedFormatString { + symbol: v, + span: cfg.span, + is_diagnostic_namespace_variant: false + } + .format( + tcx, + trait_ref, + &options_map + ) + ) }); options.contains(&(cfg.name, value)) @@ -679,19 +777,19 @@ impl<'tcx> OnUnimplementedFormatString { tcx: TyCtxt<'tcx>, item_def_id: DefId, from: Symbol, - err_sp: Span, + value_span: Span, + is_diagnostic_namespace_variant: bool, ) -> Result<Self, ErrorGuaranteed> { - let result = OnUnimplementedFormatString(from); - result.verify(tcx, item_def_id, err_sp)?; + let result = OnUnimplementedFormatString { + symbol: from, + span: value_span, + is_diagnostic_namespace_variant, + }; + result.verify(tcx, item_def_id)?; Ok(result) } - fn verify( - &self, - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - span: Span, - ) -> Result<(), ErrorGuaranteed> { + fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> { let trait_def_id = if tcx.is_trait(item_def_id) { item_def_id } else { @@ -700,7 +798,7 @@ impl<'tcx> OnUnimplementedFormatString { }; let trait_name = tcx.item_name(trait_def_id); let generics = tcx.generics_of(item_def_id); - let s = self.0.as_str(); + let s = self.symbol.as_str(); let parser = Parser::new(s, None, None, false, ParseMode::Format); let mut result = Ok(()); for token in parser { @@ -710,24 +808,40 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentNamed(s) => { match Symbol::intern(s) { // `{ThisTraitsName}` is allowed - s if s == trait_name => (), - s if ALLOWED_FORMAT_SYMBOLS.contains(&s) => (), + s if s == trait_name && !self.is_diagnostic_namespace_variant => (), + s if ALLOWED_FORMAT_SYMBOLS.contains(&s) + && !self.is_diagnostic_namespace_variant => + { + () + } // So is `{A}` if A is a type parameter s if generics.params.iter().any(|param| param.name == s) => (), s => { - result = Err(struct_span_err!( - tcx.sess, - span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{trait_name}`") - } else { - "impl".to_string() - } - ) - .emit()); + if self.is_diagnostic_namespace_variant { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + UnknownFormatParameterForOnUnimplementedAttr { + argument_name: s, + trait_name, + }, + ); + } else { + result = Err(struct_span_err!( + tcx.sess, + self.span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{trait_name}`") + } else { + "impl".to_string() + } + ) + .emit()); + } } } } @@ -735,7 +849,7 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { let reported = struct_span_err!( tcx.sess, - span, + self.span, E0231, "only named substitution parameters are allowed" ) @@ -774,37 +888,42 @@ impl<'tcx> OnUnimplementedFormatString { .collect::<FxHashMap<Symbol, String>>(); let empty_string = String::new(); - let s = self.0.as_str(); + let s = self.symbol.as_str(); let parser = Parser::new(s, None, None, false, ParseMode::Format); let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); parser .map(|p| match p { - Piece::String(s) => s, + Piece::String(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => { - let s = Symbol::intern(s); + Position::ArgumentNamed(arg) => { + let s = Symbol::intern(arg); match generic_map.get(&s) { - Some(val) => val, - None if s == name => &trait_str, + Some(val) => val.to_string(), + None if self.is_diagnostic_namespace_variant => { + format!("{{{arg}}}") + } + None if s == name => trait_str.clone(), None => { if let Some(val) = options.get(&s) { - val + val.clone() } else if s == sym::from_desugaring { // don't break messages using these two arguments incorrectly - &empty_string - } else if s == sym::ItemContext { - &item_context + String::new() + } else if s == sym::ItemContext + && !self.is_diagnostic_namespace_variant + { + item_context.clone() } else if s == sym::integral { - "{integral}" + String::from("{integral}") } else if s == sym::integer_ { - "{integer}" + String::from("{integer}") } else if s == sym::float { - "{float}" + String::from("{float}") } else { bug!( "broken on_unimplemented {:?} for {:?}: \ no argument matching {:?}", - self.0, + self.symbol, trait_ref, s ) @@ -812,7 +931,7 @@ impl<'tcx> OnUnimplementedFormatString { } } } - _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), + _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol), }, }) .collect() 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 6b09bc898..a1b896d22 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -17,7 +17,7 @@ use rustc_errors::{ ErrorGuaranteed, MultiSpan, Style, SuggestionStyle, }; use rustc_hir as hir; -use rustc_hir::def::DefKind; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::is_range_literal; @@ -26,7 +26,7 @@ use rustc_hir::{CoroutineKind, CoroutineSource, Node}; use rustc_hir::{Expr, HirId}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime}; +use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; use rustc_middle::hir::map; use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError::{self, Sorts}; @@ -36,7 +36,7 @@ use rustc_middle::ty::{ TypeSuperFoldable, TypeVisitableExt, TypeckResults, }; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP}; use rustc_target::spec::abi; use std::borrow::Cow; @@ -99,7 +99,7 @@ impl<'tcx, 'a> CoroutineData<'tcx, 'a> { .awaits .into_iter() .map(|id| hir.expect_expr(id)) - .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(&await_expr)))) + .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(await_expr)))) .map(|expr| expr.span) } } @@ -222,6 +222,15 @@ pub trait TypeErrCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + fn note_conflicting_fn_args( + &self, + err: &mut Diagnostic, + cause: &ObligationCauseCode<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ); + fn note_conflicting_closure_bounds( &self, cause: &ObligationCauseCode<'tcx>, @@ -501,7 +510,7 @@ pub fn suggest_restriction<'tcx>( impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn suggest_restricting_param_bound( &self, - mut err: &mut Diagnostic, + err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, associated_ty: Option<(&'static str, Ty<'tcx>)>, mut body_id: LocalDefId, @@ -521,7 +530,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we // don't suggest `T: Sized + ?Sized`. - while let Some(node) = self.tcx.hir().find_by_def_id(body_id) { + while let Some(node) = self.tcx.opt_hir_node_by_def_id(body_id) { match node { hir::Node::Item(hir::Item { ident, @@ -533,7 +542,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { suggest_restriction( self.tcx, body_id, - &generics, + generics, "`Self`", err, None, @@ -552,7 +561,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { assert!(param_ty); // Restricting `Self` for a single method. suggest_restriction( - self.tcx, body_id, &generics, "`Self`", err, None, projection, trait_pred, + self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred, None, ); return; @@ -575,7 +584,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { suggest_restriction( self.tcx, body_id, - &generics, + generics, "the associated type", err, Some(fn_sig), @@ -595,7 +604,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { suggest_restriction( self.tcx, body_id, - &generics, + generics, "the associated type", err, None, @@ -662,7 +671,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if suggest_constraining_type_param( self.tcx, generics, - &mut err, + err, ¶m_name, &constraint, Some(trait_pred.def_id()), @@ -690,7 +699,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if suggest_arbitrary_trait_bound( self.tcx, generics, - &mut err, + err, trait_pred, associated_ty, ) { @@ -723,7 +732,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let Some(typeck_results) = &self.typeck_results else { return false; }; - let hir::Node::Expr(expr) = self.tcx.hir().get(*arg_hir_id) else { + let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id) else { return false; }; let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) else { @@ -739,9 +748,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { real_trait_pred = parent_trait_pred; } - // We `erase_late_bound_regions` here because `make_subregion` does not handle - // `ReLateBound`, and we don't particularly care about the regions. - let real_ty = self.tcx.erase_late_bound_regions(real_trait_pred.self_ty()); + // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle + // `ReBound`, and we don't particularly care about the regions. + let real_ty = self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty()); if !self.can_eq(obligation.param_env, real_ty, arg_ty) { continue; } @@ -776,7 +785,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { kind: hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr), .. - })) = self.tcx.hir().find(*arg_hir_id) + })) = self.tcx.opt_hir_node(*arg_hir_id) { let derefs = "*".repeat(steps); err.span_suggestion_verbose( @@ -812,7 +821,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if self.predicate_may_hold(&obligation) && self.predicate_must_hold_modulo_regions(&sized_obligation) { - let call_node = self.tcx.hir().get(*call_hir_id); + let call_node = self.tcx.hir_node(*call_hir_id); let msg = "consider dereferencing here"; let is_receiver = matches!( call_node, @@ -871,7 +880,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }; let hir = self.tcx.hir(); - let hir_id = hir.local_def_id_to_hir_id(def_id.as_local()?); + let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?); match hir.find_parent(hir_id) { Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { get_name(err, &local.pat.kind) @@ -908,7 +917,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let self_ty = self.instantiate_binder_with_fresh_vars( DUMMY_SP, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, trait_pred.self_ty(), ); @@ -1034,10 +1043,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; }; - let hir::def::Res::Local(hir_id) = path.res else { + let Res::Local(hir_id) = path.res else { return; }; - let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else { + let Some(hir::Node::Pat(pat)) = self.tcx.opt_hir_node(hir_id) else { return; }; let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) = @@ -1097,7 +1106,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { else { return false; }; - let arg_node = self.tcx.hir().get(*arg_hir_id); + let arg_node = self.tcx.hir_node(*arg_hir_id); let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else { return false }; let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None); @@ -1237,7 +1246,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let output = self.instantiate_binder_with_fresh_vars( DUMMY_SP, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, output, ); let inputs = inputs @@ -1246,7 +1255,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .map(|ty| { self.instantiate_binder_with_fresh_vars( DUMMY_SP, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, inputs.rebind(*ty), ) }) @@ -1273,7 +1282,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } = obligation.cause.code() { - &parent_code + parent_code } else if let ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() { @@ -1378,14 +1387,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.message = vec![(rustc_errors::DiagnosticMessage::from(msg), Style::NoStyle)]; } + let mut file = None; err.span_label( span, format!( "the trait `{}` is not implemented for `{}`", old_pred.print_modifiers_and_trait_path(), - old_pred.self_ty().skip_binder(), + self.tcx.short_ty_string(old_pred.self_ty().skip_binder(), &mut file), ), ); + if let Some(file) = file { + err.note(format!( + "the full type name has been written to '{}'", + file.display() + )); + } if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { err.span_suggestions( @@ -1618,8 +1634,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let hir::def::Res::Local(hir_id) = path.res - && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id) + && let Res::Local(hir_id) = path.res + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(hir_id) && let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id) && let None = local.ty && let Some(binding_expr) = local.init @@ -1634,9 +1650,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) { let hir = self.tcx.hir(); - if let ObligationCauseCode::AwaitableExpr(Some(hir_id)) = - obligation.cause.code().peel_derives() - && let hir::Node::Expr(expr) = hir.get(*hir_id) + if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() + && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) { // FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()` // and if not maybe suggest doing something else? If we kept the expression around we @@ -1786,7 +1801,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let hir = self.tcx.hir(); - let node = hir.find_by_def_id(obligation.cause.body_id); + let node = self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id); if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node && let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind && sig.decl.output.span().overlaps(span) @@ -1821,9 +1836,8 @@ 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) + self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id) else { return None; }; @@ -1867,7 +1881,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(obligation.cause.body_id)); let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(&body); + visitor.visit_body(body); let mut sugg = vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())]; @@ -1915,14 +1929,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } let hir = self.tcx.hir(); - let node = hir.find_by_def_id(obligation.cause.body_id); + let node = self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id); if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) = node { let body = hir.body(*body_id); // Point at all the `return`s in the function as they have failed trait bounds. let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(&body); + visitor.visit_body(body); let typeck_results = self.typeck_results.as_ref().unwrap(); for expr in &visitor.returns { if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) { @@ -2006,6 +2020,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let signature_kind = format!("{argument_kind} signature"); err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str); + self.note_conflicting_fn_args(&mut err, cause, expected, found, param_env); self.note_conflicting_closure_bounds(cause, &mut err); if let Some(found_node) = found_node { @@ -2015,6 +2030,158 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err } + fn note_conflicting_fn_args( + &self, + err: &mut Diagnostic, + cause: &ObligationCauseCode<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) { + let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = cause else { + return; + }; + let ty::FnPtr(expected) = expected.kind() else { + return; + }; + let ty::FnPtr(found) = found.kind() else { + return; + }; + let Some(Node::Expr(arg)) = self.tcx.opt_hir_node(*arg_hir_id) else { + return; + }; + let hir::ExprKind::Path(path) = arg.kind else { + return; + }; + let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(*expected).inputs(); + let found_inputs = self.tcx.instantiate_bound_regions_with_erased(*found).inputs(); + let both_tys = expected_inputs.iter().copied().zip(found_inputs.iter().copied()); + + let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| { + let (expected_ty, expected_refs) = get_deref_type_and_refs(expected); + let (found_ty, found_refs) = get_deref_type_and_refs(found); + + if infcx.can_eq(param_env, found_ty, expected_ty) { + if found_refs.len() == expected_refs.len() + && found_refs.iter().eq(expected_refs.iter()) + { + name + } else if found_refs.len() > expected_refs.len() { + let refs = &found_refs[..found_refs.len() - expected_refs.len()]; + if found_refs[..expected_refs.len()].iter().eq(expected_refs.iter()) { + format!( + "{}{name}", + refs.iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::<Vec<_>>() + .join(""), + ) + } else { + // The refs have different mutability. + format!( + "{}*{name}", + refs.iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::<Vec<_>>() + .join(""), + ) + } + } else if expected_refs.len() > found_refs.len() { + format!( + "{}{name}", + (0..(expected_refs.len() - found_refs.len())) + .map(|_| "*") + .collect::<Vec<_>>() + .join(""), + ) + } else { + format!( + "{}{name}", + found_refs + .iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .chain(found_refs.iter().map(|_| "*".to_string())) + .collect::<Vec<_>>() + .join(""), + ) + } + } else { + format!("/* {found} */") + } + }; + let args_have_same_underlying_type = both_tys.clone().all(|(expected, found)| { + let (expected_ty, _) = get_deref_type_and_refs(expected); + let (found_ty, _) = get_deref_type_and_refs(found); + self.can_eq(param_env, found_ty, expected_ty) + }); + let (closure_names, call_names): (Vec<_>, Vec<_>) = if args_have_same_underlying_type + && !expected_inputs.is_empty() + && expected_inputs.len() == found_inputs.len() + && let Some(typeck) = &self.typeck_results + && let Res::Def(res_kind, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id) + && res_kind.is_fn_like() + { + let closure: Vec<_> = self + .tcx + .fn_arg_names(fn_def_id) + .iter() + .enumerate() + .map(|(i, ident)| { + if ident.name.is_empty() || ident.name == kw::SelfLower { + format!("arg{i}") + } else { + format!("{ident}") + } + }) + .collect(); + let args = closure + .iter() + .zip(both_tys) + .map(|(name, (expected, found))| { + arg_expr(self.infcx, name.to_owned(), expected, found) + }) + .collect(); + (closure, args) + } else { + let closure_args = expected_inputs + .iter() + .enumerate() + .map(|(i, _)| format!("arg{i}")) + .collect::<Vec<_>>(); + let call_args = both_tys + .enumerate() + .map(|(i, (expected, found))| { + arg_expr(self.infcx, format!("arg{i}"), expected, found) + }) + .collect::<Vec<_>>(); + (closure_args, call_args) + }; + let closure_names: Vec<_> = closure_names + .into_iter() + .zip(expected_inputs.iter()) + .map(|(name, ty)| { + format!( + "{name}{}", + if ty.has_infer_types() { + String::new() + } else if ty.references_error() { + ": /* type */".to_string() + } else { + format!(": {ty}") + } + ) + }) + .collect(); + err.multipart_suggestion( + "consider wrapping the function in a closure", + vec![ + (arg.span.shrink_to_lo(), format!("|{}| ", closure_names.join(", "))), + (arg.span.shrink_to_hi(), format!("({})", call_names.join(", "))), + ], + Applicability::MaybeIncorrect, + ); + } + // Add a note if there are two `Fn`-family bounds that have conflicting argument // requirements, which will always cause a closure to have a type error. fn note_conflicting_closure_bounds( @@ -2287,8 +2454,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // represent regions that are part of the suspended // coroutine frame. Bound regions are preserved by // `erase_regions` and so we must also call - // `erase_late_bound_regions`. - let ty_erased = self.tcx.erase_late_bound_regions(ty); + // `instantiate_bound_regions_with_erased`. + let ty_erased = self.tcx.instantiate_bound_regions_with_erased(ty); let ty_erased = self.tcx.erase_regions(ty_erased); let eq = ty_erased == target_ty_erased; debug!(?ty_erased, ?target_ty_erased, ?eq); @@ -2300,7 +2467,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // cycles. If we can't use resolved types because the coroutine comes from another crate, // we still provide a targeted error but without all the relevant spans. let coroutine_data = match &self.typeck_results { - Some(t) if t.hir_owner.to_def_id() == coroutine_did_root => CoroutineData(&t), + Some(t) if t.hir_owner.to_def_id() == coroutine_did_root => CoroutineData(t), _ if coroutine_did.is_local() => { CoroutineData(self.tcx.typeck(coroutine_did.expect_local())) } @@ -2344,7 +2511,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if interior_or_upvar_span.is_none() { interior_or_upvar_span = - coroutine_data.try_get_upvar_span(&self, coroutine_did, ty_matches); + coroutine_data.try_get_upvar_span(self, coroutine_did, ty_matches); } if interior_or_upvar_span.is_none() && !coroutine_did.is_local() { @@ -2415,7 +2582,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .tcx .parent(coroutine_did) .as_local() - .map(|parent_did| hir.local_def_id_to_hir_id(parent_did)) + .map(|parent_did| self.tcx.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 `{name}` is not {trait_name}") @@ -2426,11 +2593,28 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { CoroutineKind::Async(CoroutineSource::Closure) => { format!("future created by async closure is not {trait_name}") } + CoroutineKind::AsyncGen(CoroutineSource::Fn) => self + .tcx + .parent(coroutine_did) + .as_local() + .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) + .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .map(|name| { + format!("async iterator returned by `{name}` is not {trait_name}") + })?, + CoroutineKind::AsyncGen(CoroutineSource::Block) => { + format!("async iterator created by async gen block is not {trait_name}") + } + CoroutineKind::AsyncGen(CoroutineSource::Closure) => { + format!( + "async iterator created by async gen closure is not {trait_name}" + ) + } CoroutineKind::Gen(CoroutineSource::Fn) => self .tcx .parent(coroutine_did) .as_local() - .map(|parent_did| hir.local_def_id_to_hir_id(parent_did)) + .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) .map(|name| { format!("iterator returned by `{name}` is not {trait_name}") @@ -2517,7 +2701,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { CoroutineInteriorOrUpvar::Upvar(upvar_span) => { // `Some((ref_ty, is_mut))` if `target_ty` is `&T` or `&mut T` and fails to impl `Send` let non_send = match target_ty.kind() { - ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(&obligation) { + ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(obligation) { Ok(eval) if !eval.may_apply() => Some((ref_ty, mutability.is_mut())), _ => None, }, @@ -2593,11 +2777,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::MethodReceiver | ObligationCauseCode::ReturnNoExpression | ObligationCauseCode::UnifyReceiver(..) - | ObligationCauseCode::OpaqueType | ObligationCauseCode::MiscObligation | ObligationCauseCode::WellFormed(..) | ObligationCauseCode::MatchImpl(..) - | ObligationCauseCode::ReturnType | ObligationCauseCode::ReturnValue(_) | ObligationCauseCode::BlockTailExpression(..) | ObligationCauseCode::AwaitableExpr(_) @@ -2608,7 +2790,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::BinOp { .. } | ObligationCauseCode::AscribeUserTypeProvePredicate(..) | ObligationCauseCode::DropImpl - | ObligationCauseCode::ConstParam(_) => {} + | ObligationCauseCode::ConstParam(_) + | ObligationCauseCode::ReferenceOutlivesReferent(..) + | ObligationCauseCode::ObjectTypeBound(..) => {} ObligationCauseCode::RustCall => { if let Some(pred) = predicate.to_opt_poly_trait_pred() && Some(pred.def_id()) == self.tcx.lang_items().sized_trait() @@ -2622,19 +2806,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ObligationCauseCode::TupleElem => { err.note("only the last element of a tuple may have a dynamically sized type"); } - ObligationCauseCode::ProjectionWf(data) => { - err.note(format!("required so that the projection `{data}` is well-formed")); - } - ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => { - err.note(format!( - "required so that reference `{ref_ty}` does not outlive its referent" - )); - } - ObligationCauseCode::ObjectTypeBound(object_ty, region) => { - err.note(format!( - "required so that the lifetime bound of `{region}` for `{object_ty}` is satisfied", - )); - } ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::ExprItemObligation(..) => { // We hold the `DefId` of the item introducing the obligation, but displaying it @@ -2723,11 +2894,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .collect::<Vec<_>>(); if !impls.is_empty() { let len = impls.len(); - let mut types = impls.iter() - .map(|t| with_no_trimmed_paths!(format!( - " {}", - tcx.type_of(*t).instantiate_identity(), - ))) + let mut types = impls + .iter() + .map(|t| { + with_no_trimmed_paths!(format!( + " {}", + tcx.type_of(*t).instantiate_identity(), + )) + }) .collect::<Vec<_>>(); let post = if types.len() > 9 { types.truncate(8); @@ -2749,50 +2923,62 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::Coercion { source, target } => { - let (source, source_file) = - self.tcx.short_ty_string(self.resolve_vars_if_possible(source)); - let (target, target_file) = - self.tcx.short_ty_string(self.resolve_vars_if_possible(target)); + let mut file = None; + let source = + self.tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file); + let target = + self.tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file); err.note(with_forced_trimmed_paths!(format!( "required for the cast from `{source}` to `{target}`", ))); - if let Some(file) = source_file { - err.note(format!( - "the full name for the source type has been written to '{}'", - file.display(), - )); - } - if let Some(file) = target_file { + if let Some(file) = file { err.note(format!( - "the full name for the target type has been written to '{}'", + "the full name for the type has been written to '{}'", file.display(), )); } } - ObligationCauseCode::RepeatElementCopy { is_constable, elt_type, elt_span, elt_stmt_span } => { + ObligationCauseCode::RepeatElementCopy { + is_constable, + elt_type, + elt_span, + elt_stmt_span, + } => { err.note( "the `Copy` trait is required because this value will be copied for each element of the array", ); let value_kind = match is_constable { IsConstable::Fn => Some("the result of the function call"), IsConstable::Ctor => Some("the result of the constructor"), - _ => None + _ => None, }; let sm = tcx.sess.source_map(); - if let Some(value_kind) = value_kind && - let Ok(snip) = sm.span_to_snippet(elt_span) + if let Some(value_kind) = value_kind + && let Ok(snip) = sm.span_to_snippet(elt_span) { let help_msg = format!( "consider creating a new `const` item and initializing it with {value_kind} \ - to be used in the repeat position"); + to be used in the repeat position" + ); let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default(); - err.multipart_suggestion(help_msg, vec![ - (elt_stmt_span.shrink_to_lo(), format!("const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}")), - (elt_span, "ARRAY_REPEAT_VALUE".to_string()) - ], Applicability::MachineApplicable); + err.multipart_suggestion( + help_msg, + vec![ + ( + elt_stmt_span.shrink_to_lo(), + format!( + "const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}" + ), + ), + (elt_span, "ARRAY_REPEAT_VALUE".to_string()), + ], + Applicability::MachineApplicable, + ); } - if self.tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn|IsConstable::Ctor) { + if self.tcx.sess.is_nightly_build() + && matches!(is_constable, IsConstable::Fn | IsConstable::Ctor) + { err.help( "create an inline `const` block, see RFC #2920 \ <https://github.com/rust-lang/rfcs/pull/2920> for more information", @@ -2801,7 +2987,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::VariableType(hir_id) => { let parent_node = self.tcx.hir().parent_id(hir_id); - match self.tcx.hir().find(parent_node) { + match self.tcx.opt_hir_node(parent_node) { Some(Node::Local(hir::Local { ty: Some(ty), .. })) => { err.span_suggestion_verbose( ty.span.shrink_to_lo(), @@ -2945,7 +3131,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "all values captured by value by a closure must have a statically known size", ); let hir::ExprKind::Closure(closure) = - self.tcx.hir().get_by_def_id(closure_def_id).expect_expr().kind + self.tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind else { bug!("expected closure in SizedClosureCapture obligation"); }; @@ -2957,16 +3143,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => { let what = match self.tcx.coroutine_kind(coroutine_def_id) { - None | Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) => "yield", + None + | Some(hir::CoroutineKind::Coroutine) + | Some(hir::CoroutineKind::Gen(_)) => "yield", Some(hir::CoroutineKind::Async(..)) => "await", + Some(hir::CoroutineKind::AsyncGen(_)) => "yield`/`await", }; err.note(format!( "all values live across `{what}` must have a statically known size" )); } - ObligationCauseCode::ConstPatternStructural => { - err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`"); - } ObligationCauseCode::SharedStatic => { err.note("shared static variables must have a type that implements `Sync`"); } @@ -3000,9 +3186,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Don't print the tuple of capture types 'print: { if !is_upvar_tys_infer_tuple { - let msg = with_forced_trimmed_paths!(format!( - "required because it appears within the type `{ty}`", - )); + let mut file = None; + let ty_str = self.tcx.short_ty_string(ty, &mut file); + let msg = format!("required because it appears within the type `{ty_str}`"); match ty.kind() { ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) { Some(ident) => err.span_note(ident.span, msg), @@ -3099,8 +3285,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut parent_trait_pred = self.resolve_vars_if_possible(data.derived.parent_trait_pred); 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()); + let mut file = None; + let self_ty = + self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); let msg = format!( "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() @@ -3197,8 +3384,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { count, pluralize!(count) )); - let (self_ty, file) = - self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty()); + let mut file = None; + let self_ty = self + .tcx + .short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); err.note(format!( "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() @@ -3283,7 +3472,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err, predicate, param_env, - &parent_code, + parent_code, obligated_types, seen_requirements, ) @@ -3352,7 +3541,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); let impls_future = self.type_implements_trait( future_trait, - [self.tcx.erase_late_bound_regions(self_ty)], + [self.tcx.instantiate_bound_regions_with_erased(self_ty)], obligation.param_env, ); if !impls_future.must_apply_modulo_regions() { @@ -3443,17 +3632,19 @@ 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, args); + let field_ty = ty::GenericArg::from(field.ty(self.tcx, args)); let trait_args = match diagnostic_name { sym::PartialEq | sym::PartialOrd => { Some(field_ty) } _ => None, }; + // Also add host param, if present + let host = self.tcx.generics_of(trait_pred.def_id()).host_effect_index.map(|idx| trait_pred.skip_binder().trait_ref.args[idx]); 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_args), + [field_ty].into_iter().chain(trait_args).chain(host), ), ..*tr }); @@ -3474,6 +3665,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_pred.skip_binder().self_ty(), diagnostic_name, ), + // FIXME(effects, const_trait_impl) derive_const as suggestion? format!("#[derive({diagnostic_name})]\n"), Applicability::MaybeIncorrect, ); @@ -3513,13 +3705,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { call_hir_id: HirId, ) { let tcx = self.tcx; - let hir = tcx.hir(); - if let Some(Node::Expr(expr)) = hir.find(arg_hir_id) + if let Some(Node::Expr(expr)) = tcx.opt_hir_node(arg_hir_id) && let Some(typeck_results) = &self.typeck_results { if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr { let inner_expr = expr.peel_blocks(); - let ty = typeck_results.expr_ty_adjusted_opt(inner_expr) + let ty = typeck_results + .expr_ty_adjusted_opt(inner_expr) .unwrap_or(Ty::new_misc_error(tcx)); let span = inner_expr.span; if Some(span) != err.span.primary_span() { @@ -3538,16 +3730,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { tcx.lang_items().fn_once_trait(), tcx.lang_items().fn_mut_trait(), tcx.lang_items().fn_trait(), - ].contains(&Some(pred.def_id())) + ] + .contains(&Some(pred.def_id())) { if let [stmt, ..] = block.stmts && let hir::StmtKind::Semi(value) = stmt.kind && let hir::ExprKind::Closure(hir::Closure { - body, - fn_decl_span, - .. + body, fn_decl_span, .. }) = value.kind - && let body = hir.body(*body) + && let body = tcx.hir().body(*body) && !matches!(body.value.kind, hir::ExprKind::Block(..)) { // Check if the failed predicate was an expectation of a closure type @@ -3568,9 +3759,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "you might have meant to create the closure instead of a block", format!( "|{}| ", - (0..pred.trait_ref.args.len() - 1).map(|_| "_") + (0..pred.trait_ref.args.len() - 1) + .map(|_| "_") .collect::<Vec<_>>() - .join(", ")), + .join(", ") + ), Applicability::MaybeIncorrect, ); } @@ -3595,7 +3788,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let where_pred = self.instantiate_binder_with_placeholders(where_pred); let failed_pred = self.instantiate_binder_with_fresh_vars( expr.span, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, failed_pred, ); @@ -3627,21 +3820,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path - && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) + && let hir::Path { res: Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id) && let parent_hir_id = self.tcx.hir().parent_id(binding.hir_id) - && let Some(hir::Node::Local(local)) = self.tcx.hir().find(parent_hir_id) + && let Some(hir::Node::Local(local)) = self.tcx.opt_hir_node(parent_hir_id) && let Some(binding_expr) = local.init { // If the expression we're calling on is a binding, we want to point at the // `let` when talking about the type. Otherwise we'll point at every part // of the method chain with the type. - self.point_at_chain(binding_expr, &typeck_results, type_diffs, param_env, err); + self.point_at_chain(binding_expr, typeck_results, type_diffs, param_env, err); } else { - self.point_at_chain(expr, &typeck_results, type_diffs, param_env, err); + self.point_at_chain(expr, typeck_results, type_diffs, param_env, err); } } - let call_node = hir.find(call_hir_id); + let call_node = tcx.opt_hir_node(call_hir_id); if let Some(Node::Expr(hir::Expr { kind: hir::ExprKind::MethodCall(path, rcvr, ..), .. })) = call_node @@ -3651,7 +3844,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - if let Some(Node::Expr(expr)) = hir.find(call_hir_id) { + if let Some(Node::Expr(expr)) = tcx.opt_hir_node(call_hir_id) { if let hir::ExprKind::Call(hir::Expr { span, .. }, _) | hir::ExprKind::MethodCall( hir::PathSegment { ident: Ident { span, .. }, .. }, @@ -3812,7 +4005,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { continue; }; let hir = tcx.hir(); - let node = hir.get_by_def_id(hir.get_parent_item(expr.hir_id).def_id); + let node = tcx.hir_node_by_def_id(hir.get_parent_item(expr.hir_id).def_id); let pred = ty::Binder::dummy(ty::TraitPredicate { trait_ref: ty::TraitRef::from_lang_item( @@ -3832,7 +4025,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { suggest_restriction( tcx, hir.body_owner_def_id(body_id), - &generics, + generics, &format!("type parameter `{ty}`"), err, node.fn_sig(), @@ -3887,8 +4080,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path - && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) + && let hir::Path { res: Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id) && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) { // We've reached the root of the method call chain... @@ -4267,7 +4460,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } return; }; - let Some(hir::Node::TraitItem(item)) = self.tcx.hir().find_by_def_id(fn_def_id) else { + let Some(hir::Node::TraitItem(item)) = self.tcx.opt_hir_node_by_def_id(fn_def_id) else { return; }; @@ -4342,17 +4535,6 @@ fn hint_missing_borrow<'tcx>( let args = fn_decl.inputs.iter(); - fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { - let mut refs = vec![]; - - while let ty::Ref(_, new_ty, mutbl) = ty.kind() { - ty = *new_ty; - refs.push(*mutbl); - } - - (ty, refs) - } - let mut to_borrow = Vec::new(); let mut remove_borrow = Vec::new(); @@ -4483,7 +4665,7 @@ pub trait NextTypeParamName { impl NextTypeParamName for &[hir::GenericParam<'_>] { fn next_type_param_name(&self, name: Option<&str>) -> String { // This is the list of possible parameter names that we might suggest. - let name = name.and_then(|n| n.chars().next()).map(|c| c.to_string().to_uppercase()); + let name = name.and_then(|n| n.chars().next()).map(|c| c.to_uppercase().to_string()); let name = name.as_deref(); let possible_names = [name.unwrap_or("T"), "T", "U", "V", "X", "Y", "Z", "A", "B", "C"]; let used_names = self @@ -4512,7 +4694,7 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) { if let hir::TyKind::Path(hir::QPath::Resolved( None, - hir::Path { res: hir::def::Res::Def(_, segment_did), .. }, + hir::Path { res: Res::Def(_, segment_did), .. }, )) = t.kind { if self.param_did == *segment_did { @@ -4530,6 +4712,7 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { } pub(super) fn get_explanation_based_on_obligation<'tcx>( + tcx: TyCtxt<'tcx>, obligation: &PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, trait_predicate: &ty::PolyTraitPredicate<'tcx>, @@ -4550,13 +4733,13 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( pre_message, trait_predicate.print_modifiers_and_trait_path(), desc, - trait_ref.skip_binder().self_ty(), + tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None), ), None => format!( "{}the trait `{}` is not implemented for `{}`", pre_message, trait_predicate.print_modifiers_and_trait_path(), - trait_ref.skip_binder().self_ty(), + tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None), ), } } @@ -4599,9 +4782,15 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( return None; }; - let future = tcx.hir().get_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); - let Some(hir::GenericBound::LangItemTrait(_, _, _, generics)) = future.bounds.get(0) else { - // `async fn` should always lower to a lang item bound... but don't ICE. + let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else { + // `async fn` should always lower to a single bound... but don't ICE. + return None; + }; + let Some(hir::PathSegment { args: Some(generics), .. }) = + trait_ref.trait_ref.path.segments.last() + else { + // desugaring to a single path segment for `Future<...>`. return None; }; let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) = @@ -4645,3 +4834,14 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( Some(sugg) } + +fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { + let mut refs = vec![]; + + while let ty::Ref(_, new_ty, mutbl) = ty.kind() { + ty = *new_ty; + refs.push(*mutbl); + } + + (ty, refs) +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index ba2e3d1ae..9ee091bbd 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -3,6 +3,7 @@ use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as 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::InferCtxtExt as _; use crate::infer::{self, InferCtxt}; use crate::traits::error_reporting::infer_ctxt_ext::InferCtxtExt; use crate::traits::error_reporting::{ambiguity, ambiguity::Ambiguity::*}; @@ -36,11 +37,11 @@ use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, }; -use rustc_session::config::{DumpSolverProofTree, TraitSolver}; +use rustc_session::config::DumpSolverProofTree; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; -use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use rustc_span::{BytePos, ExpnKind, Span, Symbol, DUMMY_SP}; use std::borrow::Cow; use std::fmt; use std::iter; @@ -98,8 +99,21 @@ pub trait TypeErrCtxtExt<'tcx> { error: &SelectionError<'tcx>, ); + fn emit_specialized_closure_kind_error( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Option<ErrorGuaranteed>; + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool; + fn try_conversion_context( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + err: &mut Diagnostic, + ) -> bool; + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, @@ -212,7 +226,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - self.tcx.sess.delay_span_bug(DUMMY_SP, "expected fulfillment errors") + self.tcx.sess.span_delayed_bug(DUMMY_SP, "expected fulfillment errors") } /// Reports that an overflow has occurred and halts compilation. We @@ -236,7 +250,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.emit(); self.tcx.sess.abort_if_errors(); - bug!(); + // FIXME: this should be something like `build_overflow_error_fatal`, which returns + // `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`. + unreachable!( + "did not expect compilation to continue after `abort_if_errors`, \ + since an error was definitely emitted!" + ); } fn build_overflow_error<T>( @@ -356,14 +375,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) { let tcx = self.tcx; - if tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + if tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() + == DumpSolverProofTree::OnError + { dump_proof_tree(root_obligation, self.infcx); } let mut span = obligation.cause.span; // FIXME: statically guarantee this by tainting after the diagnostic is emitted self.set_tainted_by_errors( - tcx.sess.delay_span_bug(span, "`report_selection_error` did not emit an error"), + tcx.sess.span_delayed_bug(span, "`report_selection_error` did not emit an error"), ); let mut err = match *error { @@ -411,6 +432,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => { let trait_predicate = bound_predicate.rebind(trait_predicate); let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + let trait_ref = trait_predicate.to_poly_trait_ref(); + + if let Some(_guar) = self.emit_specialized_closure_kind_error(&obligation, trait_ref) { + return; + } // FIXME(effects) let predicate_is_const = false; @@ -425,22 +451,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // reported on the binding definition (#56607). return; } - let trait_ref = trait_predicate.to_poly_trait_ref(); - let (post_message, pre_message, type_def, file_note) = self + let mut file = None; + let (post_message, pre_message, type_def) = self .get_parent_trait_ref(obligation.cause.code()) .map(|(t, s)| { - let (t, file) = self.tcx.short_ty_string(t); + let t = self.tcx.short_ty_string(t, &mut file); ( format!(" in `{t}`"), format!("within `{t}`, "), s.map(|s| (format!("within this `{t}`"), s)), - file.map(|file| format!( - "the full trait has been written to '{}'", - file.display(), - )) ) }) .unwrap_or_default(); + let file_note = file.map(|file| format!( + "the full trait has been written to '{}'", + file.display(), + )); let OnUnimplementedNote { message, @@ -499,6 +525,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut err = struct_span_err!(self.tcx.sess, span, E0277, "{}", err_msg); + let mut suggested = false; + if is_try_conversion { + suggested = self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err); + } + if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { err.span_label( ret_span, @@ -511,7 +542,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() { self.add_tuple_trait_message( - &obligation.cause.code().peel_derives(), + obligation.cause.code().peel_derives(), &mut err, ); } @@ -524,6 +555,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } let explanation = get_explanation_based_on_obligation( + self.tcx, &obligation, trait_ref, &trait_predicate, @@ -569,7 +601,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { self.suggest_borrowing_for_object_cast( &mut err, - &root_obligation, + root_obligation, source, target, ); @@ -599,8 +631,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate); - let mut suggested = - self.suggest_dereferences(&obligation, &mut err, trait_predicate); + suggested |= self.suggest_dereferences(&obligation, &mut err, trait_predicate); suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); let impl_candidates = self.find_similar_impl_candidates(trait_predicate); suggested = if let &[cand] = &impl_candidates[..] { @@ -612,7 +643,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span.shrink_to_hi(), format!( "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`", - cand.print_only_trait_path(), + cand.print_trait_sugared(), cand.self_ty(), ), format!(" as {}", cand.self_ty()), @@ -786,30 +817,22 @@ 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_args, kind) => { - let found_kind = self.closure_kind(closure_args).unwrap(); - self.report_closure_error(&obligation, closure_def_id, found_kind, kind) - } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => { let ty = self.resolve_vars_if_possible(ty); - match self.tcx.sess.opts.unstable_opts.trait_solver { - TraitSolver::Classic => { - // WF predicates cannot themselves make - // errors. They can only block due to - // ambiguity; otherwise, they always - // degenerate into other obligations - // (which may fail). - span_bug!(span, "WF predicate not satisfied for {:?}", ty); - } - TraitSolver::Next | TraitSolver::NextCoherence => { - // FIXME: we'll need a better message which takes into account - // which bounds actually failed to hold. - self.tcx.sess.struct_span_err( - span, - format!("the type `{ty}` is not well-formed"), - ) - } + if self.next_trait_solver() { + // FIXME: we'll need a better message which takes into account + // which bounds actually failed to hold. + self.tcx.sess.struct_span_err( + span, + format!("the type `{ty}` is not well-formed"), + ) + } else { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); } } @@ -837,6 +860,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"), + ty::PredicateKind::NormalizesTo(..) => span_bug!( + span, + "NormalizesTo predicate should never be the predicate cause of a SelectionError" + ), + ty::PredicateKind::AliasRelate(..) => span_bug!( span, "AliasRelate predicate should never be the predicate cause of a SelectionError" @@ -927,18 +955,48 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.emit(); } + fn emit_specialized_closure_kind_error( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Option<ErrorGuaranteed> { + if let ty::Closure(closure_def_id, closure_args) = *trait_ref.self_ty().skip_binder().kind() + && let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id()) + && let Some(found_kind) = self.closure_kind(closure_args) + && !found_kind.extends(expected_kind) + && let sig = closure_args.as_closure().sig() + && self.can_sub( + obligation.param_env, + trait_ref, + sig.map_bound(|sig| { + ty::TraitRef::new( + self.tcx, + trait_ref.def_id(), + [trait_ref.self_ty().skip_binder(), sig.inputs()[0]], + ) + }), + ) + { + let mut err = + self.report_closure_error(&obligation, closure_def_id, found_kind, expected_kind); + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + Some(err.emit()) + } else { + None + } + } + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool { - if let ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id, - .. - } = obligation.cause.code() - && let Some(Node::Expr(arg)) = self.tcx.hir().find(*arg_hir_id) + if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = + obligation.cause.code() + && let Some(Node::Expr(arg)) = self.tcx.opt_hir_node(*arg_hir_id) && let arg = arg.peel_borrows() && let hir::ExprKind::Path(hir::QPath::Resolved( None, hir::Path { res: hir::def::Res::Local(hir_id), .. }, )) = arg.kind - && let Some(Node::Pat(pat)) = self.tcx.hir().find(*hir_id) + && let Some(Node::Pat(pat)) = self.tcx.opt_hir_node(*hir_id) && let Some(preds) = self.reported_trait_errors.borrow().get(&pat.span) && preds.contains(&obligation.predicate) { @@ -947,6 +1005,223 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { false } + /// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`, + /// identify thoe method chain sub-expressions that could or could not have been annotated + /// with `?`. + fn try_conversion_context( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + err: &mut Diagnostic, + ) -> bool { + let span = obligation.cause.span; + struct V<'v> { + search_span: Span, + found: Option<&'v hir::Expr<'v>>, + } + impl<'v> Visitor<'v> for V<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind + { + if ex.span.with_lo(ex.span.hi() - BytePos(1)).source_equal(self.search_span) { + if let hir::ExprKind::Call(_, [expr, ..]) = expr.kind { + self.found = Some(expr); + return; + } + } + } + hir::intravisit::walk_expr(self, ex); + } + } + let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id); + let body_id = match self.tcx.opt_hir_node(hir_id) { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => { + body_id + } + _ => return false, + }; + let mut v = V { search_span: span, found: None }; + v.visit_body(self.tcx.hir().body(*body_id)); + let Some(expr) = v.found else { + return false; + }; + let Some(typeck) = &self.typeck_results else { + return false; + }; + let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent() + else { + return false; + }; + if !self.tcx.is_diagnostic_item(sym::FromResidual, y.def_id()) { + return false; + } + let self_ty = trait_ref.self_ty(); + let found_ty = trait_ref.args.get(1).and_then(|a| a.as_type()); + + let mut prev_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + + // We always look at the `E` type, because that's the only one affected by `?`. If the + // incorrect `Result<T, E>` is because of the `T`, we'll get an E0308 on the whole + // expression, after the `?` has "unwrapped" the `T`. + let get_e_type = |prev_ty: Ty<'tcx>| -> Option<Ty<'tcx>> { + let ty::Adt(def, args) = prev_ty.kind() else { + return None; + }; + let Some(arg) = args.get(1) else { + return None; + }; + if !self.tcx.is_diagnostic_item(sym::Result, def.did()) { + return None; + } + arg.as_type() + }; + + let mut suggested = false; + let mut chain = vec![]; + + // The following logic is simlar to `point_at_chain`, but that's focused on associated types + let mut expr = expr; + while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind { + // Point at every method call in the chain with the `Result` type. + // let foo = bar.iter().map(mapper)?; + // ------ ----------- + expr = rcvr_expr; + chain.push((span, prev_ty)); + + let next_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + + let is_diagnostic_item = |symbol: Symbol, ty: Ty<'tcx>| { + let ty::Adt(def, _) = ty.kind() else { + return false; + }; + self.tcx.is_diagnostic_item(symbol, def.did()) + }; + // For each method in the chain, see if this is `Result::map_err` or + // `Option::ok_or_else` and if it is, see if the closure passed to it has an incorrect + // trailing `;`. + if let Some(ty) = get_e_type(prev_ty) + && let Some(found_ty) = found_ty + // Ideally we would instead use `FnCtxt::lookup_method_for_diagnostic` for 100% + // accurate check, but we are in the wrong stage to do that and looking for + // `Result::map_err` by checking the Self type and the path segment is enough. + // sym::ok_or_else + && ( + ( // Result::map_err + path_segment.ident.name == sym::map_err + && is_diagnostic_item(sym::Result, next_ty) + ) || ( // Option::ok_or_else + path_segment.ident.name == sym::ok_or_else + && is_diagnostic_item(sym::Option, next_ty) + ) + ) + // Found `Result<_, ()>?` + && let ty::Tuple(tys) = found_ty.kind() + && tys.is_empty() + // The current method call returns `Result<_, ()>` + && self.can_eq(obligation.param_env, ty, found_ty) + // There's a single argument in the method call and it is a closure + && args.len() == 1 + && let Some(arg) = args.get(0) + && let hir::ExprKind::Closure(closure) = arg.kind + // The closure has a block for its body with no tail expression + && let body = self.tcx.hir().body(closure.body) + && let hir::ExprKind::Block(block, _) = body.value.kind + && let None = block.expr + // The last statement is of a type that can be converted to the return error type + && let [.., stmt] = block.stmts + && let hir::StmtKind::Semi(expr) = stmt.kind + && let expr_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr) + .unwrap_or(Ty::new_misc_error(self.tcx)), + ) + && self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty, expr_ty], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + suggested = true; + err.span_suggestion_short( + stmt.span.with_lo(expr.span.hi()), + "remove this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + + prev_ty = next_ty; + + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id) + && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) + { + // We've reached the root of the method call chain... + if let hir::Node::Local(local) = parent + && let Some(binding_expr) = local.init + { + // ...and it is a binding. Get the binding creation and continue the chain. + expr = binding_expr; + } + if let hir::Node::Param(_param) = parent { + // ...and it is a an fn argument. + break; + } + } + } + // `expr` is now the "root" expression of the method call chain, which can be any + // expression kind, like a method call or a path. If this expression is `Result<T, E>` as + // well, then we also point at it. + prev_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + chain.push((expr.span, prev_ty)); + + let mut prev = None; + for (span, err_ty) in chain.into_iter().rev() { + let err_ty = get_e_type(err_ty); + let err_ty = match (err_ty, prev) { + (Some(err_ty), Some(prev)) if !self.can_eq(obligation.param_env, err_ty, prev) => { + err_ty + } + (Some(err_ty), None) => err_ty, + _ => { + prev = err_ty; + continue; + } + }; + if self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty, err_ty], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + if !suggested { + err.span_label(span, format!("this has type `Result<_, {err_ty}>`")); + } + } else { + err.span_label( + span, + format!( + "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`", + ), + ); + } + prev = Some(err_ty); + } + suggested + } + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, @@ -1291,7 +1566,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) { - if self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + if self.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() + == DumpSolverProofTree::OnError + { dump_proof_tree(&error.root_obligation, self.infcx); } @@ -1377,7 +1654,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { let data = self.instantiate_binder_with_fresh_vars( obligation.cause.span, - infer::LateBoundRegionConversionTime::HigherRankedType, + infer::BoundRegionConversionTime::HigherRankedType, bound_predicate.rebind(data), ); let unnormalized_term = match data.term.unpack() { @@ -1397,7 +1674,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) .into(), }; - // FIXME(-Ztrait-solver=next): For diagnostic purposes, it would be nice + // FIXME(-Znext-solver): For diagnostic purposes, it would be nice // to deeply normalize this type. let normalized_term = ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); @@ -1413,7 +1690,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::ExprItemObligation(..) | ObligationCauseCode::ExprBindingObligation(..) | ObligationCauseCode::Coercion { .. } - | ObligationCauseCode::OpaqueType ); // constrain inference variables a bit more to nested obligations from normalize so @@ -1653,6 +1929,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { hir::CoroutineKind::Async(hir::CoroutineSource::Block) => "an async block", hir::CoroutineKind::Async(hir::CoroutineSource::Fn) => "an async function", hir::CoroutineKind::Async(hir::CoroutineSource::Closure) => "an async closure", + hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Block) => "an async gen block", + hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Fn) => "an async gen function", + hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Closure) => "an async gen closure", hir::CoroutineKind::Gen(hir::CoroutineSource::Block) => "a gen block", hir::CoroutineKind::Gen(hir::CoroutineSource::Fn) => "a gen function", hir::CoroutineKind::Gen(hir::CoroutineSource::Closure) => "a gen closure", @@ -1751,7 +2030,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()), }); err.highlighted_help(vec![ - (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle), + (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle), ("is".to_string(), Style::Highlight), (" implemented for `".to_string(), Style::NoStyle), (cand.self_ty().to_string(), Style::Highlight), @@ -1787,7 +2066,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { _ => (" implemented for `", ""), }; err.highlighted_help(vec![ - (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle), + (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle), ("is".to_string(), Style::Highlight), (desc.to_string(), Style::NoStyle), (cand.self_ty().to_string(), Style::Highlight), @@ -1820,7 +2099,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let end = if candidates.len() <= 9 { candidates.len() } else { 8 }; err.help(format!( "the following {other}types implement trait `{}`:{}{}", - trait_ref.print_only_trait_path(), + trait_ref.print_trait_sugared(), candidates[..end].join(""), if candidates.len() > 9 { format!("\nand {} others", candidates.len() - 8) @@ -1967,7 +2246,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { - self.get_parent_trait_ref(&parent_code) + self.get_parent_trait_ref(parent_code) } _ => None, } @@ -2182,7 +2461,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) { let mut expr_finder = FindExprBySpan::new(span); - expr_finder.visit_expr(&self.tcx.hir().body(body_id).value); + expr_finder.visit_expr(self.tcx.hir().body(body_id).value); if let Some(hir::Expr { kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), @@ -2228,7 +2507,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ident: trait_name, kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), .. - })) = self.tcx.hir().find_by_def_id(local_def_id) + })) = self.tcx.opt_hir_node_by_def_id(local_def_id) && let Some(method_ref) = trait_item_refs .iter() .find(|item_ref| item_ref.ident == *assoc_item_name) @@ -2731,7 +3010,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Some(format!("{cannot_do_this} in const contexts")) } // overridden post message - (true, Some(AppendConstMessage::Custom(custom_msg))) => { + (true, Some(AppendConstMessage::Custom(custom_msg, _))) => { Some(format!("{cannot_do_this}{custom_msg}")) } // fallback to generic message @@ -2752,7 +3031,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { use rustc_transmute::Answer; // Erase regions because layout code doesn't particularly care about regions. - let trait_ref = self.tcx.erase_regions(self.tcx.erase_late_bound_regions(trait_ref)); + let trait_ref = + self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); let src_and_dst = rustc_transmute::Types { dst: trait_ref.args.type_at(0), @@ -2933,7 +3213,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { obligation.param_env, ) { self.report_similar_impl_candidates_for_root_obligation( - &obligation, + obligation, *trait_predicate, body_def_id, err, @@ -3040,18 +3320,18 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Additional context information explaining why the closure only implements // a particular trait. if let Some(typeck_results) = &self.typeck_results { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); + let hir_id = self.tcx.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.fn_once_label = Some(ClosureFnOnceLabel { span: *span, - place: ty::place_to_string_for_capture(self.tcx, &place), + place: ty::place_to_string_for_capture(self.tcx, place), }) } (ty::ClosureKind::FnMut, Some((span, place))) => { err.fn_mut_label = Some(ClosureFnMutLabel { span: *span, - place: ty::place_to_string_for_capture(self.tcx, &place), + place: ty::place_to_string_for_capture(self.tcx, place), }) } _ => {} @@ -3115,7 +3395,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }; if let Some(diag) = - self.tcx.sess.diagnostic().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle) + self.tcx.sess.dcx().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle) { diag.cancel(); } @@ -3163,7 +3443,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut not_tupled = false; let found = match found_trait_ref.skip_binder().args.type_at(1).kind() { - ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], + ty::Tuple(tys) => vec![ArgKind::empty(); tys.len()], _ => { not_tupled = true; vec![ArgKind::empty()] @@ -3172,7 +3452,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let expected_ty = expected_trait_ref.skip_binder().args.type_at(1); let expected = match expected_ty.kind() { - ty::Tuple(ref tys) => { + ty::Tuple(tys) => { tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect() } _ => { @@ -3253,20 +3533,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { - let ty::ConstKind::Unevaluated(uv) = ct.kind() else { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + let mut err = + self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); + let const_span = self.tcx.def_span(uv.def); + match self.tcx.sess.source_map().span_to_snippet(const_span) { + Ok(snippet) => err.help(format!( + "try adding a `where` bound using this expression: `where [(); {snippet}]:`" + )), + _ => err.help("consider adding a `where` bound using this expression"), + }; + Some(err) + } + ty::ConstKind::Expr(_) => { + let err = self + .tcx + .sess + .struct_span_err(span, format!("unconstrained generic constant `{ct}`")); + Some(err) + } + _ => { bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); - }; - let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); - let const_span = self.tcx.def_span(uv.def); - match self.tcx.sess.source_map().span_to_snippet(const_span) { - Ok(snippet) => err.help(format!( - "try adding a `where` bound using this expression: `where [(); {snippet}]:`" - )), - _ => err.help("consider adding a `where` bound using this expression"), - }; - Some(err) - } + } + }, _ => { span_bug!( span, diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index fb9cf51b5..045d7e444 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1,4 +1,5 @@ use crate::infer::{InferCtxt, TyOrConstInferVar}; +use crate::traits::error_reporting::TypeErrCtxtExt; use rustc_data_structures::captures::Captures; use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; @@ -115,12 +116,13 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { fn register_predicate_obligation( &mut self, infcx: &InferCtxt<'tcx>, - obligation: PredicateObligation<'tcx>, + mut obligation: PredicateObligation<'tcx>, ) { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); // this helps to reduce duplicate errors, as well as making // debug output much nicer to read and so on. - let obligation = infcx.resolve_vars_if_possible(obligation); + debug_assert!(!obligation.param_env.has_non_region_infer()); + obligation.predicate = infcx.resolve_vars_if_possible(obligation.predicate); debug!(?obligation, "register_predicate_obligation"); @@ -350,7 +352,6 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) | ty::PredicateKind::ObjectSafe(_) - | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) @@ -360,8 +361,11 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)])) } ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, + ty::PredicateKind::NormalizesTo(..) => { + bug!("NormalizesTo is only used by the new solver") + } ty::PredicateKind::AliasRelate(..) => { - bug!("AliasRelate is only used for new solver") + bug!("AliasRelate is only used by the new solver") } }, Some(pred) => match pred { @@ -411,17 +415,30 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } - 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![]) - } else { - ProcessResult::Error(CodeSelectionError(Unimplemented)) - } - } - None => ProcessResult::Unchanged, - } + ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, + ty::PredicateKind::NormalizesTo(..) => { + bug!("NormalizesTo is only used by the new solver") + } + ty::PredicateKind::AliasRelate(..) => { + bug!("AliasRelate is only used by the new solver") + } + + // General case overflow check. Allow `process_trait_obligation` + // and `process_projection_obligation` to handle checking for + // the recursion limit themselves. Also don't check some + // predicate kinds that don't give further obligations. + _ if !self + .selcx + .tcx() + .recursion_limit() + .value_within_limit(obligation.recursion_depth) => + { + self.selcx.infcx.err_ctxt().report_overflow_error( + &obligation.predicate, + obligation.cause.span, + false, + |_| {}, + ); } ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { @@ -454,7 +471,12 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)]; ProcessResult::Unchanged } - Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Ok(Ok(mut ok)) => { + for subobligation in &mut ok.obligations { + subobligation.set_depth_from_parent(obligation.recursion_depth); + } + ProcessResult::Changed(mk_pending(ok.obligations)) + } Ok(Err(err)) => { let expected_found = ExpectedFound::new(subtype.a_is_expected, subtype.a, subtype.b); @@ -625,10 +647,6 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } } - ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, - ty::PredicateKind::AliasRelate(..) => { - bug!("AliasRelate is only used for new solver") - } ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( DefineOpaqueTypes::No, diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index ab07b10c6..cf4fa2337 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -9,7 +9,7 @@ use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_infer::traits::query::NoSolution; use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError}; -use rustc_middle::ty::{self, AdtDef, GenericArg, List, ParamEnv, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, AdtDef, GenericArg, List, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::DUMMY_SP; use super::outlives_bounds::InferCtxtExt; @@ -173,7 +173,7 @@ pub fn all_fields_implement_trait<'tcx>( // between expected and found const-generic types. Don't report an // additional copy error here, since it's not typically useful. if !normalization_errors.is_empty() || ty.references_error() { - tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking {tr} implementation", tr = tcx.def_path_str(trait_def_id))); + tcx.sess.span_delayed_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking {tr} implementation", tr = tcx.def_path_str(trait_def_id))); continue; } @@ -209,10 +209,10 @@ pub fn all_fields_implement_trait<'tcx>( pub fn check_tys_might_be_eq<'tcx>( tcx: TyCtxt<'tcx>, - canonical: Canonical<'tcx, (ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>, + canonical: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, ) -> Result<(), NoSolution> { - let (infcx, (param_env, ty_a, ty_b), _) = - tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical); + let (infcx, key, _) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical); + let (param_env, (ty_a, ty_b)) = key.into_parts(); let ocx = ObligationCtxt::new(&infcx); let result = ocx.eq(&ObligationCause::dummy(), param_env, ty_a, ty_b); diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index fff5510bb..8c5f1e907 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -215,7 +215,7 @@ fn do_normalize_predicates<'tcx>( // the normalized predicates. let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( span, format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"), ); @@ -315,7 +315,7 @@ pub fn normalize_param_env_or_error<'tcx>( // We do not normalize types here as there is no backwards compatibility requirement // for us to do so. // - // FIXME(-Ztrait-solver=next): remove this hack since we have deferred projection equality + // FIXME(-Znext-solver): remove this hack since we have deferred projection equality predicate.fold_with(&mut ConstNormalizer(tcx)) }), ) @@ -386,7 +386,7 @@ pub fn normalize_param_env_or_error<'tcx>( /// Normalize a type and process all resulting obligations, returning any errors. /// -/// FIXME(-Ztrait-solver=next): This should be replaced by `At::deeply_normalize` +/// FIXME(-Znext-solver): This should be replaced by `At::deeply_normalize` /// which has the same behavior with the new solver. Because using a separate /// fulfillment context worsens caching in the old solver, `At::deeply_normalize` /// is still lazy with the old solver as it otherwise negatively impacts perf. @@ -484,7 +484,7 @@ fn is_impossible_associated_item( t.super_visit_with(self) } fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::ReEarlyBound(param) = r.kind() + if let ty::ReEarlyParam(param) = r.kind() && let param_def_id = self.generics.region_param(¶m, self.tcx).def_id && self.tcx.parent(param_def_id) == self.trait_item_def_id { diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 17c7f94ee..7ac37315f 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -84,7 +84,7 @@ fn check_is_object_safe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { span, ) = violation { - lint_object_unsafe_trait(tcx, *span, trait_def_id, &violation); + lint_object_unsafe_trait(tcx, *span, trait_def_id, violation); } } return true; @@ -192,9 +192,8 @@ fn lint_object_unsafe_trait( ); if node.is_some() { // Only provide the help if its a local trait, otherwise it's not - violation.solution(err); + violation.solution().add_to(err); } - err }, ); } @@ -329,7 +328,7 @@ fn super_predicates_have_non_lifetime_binders( tcx.super_predicates_of(trait_def_id) .predicates .iter() - .filter_map(|(pred, span)| pred.has_non_region_late_bound().then_some(*span)) + .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(*span)) .collect() } @@ -345,7 +344,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { // Search for a predicate like `Self : Sized` amongst the trait bounds. let predicates = tcx.predicates_of(def_id); let predicates = predicates.instantiate_identity(tcx).predicates; - elaborate(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() { + elaborate(tcx, predicates).any(|pred| match pred.kind().skip_binder() { ty::ClauseKind::Trait(ref trait_pred) => { trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0) } @@ -509,7 +508,7 @@ fn virtual_call_violations_for_method<'tcx>( Ok(layout) => Some(layout.abi), Err(err) => { // #78372 - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( tcx.def_span(method.def_id), format!("error: {err}\n while computing layout for type {ty:?}"), ); @@ -525,7 +524,7 @@ fn virtual_call_violations_for_method<'tcx>( match abi_of_ty(unit_receiver_ty) { Some(Abi::Scalar(..)) => (), abi => { - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( tcx.def_span(method.def_id), format!( "receiver when `Self = ()` should have a Scalar ABI; found {abi:?}" @@ -543,7 +542,7 @@ fn virtual_call_violations_for_method<'tcx>( match abi_of_ty(trait_object_receiver) { Some(Abi::ScalarPair(..)) => (), abi => { - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( tcx.def_span(method.def_id), format!( "receiver when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}" @@ -595,9 +594,7 @@ fn virtual_call_violations_for_method<'tcx>( // would already have reported an error at the definition of the // auto trait. if pred_trait_ref.args.len() != 1 { - tcx.sess - .diagnostic() - .delay_span_bug(span, "auto traits cannot have generic parameters"); + tcx.sess.dcx().span_delayed_bug(span, "auto traits cannot have generic parameters"); } return false; } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 32bbd626d..7513f88cf 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -72,7 +72,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { }; let mut constraints = QueryRegionConstraints::default(); - let Ok(InferOk { value, obligations }) = self + let Ok(InferOk { value: mut bounds, obligations }) = self .instantiate_nll_query_response_and_region_obligations( &ObligationCause::dummy(), param_env, @@ -85,6 +85,10 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { }; assert_eq!(&obligations, &[]); + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + bounds.retain(|bound| !bound.has_placeholders()); + if !constraints.is_empty() { let span = self.tcx.def_span(body_id); @@ -107,14 +111,14 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { let errors = ocx.select_all_or_error(); if !errors.is_empty() { - self.tcx.sess.delay_span_bug( + self.tcx.sess.span_delayed_bug( span, "implied_outlives_bounds failed to solve obligations from instantiation", ); } }; - value + bounds } fn implied_bounds_tys( diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 471d10dbd..a1b0ada0e 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -18,7 +18,7 @@ use rustc_middle::traits::ImplSourceUserDefinedData; use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; +use crate::infer::{BoundRegionConversionTime, InferCtxt, InferOk}; use crate::traits::error_reporting::TypeErrCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::select::ProjectionMatchesProjection; @@ -67,7 +67,7 @@ pub trait NormalizeExt<'tcx> { /// same goals in both a temporary and the shared context which negatively impacts /// performance as these don't share caching. /// - /// FIXME(-Ztrait-solver=next): This has the same behavior as `traits::fully_normalize` + /// FIXME(-Znext-solver): This has the same behavior as `traits::fully_normalize` /// in the new solver, but because of performance reasons, we currently reuse an /// existing fulfillment context in the old solver. Once we also eagerly prove goals with /// the old solver or have removed the old solver, remove `traits::fully_normalize` and @@ -191,7 +191,9 @@ impl<'tcx> ProjectionCandidateSet<'tcx> { match (current, candidate) { (ParamEnv(..), ParamEnv(..)) => convert_to_ambiguous = (), (ParamEnv(..), _) => return false, - (_, ParamEnv(..)) => unreachable!(), + (_, ParamEnv(..)) => bug!( + "should never prefer non-param-env candidates over param-env candidates" + ), (_, _) => convert_to_ambiguous = (), } } @@ -584,7 +586,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx data, self.cause.clone(), self.depth, - &mut self.obligations, + self.obligations, ) } else { opt_normalize_projection_type( @@ -593,7 +595,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx data, self.cause.clone(), self.depth, - &mut self.obligations, + self.obligations, ) .ok() .flatten() @@ -632,7 +634,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx data, self.cause.clone(), self.depth, - &mut self.obligations, + self.obligations, ) .ok() .flatten() @@ -717,7 +719,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx data, self.cause.clone(), self.depth, - &mut self.obligations, + self.obligations, ) } @@ -732,7 +734,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx data, self.cause.clone(), self.depth, - &mut self.obligations, + self.obligations, ); PlaceholderReplacer::replace_placeholders( @@ -894,16 +896,16 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReLateBound(debruijn, _) - if debruijn.as_usize() + 1 - > self.current_index.as_usize() + self.universe_indices.len() => + ty::ReBound(debruijn, _) + if debruijn.as_usize() + >= self.current_index.as_usize() + self.universe_indices.len() => { bug!( "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", self.universe_indices ); } - ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => { + ty::ReBound(debruijn, br) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); let p = ty::PlaceholderRegion { universe, bound: br }; self.mapped_regions.insert(p, br); @@ -1001,7 +1003,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { &mut self, t: ty::Binder<'tcx, T>, ) -> ty::Binder<'tcx, T> { - if !t.has_placeholders() && !t.has_infer_regions() { + if !t.has_placeholders() && !t.has_infer() { return t; } self.current_index.shift_in(1); @@ -1034,7 +1036,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { let db = ty::DebruijnIndex::from_usize( self.universe_indices.len() - index + self.current_index.as_usize() - 1, ); - ty::Region::new_late_bound(self.interner(), db, *replace_var) + ty::Region::new_bound(self.interner(), db, *replace_var) } None => r1, } @@ -1048,6 +1050,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty = self.infcx.shallow_resolve(ty); match *ty.kind() { ty::Placeholder(p) => { let replace_var = self.mapped_types.get(&p); @@ -1063,16 +1066,23 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { ); Ty::new_bound(self.infcx.tcx, db, *replace_var) } - None => ty, + None => { + if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } } } - _ if ty.has_placeholders() || ty.has_infer_regions() => ty.super_fold_with(self), + _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), _ => ty, } } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + let ct = self.infcx.shallow_resolve(ct); if let ty::ConstKind::Placeholder(p) = ct.kind() { let replace_var = self.mapped_consts.get(&p); match replace_var { @@ -1087,7 +1097,13 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { ); ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) } - None => ct, + None => { + if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } } } else { ct.super_fold_with(self) @@ -1440,11 +1456,22 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>( // Infer the generic parameters of the impl by unifying the // impl type with the self type of the projection. - let self_ty = alias_ty.self_ty(); + let mut self_ty = alias_ty.self_ty(); + if !selcx.infcx.next_trait_solver() { + self_ty = normalize_with_depth_to( + selcx, + param_env, + cause.clone(), + depth + 1, + self_ty, + obligations, + ); + } + match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { Ok(mut ok) => obligations.append(&mut ok.obligations), Err(_) => { - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( cause.span, format!( "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not" @@ -1798,11 +1825,18 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let lang_items = selcx.tcx().lang_items(); - if [lang_items.coroutine_trait(), lang_items.future_trait(), lang_items.iterator_trait()].contains(&Some(trait_ref.def_id)) - || selcx.tcx().fn_trait_kind_from_def_id(trait_ref.def_id).is_some() + if [ + lang_items.coroutine_trait(), + lang_items.future_trait(), + lang_items.iterator_trait(), + lang_items.async_iterator_trait(), + lang_items.fn_trait(), + lang_items.fn_mut_trait(), + lang_items.fn_once_trait(), + ].contains(&Some(trait_ref.def_id)) { true - } else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) { + }else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) { match self_ty.kind() { ty::Bool | ty::Char @@ -1949,7 +1983,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // These traits have no associated types. - selcx.tcx().sess.delay_span_bug( + selcx.tcx().sess.span_delayed_bug( obligation.cause.span, format!("Cannot project an associated type from `{impl_source:?}`"), ); @@ -2017,6 +2051,8 @@ fn confirm_select_candidate<'cx, 'tcx>( confirm_future_candidate(selcx, obligation, data) } else if lang_items.iterator_trait() == Some(trait_def_id) { confirm_iterator_candidate(selcx, obligation, data) + } else if lang_items.async_iterator_trait() == Some(trait_def_id) { + confirm_async_iterator_candidate(selcx, obligation, data) } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() { if obligation.predicate.self_ty().is_closure() { confirm_closure_candidate(selcx, obligation, data) @@ -2046,12 +2082,13 @@ fn confirm_coroutine_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, nested: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let ty::Coroutine(_, args, _) = - selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() - else { - unreachable!() + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let ty::Coroutine(_, args, _) = self_ty.kind() else { + unreachable!( + "expected coroutine self type for built-in coroutine candidate, found {self_ty}" + ) }; - let coroutine_sig = args.as_coroutine().poly_sig(); + let coroutine_sig = args.as_coroutine().sig(); let Normalized { value: coroutine_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -2066,29 +2103,31 @@ fn confirm_coroutine_candidate<'cx, 'tcx>( let coroutine_def_id = tcx.require_lang_item(LangItem::Coroutine, None); - let predicate = super::util::coroutine_trait_ref_and_outputs( + let (trait_ref, yield_ty, return_ty) = super::util::coroutine_trait_ref_and_outputs( tcx, coroutine_def_id, obligation.predicate.self_ty(), coroutine_sig, - ) - .map_bound(|(trait_ref, yield_ty, return_ty)| { - let name = tcx.associated_item(obligation.predicate.def_id).name; - let ty = if name == sym::Return { - return_ty - } else if name == sym::Yield { - yield_ty - } else { - bug!() - }; + ); - ty::ProjectionPredicate { - projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), - term: ty.into(), - } - }); + let name = tcx.associated_item(obligation.predicate.def_id).name; + let ty = if name == sym::Return { + return_ty + } else if name == sym::Yield { + yield_ty + } else { + span_bug!( + tcx.def_span(obligation.predicate.def_id), + "unexpected associated type: `Coroutine::{name}`" + ); + }; - confirm_param_env_candidate(selcx, obligation, predicate, false) + let predicate = ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), + term: ty.into(), + }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(nested) .with_addl_obligations(obligations) } @@ -2098,12 +2137,13 @@ fn confirm_future_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, nested: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let ty::Coroutine(_, args, _) = - selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() - else { - unreachable!() + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let ty::Coroutine(_, args, _) = self_ty.kind() else { + unreachable!( + "expected coroutine self type for built-in async future candidate, found {self_ty}" + ) }; - let coroutine_sig = args.as_coroutine().poly_sig(); + let coroutine_sig = args.as_coroutine().sig(); let Normalized { value: coroutine_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -2117,22 +2157,21 @@ fn confirm_future_candidate<'cx, 'tcx>( let tcx = selcx.tcx(); let fut_def_id = tcx.require_lang_item(LangItem::Future, None); - let predicate = super::util::future_trait_ref_and_outputs( + let (trait_ref, return_ty) = super::util::future_trait_ref_and_outputs( tcx, fut_def_id, obligation.predicate.self_ty(), coroutine_sig, - ) - .map_bound(|(trait_ref, return_ty)| { - debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Output); + ); - ty::ProjectionPredicate { - projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), - term: return_ty.into(), - } - }); + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Output); + + let predicate = ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), + term: return_ty.into(), + }; - confirm_param_env_candidate(selcx, obligation, predicate, false) + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(nested) .with_addl_obligations(obligations) } @@ -2142,12 +2181,54 @@ fn confirm_iterator_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, nested: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let ty::Coroutine(_, args, _) = self_ty.kind() else { + unreachable!("expected coroutine self type for built-in gen candidate, found {self_ty}") + }; + let gen_sig = args.as_coroutine().sig(); + let Normalized { value: gen_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + gen_sig, + ); + + debug!(?obligation, ?gen_sig, ?obligations, "confirm_iterator_candidate"); + + let tcx = selcx.tcx(); + let iter_def_id = tcx.require_lang_item(LangItem::Iterator, None); + + let (trait_ref, yield_ty) = super::util::iterator_trait_ref_and_outputs( + tcx, + iter_def_id, + obligation.predicate.self_ty(), + gen_sig, + ); + + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); + + let predicate = ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), + term: yield_ty.into(), + }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) +} + +fn confirm_async_iterator_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: Vec<PredicateObligation<'tcx>>, +) -> Progress<'tcx> { let ty::Coroutine(_, args, _) = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() else { unreachable!() }; - let gen_sig = args.as_coroutine().poly_sig(); + let gen_sig = args.as_coroutine().sig(); let Normalized { value: gen_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -2156,27 +2237,34 @@ fn confirm_iterator_candidate<'cx, 'tcx>( gen_sig, ); - debug!(?obligation, ?gen_sig, ?obligations, "confirm_iterator_candidate"); + debug!(?obligation, ?gen_sig, ?obligations, "confirm_async_iterator_candidate"); let tcx = selcx.tcx(); - let iter_def_id = tcx.require_lang_item(LangItem::Iterator, None); + let iter_def_id = tcx.require_lang_item(LangItem::AsyncIterator, None); - let predicate = super::util::iterator_trait_ref_and_outputs( + let (trait_ref, yield_ty) = super::util::async_iterator_trait_ref_and_outputs( tcx, iter_def_id, obligation.predicate.self_ty(), gen_sig, - ) - .map_bound(|(trait_ref, yield_ty)| { - debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); + ); - ty::ProjectionPredicate { - projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), - term: yield_ty.into(), - } - }); + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); - confirm_param_env_candidate(selcx, obligation, predicate, false) + let ty::Adt(_poll_adt, args) = *yield_ty.kind() else { + bug!(); + }; + let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else { + bug!(); + }; + let item_ty = args.type_at(0); + + let predicate = ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), + term: item_ty.into(), + }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(nested) .with_addl_obligations(obligations) } @@ -2239,8 +2327,9 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, nested: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { + let tcx = selcx.tcx(); let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); - let sig = fn_type.fn_sig(selcx.tcx()); + let sig = fn_type.fn_sig(tcx); let Normalized { value: sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -2249,9 +2338,24 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( sig, ); - confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + let host_effect_param = match *fn_type.kind() { + ty::FnDef(def_id, args) => tcx + .generics_of(def_id) + .host_effect_index + .map_or(tcx.consts.true_, |idx| args.const_at(idx)), + ty::FnPtr(_) => tcx.consts.true_, + _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"), + }; + + confirm_callable_candidate( + selcx, + obligation, + sig, + util::TupleArgumentsFlag::Yes, + host_effect_param, + ) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_closure_candidate<'cx, 'tcx>( @@ -2259,9 +2363,9 @@ fn confirm_closure_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, nested: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let ty::Closure(_, args) = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() - else { - unreachable!() + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let ty::Closure(_, args) = self_ty.kind() else { + unreachable!("expected closure self type for closure candidate, found {self_ty}") }; let closure_sig = args.as_closure().sig(); let Normalized { value: closure_sig, obligations } = normalize_with_depth( @@ -2274,9 +2378,16 @@ fn confirm_closure_candidate<'cx, 'tcx>( debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate"); - confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + confirm_callable_candidate( + selcx, + obligation, + closure_sig, + util::TupleArgumentsFlag::No, + // FIXME(effects): This doesn't handle const closures correctly! + selcx.tcx().consts.true_, + ) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_callable_candidate<'cx, 'tcx>( @@ -2284,6 +2395,7 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, flag: util::TupleArgumentsFlag, + fn_host_effect: ty::Const<'tcx>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); @@ -2298,6 +2410,7 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation.predicate.self_ty(), fn_sig, flag, + fn_host_effect, ) .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, fn_once_output_def_id, trait_ref.args), @@ -2319,7 +2432,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>( let cache_entry = infcx.instantiate_binder_with_fresh_vars( cause.span, - LateBoundRegionConversionTime::HigherRankedType, + BoundRegionConversionTime::HigherRankedType, poly_cache_entry, ); 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 f8efa6a1f..ec80df1d6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -48,9 +48,7 @@ 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 args) => { - trivial_dropck_outlives(tcx, args.as_closure().tupled_upvars_ty()) - } + ty::Closure(_, args) => trivial_dropck_outlives(tcx, args.as_closure().tupled_upvars_ty()), ty::Adt(def, _) => { if Some(def.did()) == tcx.lang_items().manually_drop() { @@ -239,7 +237,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( // By the time this code runs, all type variables ought to // be fully resolved. - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( span, format!("upvar_tys for closure not found. Expected capture information for closure {ty}",), ); @@ -288,7 +286,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( if !args.is_valid() { // By the time this code runs, all type variables ought to // be fully resolved. - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( span, format!("upvar_tys for coroutine not found. Expected capture information for coroutine {ty}",), ); 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 65f32b1c4..d812d537d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -72,7 +72,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self); fulfill_cx.register_predicate_obligation(self, obligation.clone()); // True errors - // FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK? + // FIXME(-Znext-solver): Overflows are reported as ambig here, is that OK? if !fulfill_cx.select_where_possible(self).is_empty() { Ok(EvaluationResult::EvaluatedToErr) } else if !fulfill_cx.select_all_or_error(self).is_empty() { @@ -108,7 +108,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { match self.evaluate_obligation(obligation) { Ok(result) => result, Err(OverflowError::Canonical) => { - let mut selcx = SelectionContext::new(&self); + let mut selcx = SelectionContext::new(self); selcx.evaluate_root_obligation(obligation).unwrap_or_else(|r| match r { OverflowError::Canonical => { span_bug!( diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 2e31b560b..ed55533bc 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -150,7 +150,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor { #[inline] fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { match *r { - ty::ReLateBound(debruijn, _) if debruijn > self.outer_index => { + ty::ReBound(debruijn, _) if debruijn > self.outer_index => { self.escaping = self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize()); } @@ -160,14 +160,12 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor { } fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - match ct.kind() { - ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => { - self.escaping = - self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize()); - ControlFlow::Continue(()) - } - _ => ct.super_visit_with(self), + if ct.outer_exclusive_binder() > self.outer_index { + self.escaping = self + .escaping + .max(ct.outer_exclusive_binder().as_usize() - self.outer_index.as_usize()); } + ControlFlow::Continue(()) } } @@ -287,14 +285,14 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> ty::Projection => tcx.normalize_projection_ty(c_data), ty::Weak => tcx.normalize_weak_ty(c_data), ty::Inherent => tcx.normalize_inherent_projection_ty(c_data), - _ => unreachable!(), + kind => unreachable!("did not expect {kind:?} due to match arm above"), }?; // We don't expect ambiguity. if !result.value.is_proven() { // Rustdoc normalizes possibly not well-formed types, so only // treat this as a bug if we're not in rustdoc. if !tcx.sess.opts.actually_rustdoc { - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( DUMMY_SP, format!("unexpected ambiguity: {c_data:?} {result:?}"), ); 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 302b6016e..152ceeee8 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 @@ -26,7 +26,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { tcx.type_op_ascribe_user_type(canonicalized) } - fn perform_locally_in_new_solver( + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution> { 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 c81bc5790..18bb56ba4 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 @@ -82,13 +82,13 @@ where let value = infcx.commit_if_ok(|_| { let ocx = ObligationCtxt::new(infcx); let value = op(&ocx).map_err(|_| { - infcx.tcx.sess.delay_span_bug(span, format!("error performing operation: {name}")) + infcx.tcx.sess.span_delayed_bug(span, format!("error performing operation: {name}")) })?; let errors = ocx.select_all_or_error(); if errors.is_empty() { Ok(value) } else { - Err(infcx.tcx.sess.delay_span_bug( + Err(infcx.tcx.sess.span_delayed_bug( DUMMY_SP, format!("errors selecting obligation during MIR typeck: {errors:?}"), )) diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs index f65893088..57e649f3e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs @@ -23,7 +23,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> { tcx.type_op_eq(canonicalized) } - fn perform_locally_in_new_solver( + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution> { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index e345fc39e..ba6ed2987 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -50,7 +50,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { tcx.implied_outlives_bounds(canonicalized) } - fn perform_locally_in_new_solver( + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution> { @@ -123,20 +123,19 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( Some(pred) => pred, }; match pred { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound // if we ever support that + ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) - | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous - | ty::PredicateKind::AliasRelate(..) - => {} + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::AliasRelate(..) => {} // We need to search through *all* WellFormed predicates ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { @@ -144,10 +143,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( } // We need to register region relationships - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( - r_a, - r_b, - ))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( + ty::OutlivesPredicate(r_a, r_b), + )) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( ty_a, @@ -186,7 +184,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( push_outlives_components(tcx, ty_a, &mut components); implied_bounds.extend(implied_bounds_from_components(r_b, components)) } - ty::GenericArgKind::Const(_) => unreachable!(), + ty::GenericArgKind::Const(_) => { + unreachable!("consts do not participate in outlives bounds") + } } } @@ -208,6 +208,11 @@ fn implied_bounds_from_components<'tcx>( Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)), + Component::Placeholder(_p) => { + // FIXME(non_lifetime_binders): Placeholders don't currently + // imply anything for outlives, though they could easily. + None + } Component::EscapingAlias(_) => // If the projection has escaping regions, don't // try to infer any implied bounds even for its diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 9d7933e23..272f1a54f 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -89,7 +89,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 't /// make sure to feed it predefined opaque types and the defining anchor /// and that would require duplicating all of the tcx queries. Instead, /// just perform these ops locally. - fn perform_locally_in_new_solver( + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution>; @@ -149,7 +149,7 @@ where if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, - |ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self), + |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self), "query type op", span, )? @@ -159,7 +159,7 @@ where let mut region_constraints = QueryRegionConstraints::default(); let (output, error_info, mut obligations, _) = Q::fully_perform_into(self, infcx, &mut region_constraints).map_err(|_| { - infcx.tcx.sess.delay_span_bug(span, format!("error performing {self:?}")) + infcx.tcx.sess.span_delayed_bug(span, format!("error performing {self:?}")) })?; // Typically, instantiating NLL query results does not @@ -188,7 +188,7 @@ where } } if !progress { - return Err(infcx.tcx.sess.delay_span_bug( + return Err(infcx.tcx.sess.span_delayed_bug( span, format!("ambiguity processing {obligations:?} from {self:?}"), )); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 9559f5002..3b33f6e61 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -25,11 +25,11 @@ where T::type_op_method(tcx, canonicalized) } - fn perform_locally_in_new_solver( + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution> { - // FIXME(-Ztrait-solver=next): shouldn't be using old normalizer + // FIXME(-Znext-solver): shouldn't be using old normalizer Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value)) } } 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 f2c1243f9..07587e374 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 @@ -42,7 +42,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { tcx.dropck_outlives(canonicalized) } - fn perform_locally_in_new_solver( + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution> { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 789ef6472..e21ede47f 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -40,7 +40,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { tcx.type_op_prove_predicate(canonicalized) } - fn perform_locally_in_new_solver( + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution> { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs index 10976d5cd..ae11b0825 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs @@ -20,7 +20,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> { tcx.type_op_subtype(canonicalized) } - fn perform_locally_in_new_solver( + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution> { 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 f4b6d3bcf..38b1046ac 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -115,6 +115,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_future_candidates(obligation, &mut candidates); } else if lang_items.iterator_trait() == Some(def_id) { self.assemble_iterator_candidates(obligation, &mut candidates); + } else if lang_items.async_iterator_trait() == Some(def_id) { + self.assemble_async_iterator_candidates(obligation, &mut candidates); } self.assemble_closure_candidates(obligation, &mut candidates); @@ -155,10 +157,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .infcx .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); - // FIXME(effects) proper constness needed? - candidates.vec.extend( - result.into_iter().map(|idx| ProjectionCandidate(idx, ty::BoundConstness::NotConst)), - ); + candidates.vec.extend(result.into_iter().map(|idx| ProjectionCandidate(idx))); } /// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller @@ -261,6 +260,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + fn assemble_async_iterator_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = obligation.self_ty().skip_binder(); + if let ty::Coroutine(did, args, _) = *self_ty.kind() { + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl AsyncIterator`. + if self.tcx().coroutine_is_async_gen(did) { + debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + + // Can only confirm this candidate if we have constrained + // the `Yield` type to at least `Poll<Option<?0>>`.. + let ty::Adt(_poll_def, args) = *args.as_coroutine().yield_ty().kind() else { + candidates.ambiguous = true; + return; + }; + let ty::Adt(_option_def, _) = *args.type_at(0).kind() else { + candidates.ambiguous = true; + return; + }; + + candidates.vec.push(AsyncIteratorCandidate); + } + } + } + /// Checks for the artificial impl that the compiler will create for an obligation like `X : /// FnMut<..>` where `X` is a closure type. /// @@ -291,8 +318,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } None => { - debug!("assemble_unboxed_candidates: closure_kind not yet known"); - candidates.vec.push(ClosureCandidate { is_const }); + if kind == ty::ClosureKind::FnOnce { + candidates.vec.push(ClosureCandidate { is_const }); + } else { + candidates.ambiguous = true; + } } } } @@ -328,17 +358,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Provide an impl, but only for suitable `fn` pointers. ty::FnPtr(sig) => { if sig.is_fn_trait_compatible() { - candidates.vec.push(FnPointerCandidate { is_const: false }); + candidates + .vec + .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ }); } } // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). - ty::FnDef(def_id, _) => { - if self.tcx().fn_sig(def_id).skip_binder().is_fn_trait_compatible() - && self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() + ty::FnDef(def_id, args) => { + let tcx = self.tcx(); + if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() { - candidates - .vec - .push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) }); + candidates.vec.push(FnPointerCandidate { + fn_host_effect: tcx + .generics_of(def_id) + .host_effect_index + .map_or(tcx.consts.true_, |idx| args.const_at(idx)), + }); } } _ => {} @@ -375,7 +411,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.args_refs_may_unify(obligation_args, impl_trait_ref.skip_binder().args) { + if !drcx.args_may_unify(obligation_args, impl_trait_ref.skip_binder().args) { return; } if self.reject_fn_ptr_impls( @@ -555,7 +591,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::Alias(ty::Opaque, _) => { - if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(..))) { + 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. // @@ -634,7 +670,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = placeholder_trait_predicate.self_ty(); let principal_trait_ref = match self_ty.kind() { - ty::Dynamic(ref data, ..) => { + ty::Dynamic(data, ..) => { if data.auto_traits().any(|did| did == obligation.predicate.def_id()) { debug!( "assemble_candidates_from_object_ty: matched builtin bound, \ @@ -759,10 +795,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - ( - &ty::Dynamic(ref a_data, a_region, ty::Dyn), - &ty::Dynamic(ref b_data, b_region, ty::Dyn), - ) => { + (&ty::Dynamic(a_data, a_region, ty::Dyn), &ty::Dynamic(b_data, b_region, ty::Dyn)) => { // Upcast coercions permit several things: // // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` @@ -923,9 +956,18 @@ 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. - // FIXME(effects) - if true { - candidates.vec.push(ConstDestructCandidate(None)); + let Some(host_effect_index) = + self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index + else { + candidates.vec.push(BuiltinCandidate { has_nested: false }); + return; + }; + // If the obligation has `host = true`, then the obligation is non-const and it's always + // trivially implemented. + if obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index) + == self.tcx().consts.true_ + { + candidates.vec.push(BuiltinCandidate { has_nested: false }); return; } @@ -1051,7 +1093,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) { // The regions of a type don't affect the size of the type let tcx = self.tcx(); - let self_ty = tcx.erase_late_bound_regions(obligation.predicate.self_ty()); + let self_ty = tcx.instantiate_bound_regions_with_erased(obligation.predicate.self_ty()); // We should erase regions from both the param-env and type, since both // may have infer regions. Specifically, after canonicalizing and instantiating, // early bound regions turn into region vars in both the new and old solver. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 4bfa341e3..ce3fc2185 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -9,7 +9,7 @@ use rustc_ast::Mutability; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; +use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch}; use rustc_middle::ty::{ @@ -71,7 +71,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, data) } - ProjectionCandidate(idx, _) => { + ProjectionCandidate(idx) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; ImplSource::Param(obligations) } @@ -98,8 +98,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator) } - FnPointerCandidate { is_const } => { - let data = self.confirm_fn_pointer_candidate(obligation, is_const)?; + AsyncIteratorCandidate => { + let vtable_iterator = self.confirm_async_iterator_candidate(obligation)?; + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator) + } + + FnPointerCandidate { fn_host_effect } => { + let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?; ImplSource::Builtin(BuiltinImplSource::Misc, data) } @@ -327,8 +332,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // care about other regions. Erasing late-bound regions is equivalent // to instantiating the binder with placeholders then erasing those // placeholder regions. - let predicate = - self.tcx().erase_regions(self.tcx().erase_late_bound_regions(obligation.predicate)); + let predicate = self + .tcx() + .erase_regions(self.tcx().instantiate_bound_regions_with_erased(obligation.predicate)); let Some(assume) = rustc_transmute::Assume::from_const( self.infcx.tcx, @@ -393,7 +399,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.recursion_depth + 1, obligation.param_env, trait_def_id, - &trait_ref.args, + trait_ref.args, obligation.predicate, ); @@ -454,7 +460,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { recursion_depth, param_env, impl_def_id, - &args.value, + args.value, parent_trait_pred, ); @@ -552,7 +558,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let defs: &ty::Generics = tcx.generics_of(assoc_type); if !defs.params.is_empty() && !tcx.features().generic_associated_types_extended { - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( obligation.cause.span, "GATs in trait object shouldn't have been considered", ); @@ -592,7 +598,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name); let bound_var = ty::BoundVariableKind::Region(kind); bound_vars.push(bound_var); - ty::Region::new_late_bound( + ty::Region::new_bound( tcx, ty::INNERMOST, ty::BoundRegion { @@ -647,8 +653,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_fn_pointer_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - // FIXME(effects) - _is_const: bool, + fn_host_effect: ty::Const<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); @@ -669,6 +674,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self_ty, sig, util::TupleArgumentsFlag::Yes, + fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref); @@ -707,7 +713,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.recursion_depth, obligation.param_env, trait_def_id, - &args, + args, obligation.predicate, ); @@ -730,7 +736,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligation, ?coroutine_def_id, ?args, "confirm_coroutine_candidate"); - let coroutine_sig = args.as_coroutine().poly_sig(); + let coroutine_sig = args.as_coroutine().sig(); // NOTE: The self-type is a coroutine type and hence is // in fact unparameterized (or at least does not reference any @@ -741,15 +747,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .no_bound_vars() .expect("unboxed closure type should not capture bound vars from the predicate"); - let trait_ref = super::util::coroutine_trait_ref_and_outputs( + let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), self_ty, coroutine_sig, - ) - .map_bound(|(trait_ref, ..)| trait_ref); + ); - let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; debug!(?trait_ref, ?nested, "coroutine candidate obligations"); Ok(nested) @@ -769,17 +774,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligation, ?coroutine_def_id, ?args, "confirm_future_candidate"); - let coroutine_sig = args.as_coroutine().poly_sig(); + let coroutine_sig = args.as_coroutine().sig(); - let trait_ref = super::util::future_trait_ref_and_outputs( + let (trait_ref, _) = super::util::future_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(), coroutine_sig, - ) - .map_bound(|(trait_ref, ..)| trait_ref); + ); - let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; debug!(?trait_ref, ?nested, "future candidate obligations"); Ok(nested) @@ -799,17 +803,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligation, ?coroutine_def_id, ?args, "confirm_iterator_candidate"); - let gen_sig = args.as_coroutine().poly_sig(); + let gen_sig = args.as_coroutine().sig(); - let trait_ref = super::util::iterator_trait_ref_and_outputs( + let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), gen_sig, - ) - .map_bound(|(trait_ref, ..)| trait_ref); + ); - let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + debug!(?trait_ref, ?nested, "iterator candidate obligations"); + + Ok(nested) + } + + fn confirm_async_iterator_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + // Okay to skip binder because the args on coroutine 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::Coroutine(coroutine_def_id, args, _) = *self_ty.kind() else { + bug!("closure candidate for non-closure {:?}", obligation); + }; + + debug!(?obligation, ?coroutine_def_id, ?args, "confirm_async_iterator_candidate"); + + let gen_sig = args.as_coroutine().sig(); + + let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + gen_sig, + ); + + let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; debug!(?trait_ref, ?nested, "iterator candidate obligations"); Ok(nested) @@ -820,11 +852,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { - let kind = self - .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 args on closure types never // touch bound regions, they just capture the in-scope // type/region parameters. @@ -833,16 +860,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bug!("closure candidate for non-closure {:?}", obligation); }; - let trait_ref = self.closure_trait_ref_unnormalized(obligation, args); - let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let trait_ref = + self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_); + let 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, args, kind)), - )); - Ok(nested) } @@ -920,8 +943,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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 ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") + }; + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") + }; let source_principal = a_data.principal().unwrap().with_self_ty(tcx, a_ty); let unnormalized_upcast_principal = @@ -985,7 +1012,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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)) + (&ty::Dynamic(data_a, r_a, dyn_a), &ty::Dynamic(data_b, r_b, dyn_b)) if dyn_a == dyn_b => { // See `assemble_candidates_for_unsizing` for more info. @@ -1030,7 +1057,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // `T` -> `Trait` - (_, &ty::Dynamic(ref data, r, ty::Dyn)) => { + (_, &ty::Dynamic(data, r, ty::Dyn)) => { let mut object_dids = data.auto_traits().chain(data.principal_def_id()); if let Some(did) = object_dids.find(|did| !tcx.check_is_object_safe(*did)) { return Err(TraitNotObjectSafe(did)); @@ -1184,11 +1211,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'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` - // FIXME(effects) - if true { - return Ok(vec![]); - } + let Some(host_effect_index) = + self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index + else { + bug!() + }; + let host_effect_param: ty::GenericArg<'tcx> = + obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index).into(); let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None); @@ -1277,7 +1306,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); stack.extend(tcx.coroutine_hidden_types(def_id).map(|bty| { let ty = bty.instantiate(tcx, args); - debug_assert!(!ty.has_late_bound_regions()); + debug_assert!(!ty.has_bound_regions()); ty })) } @@ -1285,7 +1314,6 @@ 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, @@ -1296,7 +1324,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.tcx(), LangItem::Destruct, cause.span, - [nested_ty], + [nested_ty.into(), host_effect_param], ), polarity: ty::ImplPolarity::Positive, }), @@ -1316,13 +1344,12 @@ 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(), LangItem::Destruct, cause.span, - [nested_ty], + [nested_ty.into(), host_effect_param], ), 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 08208cc60..23f7bdd15 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -32,8 +32,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::traits::TraitObligation; use rustc_middle::dep_graph::dep_kinds; use rustc_middle::dep_graph::DepNodeIndex; @@ -46,6 +46,7 @@ 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; +use rustc_span::Symbol; use std::cell::{Cell, RefCell}; use std::cmp; @@ -59,13 +60,13 @@ mod candidate_assembly; mod confirmation; #[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub enum IntercrateAmbiguityCause { - DownstreamCrate { trait_desc: String, self_desc: Option<String> }, - UpstreamCrateUpdate { trait_desc: String, self_desc: Option<String> }, - ReservationImpl { message: String }, +pub enum IntercrateAmbiguityCause<'tcx> { + DownstreamCrate { trait_ref: ty::TraitRef<'tcx>, self_ty: Option<Ty<'tcx>> }, + UpstreamCrateUpdate { trait_ref: ty::TraitRef<'tcx>, self_ty: Option<Ty<'tcx>> }, + ReservationImpl { message: Symbol }, } -impl IntercrateAmbiguityCause { +impl<'tcx> IntercrateAmbiguityCause<'tcx> { /// Emits notes when the overlap is caused by complex intercrate ambiguities. /// See #23980 for details. pub fn add_intercrate_ambiguity_hint(&self, err: &mut Diagnostic) { @@ -73,28 +74,32 @@ impl IntercrateAmbiguityCause { } pub fn intercrate_ambiguity_hint(&self) -> String { - match self { - IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } => { - let self_desc = if let Some(ty) = self_desc { - format!(" for type `{ty}`") - } else { - String::new() - }; - format!("downstream crates may implement trait `{trait_desc}`{self_desc}") + with_no_trimmed_paths!(match self { + IntercrateAmbiguityCause::DownstreamCrate { trait_ref, self_ty } => { + format!( + "downstream crates may implement trait `{trait_desc}`{self_desc}", + trait_desc = trait_ref.print_trait_sugared(), + self_desc = if let Some(self_ty) = self_ty { + format!(" for type `{self_ty}`") + } else { + String::new() + } + ) } - IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } => { - let self_desc = if let Some(ty) = self_desc { - format!(" for type `{ty}`") - } else { - String::new() - }; + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_ref, self_ty } => { format!( "upstream crates may add a new impl of trait `{trait_desc}`{self_desc} \ - in future versions" + in future versions", + trait_desc = trait_ref.print_trait_sugared(), + self_desc = if let Some(self_ty) = self_ty { + format!(" for type `{self_ty}`") + } else { + String::new() + } ) } - IntercrateAmbiguityCause::ReservationImpl { message } => message.clone(), - } + IntercrateAmbiguityCause::ReservationImpl { message } => message.to_string(), + }) } } @@ -114,7 +119,7 @@ pub struct SelectionContext<'cx, 'tcx> { /// We don't do his until we detect a coherence error because it can /// lead to false overflow results (#47139) and because always /// computing it may negatively impact performance. - intercrate_ambiguity_causes: Option<FxIndexSet<IntercrateAmbiguityCause>>, + intercrate_ambiguity_causes: Option<FxIndexSet<IntercrateAmbiguityCause<'tcx>>>, /// The mode that trait queries run in, which informs our error handling /// policy. In essence, canonicalized queries need their errors propagated @@ -217,8 +222,8 @@ pub enum TreatInductiveCycleAs { impl From<TreatInductiveCycleAs> for EvaluationResult { fn from(treat: TreatInductiveCycleAs) -> EvaluationResult { match treat { - TreatInductiveCycleAs::Ambig => EvaluatedToUnknown, - TreatInductiveCycleAs::Recur => EvaluatedToRecur, + TreatInductiveCycleAs::Ambig => EvaluatedToAmbigStackDependent, + TreatInductiveCycleAs::Recur => EvaluatedToErrStackDependent, } } } @@ -270,7 +275,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Gets the intercrate ambiguity causes collected since tracking /// was enabled and disables tracking at the same time. If /// tracking is not enabled, just returns an empty vector. - pub fn take_intercrate_ambiguity_causes(&mut self) -> FxIndexSet<IntercrateAmbiguityCause> { + pub fn take_intercrate_ambiguity_causes( + &mut self, + ) -> FxIndexSet<IntercrateAmbiguityCause<'tcx>> { assert!(self.is_intercrate()); self.intercrate_ambiguity_causes.take().unwrap_or_default() } @@ -367,7 +374,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug_assert!(!self.infcx.next_trait_solver()); // Watch out for overflow. This intentionally bypasses (and does // not update) the cache. - self.check_recursion_limit(&stack.obligation, &stack.obligation)?; + self.check_recursion_limit(stack.obligation, stack.obligation)?; // Check the cache. Note that we freshen the trait-ref // separately rather than using `stack.fresh_trait_ref` -- @@ -416,7 +423,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut no_candidates_apply = true; for c in candidate_set.vec.iter() { - if self.evaluate_candidate(stack, &c)?.may_apply() { + if self.evaluate_candidate(stack, c)?.may_apply() { no_candidates_apply = false; break; } @@ -428,19 +435,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); if !trait_ref.references_error() { let self_ty = trait_ref.self_ty(); - let (trait_desc, self_desc) = with_no_trimmed_paths!({ - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = - self_ty.has_concrete_skeleton().then(|| self_ty.to_string()); - (trait_desc, self_desc) - }); + let self_ty = self_ty.has_concrete_skeleton().then(|| self_ty); let cause = if let Conflict::Upstream = conflict { - IntercrateAmbiguityCause::UpstreamCrateUpdate { - trait_desc, - self_desc, - } + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_ref, self_ty } } else { - IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } + IntercrateAmbiguityCause::DownstreamCrate { trait_ref, self_ty } }; debug!(?cause, "evaluate_stack: pushing cause"); self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause); @@ -799,7 +798,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // A global type with no free lifetimes or generic parameters // outlives anything. if pred.0.has_free_regions() - || pred.0.has_late_bound_regions() + || pred.0.has_bound_regions() || pred.0.has_non_region_infer() || pred.0.has_non_region_infer() { @@ -885,19 +884,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::ClosureKind(_, closure_args, kind) => { - match self.infcx.closure_kind(closure_args) { - Some(closure_kind) => { - if closure_kind.extends(kind) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } - } - None => Ok(EvaluatedToAmbig), - } - } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { match const_evaluatable::is_const_evaluatable( self.infcx, @@ -1004,8 +990,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + ty::PredicateKind::NormalizesTo(..) => { + bug!("NormalizesTo is only used by the new solver") + } ty::PredicateKind::AliasRelate(..) => { - bug!("AliasRelate is only used for new solver") + bug!("AliasRelate is only used by the new solver") } ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { @@ -1237,15 +1226,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if unbound_input_types && stack.iter().skip(1).any(|prev| { stack.obligation.param_env == prev.obligation.param_env - && self.match_fresh_trait_refs( - stack.fresh_trait_pred, - prev.fresh_trait_pred, - prev.obligation.param_env, - ) + && self.match_fresh_trait_refs(stack.fresh_trait_pred, prev.fresh_trait_pred) }) { debug!("evaluate_stack --> unbound argument, recursive --> giving up",); - return Ok(EvaluatedToUnknown); + return Ok(EvaluatedToAmbigStackDependent); } match self.candidate_from_obligation(stack) { @@ -1464,20 +1449,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let ImplCandidate(def_id) = candidate { if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { - let value = tcx + let message = tcx .get_attr(def_id, sym::rustc_reservation_impl) .and_then(|a| a.value_str()); - if let Some(value) = value { + if let Some(message) = message { debug!( "filter_reservation_impls: \ reservation impl ambiguity on {:?}", def_id ); - intercrate_ambiguity_clauses.insert( - IntercrateAmbiguityCause::ReservationImpl { - message: value.to_string(), - }, - ); + intercrate_ambiguity_clauses + .insert(IntercrateAmbiguityCause::ReservationImpl { message }); } } return Ok(None); @@ -1751,7 +1733,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut nested_obligations = Vec::new(); let infer_predicate = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, - LateBoundRegionConversionTime::HigherRankedType, + BoundRegionConversionTime::HigherRankedType, env_predicate, ); let infer_projection = if potentially_unnormalized_candidates { @@ -1841,7 +1823,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // the param_env so that it can be given the lowest priority. See // #50825 for the motivation for this. let is_global = - |cand: &ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_late_bound_vars(); + |cand: &ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars(); // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, // `DiscriminantKindCandidate`, `ConstDestructCandidate` @@ -1879,7 +1861,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } // Drop otherwise equivalent non-const fn pointer candidates - (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => DropVictim::Yes, + (FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => { + DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_) + } ( ParamCandidate(ref other_cand), @@ -1889,6 +1873,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | CoroutineCandidate | FutureCandidate | IteratorCandidate + | AsyncIteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1896,7 +1881,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | BuiltinCandidate { .. } | TraitAliasCandidate | ObjectCandidate(_) - | ProjectionCandidate(..), + | ProjectionCandidate(_), ) => { // We have a where clause so don't go around looking // for impls. Arbitrarily give param candidates priority @@ -1906,7 +1891,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // here (see issue #50825). DropVictim::drop_if(!is_global(other_cand)) } - (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref victim_cand)) => { + (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref victim_cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No } @@ -1918,6 +1903,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | CoroutineCandidate | FutureCandidate | IteratorCandidate + | AsyncIteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1933,26 +1919,27 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ) } - (ProjectionCandidate(i, _), ProjectionCandidate(j, _)) + (ProjectionCandidate(i), ProjectionCandidate(j)) | (ObjectCandidate(i), ObjectCandidate(j)) => { // Arbitrarily pick the lower numbered candidate for backwards // compatibility reasons. Don't let this affect inference. DropVictim::drop_if(i < j && !has_non_region_infer) } - (ObjectCandidate(_), ProjectionCandidate(..)) - | (ProjectionCandidate(..), ObjectCandidate(_)) => { + (ObjectCandidate(_), ProjectionCandidate(_)) + | (ProjectionCandidate(_), ObjectCandidate(_)) => { bug!("Have both object and projection candidate") } // Arbitrarily give projection and object candidates priority. ( - ObjectCandidate(_) | ProjectionCandidate(..), + ObjectCandidate(_) | ProjectionCandidate(_), ImplCandidate(..) | AutoImplCandidate | ClosureCandidate { .. } | CoroutineCandidate | FutureCandidate | IteratorCandidate + | AsyncIteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1968,13 +1955,14 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | CoroutineCandidate | FutureCandidate | IteratorCandidate + | AsyncIteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate, - ObjectCandidate(_) | ProjectionCandidate(..), + ObjectCandidate(_) | ProjectionCandidate(_), ) => DropVictim::No, (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { @@ -2075,6 +2063,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | CoroutineCandidate | FutureCandidate | IteratorCandidate + | AsyncIteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -2086,6 +2075,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | CoroutineCandidate | FutureCandidate | IteratorCandidate + | AsyncIteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -2218,7 +2208,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } - ty::CoroutineWitness(def_id, ref args) => { + ty::CoroutineWitness(def_id, args) => { let hidden_types = bind_coroutine_hidden_types_above( self.infcx, def_id, @@ -2307,23 +2297,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::Array(element_ty, _) | ty::Slice(element_ty) => t.rebind(vec![element_ty]), - ty::Tuple(ref tys) => { + ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet t.rebind(tys.iter().collect()) } - ty::Closure(_, ref args) => { + ty::Closure(_, args) => { let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); t.rebind(vec![ty]) } - ty::Coroutine(_, ref args, _) => { + ty::Coroutine(_, args, _) => { let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); let witness = args.as_coroutine().witness(); t.rebind([ty].into_iter().chain(iter::once(witness)).collect()) } - ty::CoroutineWitness(def_id, ref args) => { + ty::CoroutineWitness(def_id, args) => { bind_coroutine_hidden_types_above(self.infcx, def_id, args, t.bound_vars()) } @@ -2436,7 +2426,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // the placeholder trait ref may fail due the Generalizer relation // raising a CyclicalTy error due to a sub_root_var relation // for a variable being generalized... - let guar = self.infcx.tcx.sess.delay_span_bug( + let guar = self.infcx.tcx.sess.span_delayed_bug( obligation.cause.span, format!( "Impl {impl_def_id:?} was matchable against {obligation:?} but now is not" @@ -2638,9 +2628,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { &self, previous: ty::PolyTraitPredicate<'tcx>, current: ty::PolyTraitPredicate<'tcx>, - param_env: ty::ParamEnv<'tcx>, ) -> bool { - let mut matcher = MatchAgainstFreshVars::new(self.tcx(), param_env); + let mut matcher = MatchAgainstFreshVars::new(self.tcx()); matcher.relate(previous, current).is_ok() } @@ -2668,6 +2657,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, args: GenericArgsRef<'tcx>, + fn_host_effect: ty::Const<'tcx>, ) -> ty::PolyTraitRef<'tcx> { let closure_sig = args.as_closure().sig(); @@ -2688,6 +2678,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { self_ty, closure_sig, util::TupleArgumentsFlag::No, + fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref) } @@ -3103,7 +3094,7 @@ fn bind_coroutine_hidden_types_above<'tcx>( kind: ty::BrAnon, }; counter += 1; - ty::Region::new_late_bound(tcx, current_depth, br) + ty::Region::new_bound(tcx, current_depth, br) } r => bug!("unexpected region: {r:?}"), }) diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index efab29743..71a88f5f0 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -37,7 +37,7 @@ pub struct OverlapError<'tcx> { pub with_impl: DefId, pub trait_ref: ty::TraitRef<'tcx>, pub self_ty: Option<Ty<'tcx>>, - pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause>, + pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>, pub involves_placeholder: bool, } @@ -107,7 +107,7 @@ pub fn translate_args_with_cause<'tcx>( param_env, source_impl, source_args, target_node ); let source_trait_ref = - infcx.tcx.impl_trait_ref(source_impl).unwrap().instantiate(infcx.tcx, &source_args); + 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 @@ -197,25 +197,22 @@ fn fulfill_implication<'tcx>( param_env, source_trait_ref, target_impl ); - let source_trait_ref = match traits::fully_normalize( - &infcx, - ObligationCause::dummy(), - param_env, - source_trait_ref, - ) { - Ok(source_trait_ref) => source_trait_ref, - Err(_errors) => { - infcx.tcx.sess.delay_span_bug( - infcx.tcx.def_span(source_impl), - format!("failed to fully normalize {source_trait_ref}"), - ); - source_trait_ref - } - }; + let source_trait_ref = + match traits::fully_normalize(infcx, ObligationCause::dummy(), param_env, source_trait_ref) + { + Ok(source_trait_ref) => source_trait_ref, + Err(_errors) => { + infcx.tcx.sess.span_delayed_bug( + infcx.tcx.def_span(source_impl), + format!("failed to fully normalize {source_trait_ref}"), + ); + source_trait_ref + } + }; let source_trait = ImplSubject::Trait(source_trait_ref); - let selcx = &mut SelectionContext::new(&infcx); + let selcx = &mut SelectionContext::new(infcx); 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_args, error_cause); @@ -415,7 +412,7 @@ fn report_conflicting_impls<'tcx>( let msg = DelayDm(|| { format!( "conflicting implementations of trait `{}`{}{}", - overlap.trait_ref.print_only_trait_path(), + overlap.trait_ref.print_trait_sugared(), overlap.self_ty.map_or_else(String::new, |ty| format!(" for type `{ty}`")), match used_to_be_allowed { Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", @@ -434,7 +431,10 @@ fn report_conflicting_impls<'tcx>( decorate(tcx, &overlap, impl_span, &mut err); Some(err.emit()) } else { - Some(tcx.sess.delay_span_bug(impl_span, "impl should have failed the orphan check")) + Some( + tcx.sess + .span_delayed_bug(impl_span, "impl should have failed the orphan check"), + ) }; sg.has_errored = reported; } @@ -445,12 +445,11 @@ fn report_conflicting_impls<'tcx>( }; tcx.struct_span_lint_hir( lint, - tcx.hir().local_def_id_to_hir_id(impl_def_id), + tcx.local_def_id_to_hir_id(impl_def_id), impl_span, msg, |err| { decorate(tcx, &overlap, impl_span, err); - err }, ); } diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 5960415a8..f8e47cacc 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -135,7 +135,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> { bug!("unexpected type during structural-match checking: {:?}", ty); } ty::Error(_) => { - self.tcx.sess.delay_span_bug(self.span, "ty::Error in structural-match check"); + self.tcx.sess.span_delayed_bug(self.span, "ty::Error in structural-match check"); // We still want to check other types after encountering an error, // as this may still emit relevant errors. return ControlFlow::Continue(()); diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 9d6be7689..e0f9fdc38 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -3,7 +3,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::traits::{FulfillmentError, TraitEngine}; use rustc_middle::ty::{self, Ty}; -use crate::traits::{query::evaluate_obligation::InferCtxtExt, NormalizeExt, Obligation}; +use crate::traits::{NormalizeExt, Obligation}; pub trait StructurallyNormalizeExt<'tcx> { fn structurally_normalize( @@ -16,46 +16,43 @@ pub trait StructurallyNormalizeExt<'tcx> { impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> { fn structurally_normalize( &self, - mut ty: Ty<'tcx>, + ty: Ty<'tcx>, fulfill_cx: &mut dyn TraitEngine<'tcx>, ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> { assert!(!ty.is_ty_var(), "should have resolved vars before calling"); if self.infcx.next_trait_solver() { - // FIXME(-Ztrait-solver=next): correctly handle - // overflow here. - for _ in 0..256 { - let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, projection_ty) = *ty.kind() - else { - break; - }; - - let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::NormalizeProjectionType, - span: self.cause.span, - }); - let obligation = Obligation::new( - self.infcx.tcx, - self.cause.clone(), - self.param_env, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: new_infer_ty.into(), - }), - ); - if self.infcx.predicate_may_hold(&obligation) { - fulfill_cx.register_predicate_obligation(self.infcx, obligation); - let errors = fulfill_cx.select_where_possible(self.infcx); - if !errors.is_empty() { - return Err(errors); - } - ty = self.infcx.resolve_vars_if_possible(new_infer_ty); - } else { - break; - } + // FIXME(-Znext-solver): Should we resolve opaques here? + let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) = *ty.kind() else { + return Ok(ty); + }; + + let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: self.cause.span, + }); + + // We simply emit an `alias-eq` goal here, since that will take care of + // normalizing the LHS of the projection until it is a rigid projection + // (or a not-yet-defined opaque in scope). + let obligation = Obligation::new( + self.infcx.tcx, + self.cause.clone(), + self.param_env, + ty::PredicateKind::AliasRelate( + ty.into(), + new_infer_ty.into(), + ty::AliasRelationDirection::Equate, + ), + ); + + fulfill_cx.register_predicate_obligation(self.infcx, obligation); + let errors = fulfill_cx.select_where_possible(self.infcx); + if !errors.is_empty() { + return Err(errors); } - Ok(ty) + Ok(self.infcx.resolve_vars_if_possible(new_infer_ty)) } else { Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx)) } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index bbde0c827..19eae93df 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -220,9 +220,8 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates); let impl_obligations = super::predicates_for_generics(cause, param_env, predicates); - let impl_obligations = impl_obligations - .chain(normalization_obligations1.into_iter()) - .chain(normalization_obligations2.into_iter()); + let impl_obligations = + impl_obligations.chain(normalization_obligations1).chain(normalization_obligations2); (subject, impl_obligations) } @@ -265,13 +264,26 @@ pub fn closure_trait_ref_and_return_type<'tcx>( self_ty: Ty<'tcx>, sig: ty::PolyFnSig<'tcx>, tuple_arguments: TupleArgumentsFlag, + fn_host_effect: ty::Const<'tcx>, ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { assert!(!self_ty.has_escaping_bound_vars()); let arguments_tuple = match tuple_arguments { TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()), }; - let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]); + let trait_ref = if tcx.generics_of(fn_trait_def_id).host_effect_index.is_some() { + ty::TraitRef::new( + tcx, + fn_trait_def_id, + [ + ty::GenericArg::from(self_ty), + ty::GenericArg::from(arguments_tuple), + ty::GenericArg::from(fn_host_effect), + ], + ) + } else { + ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]) + }; sig.map_bound(|sig| (trait_ref, sig.output())) } @@ -279,33 +291,44 @@ pub fn coroutine_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, fn_trait_def_id: DefId, self_ty: Ty<'tcx>, - sig: ty::PolyGenSig<'tcx>, -) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { + sig: ty::GenSig<'tcx>, +) -> (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>) { assert!(!self_ty.has_escaping_bound_vars()); - let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, sig.skip_binder().resume_ty]); - sig.map_bound(|sig| (trait_ref, sig.yield_ty, sig.return_ty)) + let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, sig.resume_ty]); + (trait_ref, sig.yield_ty, sig.return_ty) } pub fn future_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, fn_trait_def_id: DefId, self_ty: Ty<'tcx>, - sig: ty::PolyGenSig<'tcx>, -) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { + sig: ty::GenSig<'tcx>, +) -> (ty::TraitRef<'tcx>, Ty<'tcx>) { assert!(!self_ty.has_escaping_bound_vars()); let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty]); - sig.map_bound(|sig| (trait_ref, sig.return_ty)) + (trait_ref, sig.return_ty) } pub fn iterator_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, iterator_def_id: DefId, self_ty: Ty<'tcx>, - sig: ty::PolyGenSig<'tcx>, -) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { + sig: ty::GenSig<'tcx>, +) -> (ty::TraitRef<'tcx>, Ty<'tcx>) { assert!(!self_ty.has_escaping_bound_vars()); let trait_ref = ty::TraitRef::new(tcx, iterator_def_id, [self_ty]); - sig.map_bound(|sig| (trait_ref, sig.yield_ty)) + (trait_ref, sig.yield_ty) +} + +pub fn async_iterator_trait_ref_and_outputs<'tcx>( + tcx: TyCtxt<'tcx>, + async_iterator_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::GenSig<'tcx>, +) -> (ty::TraitRef<'tcx>, Ty<'tcx>) { + assert!(!self_ty.has_escaping_bound_vars()); + let trait_ref = ty::TraitRef::new(tcx, async_iterator_def_id, [self_ty]); + (trait_ref, sig.yield_ty) } pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index fe5b625e4..0f8d9c6bf 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -624,7 +624,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // Note that we handle the len is implicitly checked while walking `arg`. } - ty::Tuple(ref tys) => { + ty::Tuple(tys) => { if let Some((_last, rest)) = tys.split_last() { for &elem in rest { self.require_sized(elem, traits::TupleElem); @@ -761,18 +761,15 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; if !defer_to_coercion { - let cause = self.cause(traits::WellFormed(None)); - let component_traits = data.auto_traits().chain(data.principal_def_id()); - let tcx = self.tcx(); - self.out.extend(component_traits.map(|did| { - traits::Obligation::with_depth( - tcx, - cause.clone(), + if let Some(principal) = data.principal_def_id() { + self.out.push(traits::Obligation::with_depth( + self.tcx(), + self.cause(traits::WellFormed(None)), depth, param_env, - ty::Binder::dummy(ty::PredicateKind::ObjectSafe(did)), - ) - })); + ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)), + )); + } } } @@ -913,20 +910,15 @@ pub fn object_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Vec<ty::Region<'tcx>> { - // Since we don't actually *know* the self type for an object, - // this "open(err)" serves as a kind of dummy standin -- basically - // a placeholder type. - let open_ty = Ty::new_fresh(tcx, 0); - let predicates = existential_predicates.iter().filter_map(|predicate| { if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() { None } else { - Some(predicate.with_self_ty(tcx, open_ty)) + Some(predicate.with_self_ty(tcx, tcx.types.trait_object_dummy_self)) } }); - required_region_bounds(tcx, open_ty, predicates) + required_region_bounds(tcx, tcx.types.trait_object_dummy_self, predicates) } /// Given a set of predicates that apply to an object type, returns |