diff options
| author | bors <bors@rust-lang.org> | 2024-02-22 05:06:27 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-02-22 05:06:27 +0000 |
| commit | 3815fc06256f6281ee064fc607d24020dc96f05b (patch) | |
| tree | a9ec036cc3cb6e18d43f62e85574e81288b82cf0 /compiler/rustc_trait_selection/src | |
| parent | 596dd65cfa1d5f1a55a9a68d7f45bda436e3055d (diff) | |
| parent | 6f3bc7d938f5a8a772ba67cf41aad16a03b9fb1c (diff) | |
Auto merge of #3310 - rust-lang:rustup-2024-02-22, r=oli-obk
Automatic Rustup
Diffstat (limited to 'compiler/rustc_trait_selection/src')
22 files changed, 997 insertions, 1079 deletions
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 407fff03e15..10911c0d002 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,7 +1,7 @@ use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, - EmissionGuarantee, IntoDiagnostic, Level, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, + IntoDiagnostic, Level, SubdiagnosticMessageOp, }; use rustc_macros::Diagnostic; use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty}; @@ -102,7 +102,11 @@ pub enum AdjustSignatureBorrow { } impl AddToDiagnostic for AdjustSignatureBorrow { - fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { AdjustSignatureBorrow::Borrow { to_borrow } => { diag.arg("len", to_borrow.len()); diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 63555a305d8..97f715b6386 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -109,7 +109,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut errors = Vec::new(); for i in 0.. { if !infcx.tcx.recursion_limit().value_within_limit(i) { - unimplemented!("overflowed on pending obligations: {:?}", self.obligations); + // Only return true errors that we have accumulated while processing; + // keep ambiguities around, *including overflows*, because they shouldn't + // be considered true errors. + return errors; } let mut has_changed = false; diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index d87cc89954a..91312c9fdd6 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,6 +1,6 @@ use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; +use crate::traits::{BoundVarReplacer, PlaceholderReplacer}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -85,25 +85,16 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { ), ); - // Do not emit an error if normalization is known to fail but instead - // keep the projection unnormalized. This is the case for projections - // with a `T: Trait` where-clause and opaque types outside of the defining - // scope. - let result = if infcx.predicate_may_hold(&obligation) { - self.fulfill_cx.register_predicate_obligation(infcx, obligation); - let errors = self.fulfill_cx.select_all_or_error(infcx); - if !errors.is_empty() { - return Err(errors); - } - let ty = infcx.resolve_vars_if_possible(new_infer_ty); - - // Alias is guaranteed to be fully structurally resolved, - // so we can super fold here. - ty.try_super_fold_with(self)? - } else { - alias_ty.try_super_fold_with(self)? - }; + self.fulfill_cx.register_predicate_obligation(infcx, obligation); + let errors = self.fulfill_cx.select_all_or_error(infcx); + if !errors.is_empty() { + return Err(errors); + } + // Alias is guaranteed to be fully structurally resolved, + // so we can super fold here. + let ty = infcx.resolve_vars_if_possible(new_infer_ty); + let result = ty.try_super_fold_with(self)?; self.depth -= 1; Ok(result) } @@ -178,6 +169,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { Ok(t) } + #[instrument(level = "debug", skip(self), ret)] fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ty, infcx.shallow_resolve(ty)); @@ -204,11 +196,11 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { } } + #[instrument(level = "debug", skip(self), ret)] fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> { - let reveal = self.at.param_env.reveal(); let infcx = self.at.infcx; debug_assert_eq!(ct, infcx.shallow_resolve(ct)); - if !needs_normalization(&ct, reveal) { + if !ct.has_projections() { return Ok(ct); } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs new file mode 100644 index 00000000000..911462f4b9a --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs @@ -0,0 +1,25 @@ +use crate::solve::EvalCtxt; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::ty; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn normalize_anon_const( + &mut self, + 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.alias.def_id, goal.predicate.alias.args), + self.tcx() + .type_of(goal.predicate.alias.def_id) + .no_bound_vars() + .expect("const ty should not rely on other generics"), + ) { + self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index d177109c420..5f625831156 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -18,8 +18,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; +mod anon_const; mod inherent; -mod opaques; +mod opaque_types; mod weak_types; impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -31,34 +32,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let def_id = goal.predicate.def_id(); match self.tcx().def_kind(def_id) { DefKind::AssocTy | DefKind::AssocConst => { - // To only compute normalization once for each projection we only - // assemble normalization candidates if the expected term is an - // unconstrained inference variable. - // - // Why: For better cache hits, since if we have an unconstrained RHS then - // there are only as many cache keys as there are (canonicalized) alias - // types in each normalizes-to goal. This also weakens inference in a - // forwards-compatible way so we don't use the value of the RHS term to - // affect candidate assembly for projections. - // - // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal - // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for - // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver, since we're piggybacking off of regular - // goal caching. - if self.term_is_fully_unconstrained(goal) { - match self.tcx().associated_item(def_id).container { - ty::AssocItemContainer::TraitContainer => { + match self.tcx().associated_item(def_id).container { + ty::AssocItemContainer::TraitContainer => { + // To only compute normalization once for each projection we only + // assemble normalization candidates if the expected term is an + // unconstrained inference variable. + // + // Why: For better cache hits, since if we have an unconstrained RHS then + // there are only as many cache keys as there are (canonicalized) alias + // types in each normalizes-to goal. This also weakens inference in a + // forwards-compatible way so we don't use the value of the RHS term to + // affect candidate assembly for projections. + // + // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal + // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for + // `U` and equate it with `u32`. This means that we don't need a separate + // projection cache in the solver, since we're piggybacking off of regular + // goal caching. + if self.term_is_fully_unconstrained(goal) { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) + } else { + self.set_normalizes_to_hack_goal(goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - ty::AssocItemContainer::ImplContainer => { - self.normalize_inherent_associated_type(goal) - } } - } else { - self.set_normalizes_to_hack_goal(goal); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ty::AssocItemContainer::ImplContainer => { + self.normalize_inherent_associated_type(goal) + } } } DefKind::AnonConst => self.normalize_anon_const(goal), @@ -67,26 +68,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), } } - - #[instrument(level = "debug", skip(self), ret)] - fn normalize_anon_const( - &mut self, - goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, - ) -> QueryResult<'tcx> { - if let Some(normalized_const) = self.try_const_eval_resolve( - goal.param_env, - ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args), - self.tcx() - .type_of(goal.predicate.alias.def_id) - .no_bound_vars() - .expect("const ty should not rely on other generics"), - ) { - self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } - } } impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs index 356c3776c04..356c3776c04 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index f663f02f872..3619d02438d 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -18,7 +18,7 @@ use crate::traits::{ Obligation, ObligationCause, PredicateObligation, PredicateObligations, SelectionContext, }; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::Diagnostic; +use rustc_errors::{DiagnosticBuilder, EmissionGuarantee}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; @@ -58,7 +58,7 @@ pub struct OverlapResult<'tcx> { pub involves_placeholder: bool, } -pub fn add_placeholder_note(err: &mut Diagnostic) { +pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut DiagnosticBuilder<'_, G>) { err.note( "this behavior recently changed as a result of a bug fix; \ see rust-lang/rust#56105 for details", diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 1edf6b11fc3..189e1ba54bc 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -62,14 +62,11 @@ pub fn is_const_evaluatable<'tcx>( match unexpanded_ct.kind() { ty::ConstKind::Expr(_) => { - // 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.dcx().span_delayed_bug( - span, - "evaluating `ConstKind::Expr` is not currently supported", - ))) + // FIXME(generic_const_exprs): we have a fully concrete `ConstKind::Expr`, but + // haven't implemented evaluating `ConstKind::Expr` yet, so we are unable to tell + // if it is evaluatable or not. As this is unreachable for now, we can simple ICE + // here. + tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported"); } ty::ConstKind::Unevaluated(uv) => { let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); 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 2d85f84f480..8ae31392b40 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -13,7 +13,7 @@ use hir::def::CtorOf; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, + codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, }; use rustc_hir as hir; @@ -116,12 +116,12 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) - /// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but /// it can also be an `impl Trait` param that needs to be decomposed to a type /// param for cleaner code. -pub fn suggest_restriction<'tcx>( +pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( tcx: TyCtxt<'tcx>, item_id: LocalDefId, hir_generics: &hir::Generics<'tcx>, msg: &str, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, fn_sig: Option<&hir::FnSig<'_>>, projection: Option<&ty::AliasTy<'_>>, trait_pred: ty::PolyTraitPredicate<'tcx>, @@ -240,7 +240,7 @@ pub fn suggest_restriction<'tcx>( impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_restricting_param_bound( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, associated_ty: Option<(&'static str, Ty<'tcx>)>, mut body_id: LocalDefId, @@ -450,7 +450,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_dereferences( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let mut code = obligation.cause.code(); @@ -743,22 +743,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn get_closure_name( &self, def_id: DefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, msg: Cow<'static, str>, ) -> Option<Symbol> { - let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> { - // Get the local name of this closure. This can be inaccurate because - // of the possibility of reassignment, but this should be good enough. - match &kind { - hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => { - Some(ident.name) - } - _ => { - err.note(msg); - None + let get_name = + |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind<'_>| -> Option<Symbol> { + // Get the local name of this closure. This can be inaccurate because + // of the possibility of reassignment, but this should be good enough. + match &kind { + hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => { + Some(ident.name) + } + _ => { + err.note(msg); + None + } } - } - }; + }; let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?); match self.tcx.parent_hir_node(hir_id) { @@ -778,7 +779,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_fn_call( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { // It doesn't make sense to make this suggestion outside of typeck... @@ -894,7 +895,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn check_for_binding_assigned_block_without_tail_expression( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let mut span = obligation.cause.span; @@ -971,7 +972,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_add_clone_to_arg( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); @@ -1156,7 +1157,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, poly_trait_pred: ty::PolyTraitPredicate<'tcx>, has_custom_message: bool, ) -> bool { @@ -1282,6 +1283,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "the full type name has been written to '{}'", file.display() )); + err.note(format!( + "consider using `--verbose` to print full type name to the console" + )); } if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { @@ -1374,7 +1378,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Suggest borrowing the type fn suggest_borrowing_for_object_cast( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, self_ty: Ty<'tcx>, target_ty: Ty<'tcx>, @@ -1410,7 +1414,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_remove_reference( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let mut span = obligation.cause.span; @@ -1529,7 +1533,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { false } - fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) { + fn suggest_remove_await( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + ) { let hir = self.tcx.hir(); if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) @@ -1599,7 +1607,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_change_mut( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let points_at_arg = matches!( @@ -1677,7 +1685,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_semicolon_removal( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { @@ -1731,7 +1739,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// emitted. fn suggest_impl_trait( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { @@ -1913,7 +1921,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn note_conflicting_fn_args( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, cause: &ObligationCauseCode<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, @@ -2120,7 +2128,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_fully_qualified_path( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, item_def_id: DefId, span: Span, trait_ref: DefId, @@ -2185,9 +2193,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// /// Returns `true` if an async-await specific note was added to the diagnostic. #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))] - fn maybe_note_obligation_cause_for_async_await( + fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, obligation: &PredicateObligation<'tcx>, ) -> bool { let hir = self.tcx.hir(); @@ -2421,9 +2429,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// Unconditionally adds the diagnostic note described in /// `maybe_note_obligation_cause_for_async_await`'s documentation comment. #[instrument(level = "debug", skip_all)] - fn note_obligation_cause_for_async_await( + fn note_obligation_cause_for_async_await<G: EmissionGuarantee>( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, interior_or_upvar_span: CoroutineInteriorOrUpvar, is_async: bool, outer_coroutine: Option<DefId>, @@ -2656,10 +2664,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - fn note_obligation_cause_code<T>( + fn note_obligation_cause_code<G: EmissionGuarantee, T>( &self, body_id: LocalDefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, predicate: T, param_env: ty::ParamEnv<'tcx>, cause_code: &ObligationCauseCode<'tcx>, @@ -2861,6 +2869,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "the full name for the type has been written to '{}'", file.display(), )); + err.note(format!( + "consider using `--verbose` to print the full type name to the console" + )); } } ObligationCauseCode::RepeatElementCopy { @@ -3328,6 +3339,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "the full type name has been written to '{}'", file.display(), )); + err.note(format!( + "consider using `--verbose` to print the full type name to the console" + )); } let mut parent_predicate = parent_trait_pred; let mut data = &data.derived; @@ -3381,6 +3395,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "the full type name has been written to '{}'", file.display(), )); + err.note(format!( + "consider using `--verbose` to print the full type name to the console" + )); } } // #74711: avoid a stack overflow @@ -3507,7 +3524,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { )] fn suggest_await_before_try( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, span: Span, @@ -3565,7 +3582,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_floating_point_literal( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::PolyTraitRef<'tcx>, ) { let rhs_span = match obligation.cause.code() { @@ -3589,7 +3606,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_derive( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else { @@ -3655,7 +3672,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_dereferencing_index( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) { if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code() @@ -3675,10 +3692,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn note_function_argument_obligation( + fn note_function_argument_obligation<G: EmissionGuarantee>( &self, body_id: LocalDefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, arg_hir_id: HirId, parent_code: &ObligationCauseCode<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -3860,11 +3877,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn suggest_option_method_if_applicable( + fn suggest_option_method_if_applicable<G: EmissionGuarantee>( &self, failed_pred: ty::Predicate<'tcx>, param_env: ty::ParamEnv<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, expr: &hir::Expr<'_>, ) { let tcx = self.tcx; @@ -3934,7 +3951,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn look_for_iterator_item_mistakes( + fn look_for_iterator_item_mistakes<G: EmissionGuarantee>( &self, assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>], typeck_results: &TypeckResults<'tcx>, @@ -3942,7 +3959,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { param_env: ty::ParamEnv<'tcx>, path_segment: &hir::PathSegment<'_>, args: &[hir::Expr<'_>], - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, ) { let tcx = self.tcx; // Special case for iterator chains, we look at potential failures of `Iterator::Item` @@ -4037,13 +4054,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn point_at_chain( + fn point_at_chain<G: EmissionGuarantee>( &self, expr: &hir::Expr<'_>, typeck_results: &TypeckResults<'tcx>, type_diffs: Vec<TypeError<'tcx>>, param_env: ty::ParamEnv<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, ) { let mut primary_spans = vec![]; let mut span_labels = vec![]; @@ -4279,7 +4296,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// the array into a slice. fn suggest_convert_to_slice( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, candidate_impls: &[ImplCandidate<'tcx>], @@ -4351,7 +4368,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn explain_hrtb_projection( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, pred: ty::PolyTraitPredicate<'tcx>, param_env: ty::ParamEnv<'tcx>, cause: &ObligationCause<'tcx>, @@ -4417,7 +4434,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn suggest_desugaring_async_fn_in_trait( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_ref: ty::PolyTraitRef<'tcx>, ) { // Don't suggest if RTN is active -- we should prefer a where-clause bound instead. @@ -4508,7 +4525,7 @@ fn hint_missing_borrow<'tcx>( found: Ty<'tcx>, expected: Ty<'tcx>, found_node: Node<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { if matches!(found_node, Node::TraitItem(..)) { return; @@ -4867,9 +4884,9 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( /// On `impl` evaluation cycles, look for `Self::AssocTy` restrictions in `where` clauses, explain /// they are not allowed and if possible suggest alternatives. -fn point_at_assoc_type_restriction( +fn point_at_assoc_type_restriction<G: EmissionGuarantee>( tcx: TyCtxt<'_>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, self_ty_str: &str, trait_name: &str, predicate: ty::Predicate<'_>, 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 67bd18d7404..7186b96b40d 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 @@ -21,8 +21,8 @@ use crate::traits::{ }; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, StashKey, StringPart, + codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, + FatalError, MultiSpan, StashKey, StringPart, }; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; @@ -185,7 +185,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { predicate: &T, span: Span, suggest_increasing_limit: bool, - mutate: impl FnOnce(&mut Diagnostic), + mutate: impl FnOnce(&mut DiagnosticBuilder<'_>), ) -> ! where T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>, @@ -193,14 +193,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit); mutate(&mut err); err.emit(); - - self.dcx().abort_if_errors(); - // 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!" - ); + FatalError.raise(); } fn build_overflow_error<T>( @@ -272,7 +265,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { + fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) { let suggested_limit = match self.tcx.recursion_limit() { Limit(0) => Limit(2), limit => limit * 2, @@ -1020,7 +1013,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, trait_ref: ty::TraitRef<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) -> bool { let span = obligation.cause.span; struct V<'v> { @@ -1810,7 +1803,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, body_def_id: LocalDefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, other: bool, param_env: ty::ParamEnv<'tcx>, ) -> bool { @@ -1897,7 +1890,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let other = if other { "other " } else { "" }; - let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| { + let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut DiagnosticBuilder<'_>| { if candidates.is_empty() { return false; } @@ -2032,7 +2025,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { obligation: &PredicateObligation<'tcx>, trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, body_def_id: LocalDefId, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { // This is *almost* equivalent to // `obligation.cause.code().peel_derives()`, but it gives us the @@ -2103,7 +2096,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// a probable version mismatch is added to `err` fn note_version_mismatch( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::PolyTraitRef<'tcx>, ) -> bool { let get_trait_impls = |trait_def_id| { @@ -2572,7 +2565,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn annotate_source_of_ambiguity( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ambiguities: &[ambiguity::Ambiguity], predicate: ty::Predicate<'tcx>, ) { @@ -2715,7 +2708,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }) } - fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>) { + fn note_obligation_cause( + &self, + err: &mut DiagnosticBuilder<'_>, + obligation: &PredicateObligation<'tcx>, + ) { // First, attempt to add note to this error with an async-await-specific // message, and fall back to regular note otherwise. if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { @@ -2744,7 +2741,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { #[instrument(level = "debug", skip_all)] fn suggest_unsized_bound_if_applicable( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, ) { let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = @@ -2770,7 +2767,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) { + fn maybe_suggest_unsized_generics( + &self, + err: &mut DiagnosticBuilder<'_>, + span: Span, + node: Node<'tcx>, + ) { let Some(generics) = node.generics() else { return; }; @@ -2822,7 +2824,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn maybe_indirection_for_unsized( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, item: &Item<'tcx>, param: &GenericParam<'tcx>, ) -> bool { @@ -3016,7 +3018,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn add_tuple_trait_message( &self, obligation_cause_code: &ObligationCauseCode<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, ) { match obligation_cause_code { ObligationCauseCode::RustCall => { @@ -3041,7 +3043,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { obligation: &PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, trait_predicate: &ty::PolyTraitPredicate<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, span: Span, is_fn_trait: bool, suggested: bool, @@ -3122,7 +3124,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn add_help_message_for_fn_trait( &self, trait_ref: ty::PolyTraitRef<'tcx>, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, implemented_kind: ty::ClosureKind, params: ty::Binder<'tcx, Ty<'tcx>>, ) { @@ -3178,7 +3180,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn maybe_add_note_for_unsatisfied_const( &self, _trait_predicate: &ty::PolyTraitPredicate<'tcx>, - _err: &mut Diagnostic, + _err: &mut DiagnosticBuilder<'_>, _span: Span, ) -> UnsatisfiedConst { let unsatisfied_const = UnsatisfiedConst(false); @@ -3304,7 +3306,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { expected_trait_ref.self_ty().error_reported()?; let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else { - return Err(self.dcx().delayed_bug("bound vars outside binder")); + self.dcx().bug("bound vars outside binder"); }; let found_did = match *found_trait_ty.kind() { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 7ad94274634..fd981130af8 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1,5 +1,6 @@ use crate::infer::{InferCtxt, TyOrConstInferVar}; use crate::traits::error_reporting::TypeErrCtxtExt; +use crate::traits::normalize::normalize_with_depth_to; use rustc_data_structures::captures::Captures; use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; @@ -312,7 +313,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { if obligation.predicate.has_projections() { let mut obligations = Vec::new(); - let predicate = crate::traits::project::try_normalize_with_depth_to( + let predicate = normalize_with_depth_to( &mut self.selcx, obligation.param_env, obligation.cause.clone(), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 7caaccab63b..cac53796747 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -9,6 +9,7 @@ mod engine; pub mod error_reporting; mod fulfill; pub mod misc; +pub mod normalize; mod object_safety; pub mod outlives_bounds; pub mod project; @@ -40,17 +41,15 @@ use rustc_span::Span; use std::fmt::Debug; use std::ops::ControlFlow; -pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; - pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; pub use self::coherence::{OrphanCheckErr, OverlapResult}; pub use self::engine::{ObligationCtxt, TraitEngineExt}; pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; +pub use self::normalize::NormalizeExt; pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::object_safety_violations_for_assoc_item; pub use self::object_safety::ObjectSafetyViolation; -pub use self::project::NormalizeExt; pub use self::project::{normalize_inherent_projection, normalize_projection_type}; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; @@ -68,6 +67,7 @@ pub use self::util::{ }; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; +pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; pub use rustc_infer::traits::*; @@ -119,9 +119,7 @@ pub fn predicates_for_generics<'tcx>( /// Determines whether the type `ty` is known to meet `bound` and /// returns true if so. Returns false if `ty` either does not meet -/// `bound` or is not known to meet bound (note that this is -/// conservative towards *no impl*, which is the opposite of the -/// `evaluate` methods). +/// `bound` or is not known to meet bound. pub fn type_known_to_meet_bound_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -129,50 +127,8 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( def_id: DefId, ) -> bool { let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]); - pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) -} - -/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? -/// -/// Ping me on zulip if you want to use this method and need help with finding -/// an appropriate replacement. -#[instrument(level = "debug", skip(infcx, param_env, pred), ret)] -fn pred_known_to_hold_modulo_regions<'tcx>( - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pred: impl ToPredicate<'tcx>, -) -> bool { - let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred); - - let result = infcx.evaluate_obligation_no_overflow(&obligation); - debug!(?result); - - if result.must_apply_modulo_regions() { - true - } else if result.may_apply() { - // Sometimes obligations are ambiguous because the recursive evaluator - // is not smart enough, so we fall back to fulfillment when we're not certain - // that an obligation holds or not. Even still, we must make sure that - // the we do no inference in the process of checking this obligation. - let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); - infcx.probe(|_| { - let ocx = ObligationCtxt::new(infcx); - ocx.register_obligation(obligation); - - let errors = ocx.select_all_or_error(); - match errors.as_slice() { - // Only known to hold if we did no inference. - [] => infcx.shallow_resolve(goal) == goal, - - errors => { - debug!(?errors); - false - } - } - }) - } else { - false - } + let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref); + infcx.predicate_must_hold_modulo_regions(&obligation) } #[instrument(level = "debug", skip(tcx, elaborated_env))] @@ -216,7 +172,9 @@ fn do_normalize_predicates<'tcx>( // the normalized predicates. let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { - tcx.dcx().span_delayed_bug( + // @lcnr: Let's still ICE here for now. I want a test case + // for that. + tcx.dcx().span_bug( span, format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"), ); diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs new file mode 100644 index 00000000000..429e5a5d7a4 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -0,0 +1,419 @@ +//! Deeply normalize types using the old trait solver. +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_infer::infer::at::At; +use rustc_infer::infer::InferOk; +use rustc_infer::traits::PredicateObligation; +use rustc_infer::traits::{FulfillmentError, Normalized, Obligation, TraitEngine}; +use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder}; +use rustc_middle::ty::{TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitableExt}; + +use super::error_reporting::TypeErrCtxtExt; +use super::SelectionContext; +use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; + +#[extension(pub trait NormalizeExt<'tcx>)] +impl<'tcx> At<'_, 'tcx> { + /// Normalize a value using the `AssocTypeNormalizer`. + /// + /// This normalization should be used when the type contains inference variables or the + /// projection may be fallible. + fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> { + if self.infcx.next_trait_solver() { + InferOk { value, obligations: Vec::new() } + } else { + let mut selcx = SelectionContext::new(self.infcx); + let Normalized { value, obligations } = + normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value); + InferOk { value, obligations } + } + } + + /// Deeply normalizes `value`, replacing all aliases which can by normalized in + /// the current environment. In the new solver this errors in case normalization + /// fails or is ambiguous. + /// + /// In the old solver this simply uses `normalizes` and adds the nested obligations + /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the + /// same goals in both a temporary and the shared context which negatively impacts + /// performance as these don't share caching. + /// + /// FIXME(-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 + /// rename this function to `At::fully_normalize`. + fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>( + self, + value: T, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result<T, Vec<FulfillmentError<'tcx>>> { + if self.infcx.next_trait_solver() { + crate::solve::deeply_normalize(self, value) + } else { + let value = self + .normalize(value) + .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); + let errors = fulfill_cx.select_where_possible(self.infcx); + let value = self.infcx.resolve_vars_if_possible(value); + if errors.is_empty() { Ok(value) } else { Err(errors) } + } + } +} + +/// As `normalize`, but with a custom depth. +pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + value: T, +) -> Normalized<'tcx, T> +where + T: TypeFoldable<TyCtxt<'tcx>>, +{ + let mut obligations = Vec::new(); + let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations); + Normalized { value, obligations } +} + +#[instrument(level = "info", skip(selcx, param_env, cause, obligations))] +pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + value: T, + obligations: &mut Vec<PredicateObligation<'tcx>>, +) -> T +where + T: TypeFoldable<TyCtxt<'tcx>>, +{ + debug!(obligations.len = obligations.len()); + let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); + let result = ensure_sufficient_stack(|| normalizer.fold(value)); + debug!(?result, obligations.len = normalizer.obligations.len()); + debug!(?normalizer.obligations,); + result +} + +pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( + value: &T, + reveal: Reveal, +) -> bool { + let mut flags = ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_WEAK + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_CT_PROJECTION; + + match reveal { + Reveal::UserFacing => {} + Reveal::All => flags |= ty::TypeFlags::HAS_TY_OPAQUE, + } + + value.has_type_flags(flags) +} + +struct AssocTypeNormalizer<'a, 'b, 'tcx> { + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + obligations: &'a mut Vec<PredicateObligation<'tcx>>, + depth: usize, + universes: Vec<Option<ty::UniverseIndex>>, +} + +impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { + fn new( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &'a mut Vec<PredicateObligation<'tcx>>, + ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { + debug_assert!(!selcx.infcx.next_trait_solver()); + AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] } + } + + fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T { + let value = self.selcx.infcx.resolve_vars_if_possible(value); + debug!(?value); + + assert!( + !value.has_escaping_bound_vars(), + "Normalizing {value:?} without wrapping in a `Binder`" + ); + + if !needs_normalization(&value, self.param_env.reveal()) { + value + } else { + value.fold_with(self) + } + } +} + +impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.selcx.tcx() + } + + fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.universes.push(None); + let t = t.super_fold_with(self); + self.universes.pop(); + t + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !needs_normalization(&ty, self.param_env.reveal()) { + return ty; + } + + let (kind, data) = match *ty.kind() { + ty::Alias(kind, alias_ty) => (kind, alias_ty), + _ => return ty.super_fold_with(self), + }; + + // We try to be a little clever here as a performance optimization in + // cases where there are nested projections under binders. + // For example: + // ``` + // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>) + // ``` + // We normalize the args on the projection before the projecting, but + // if we're naive, we'll + // replace bound vars on inner, project inner, replace placeholders on inner, + // replace bound vars on outer, project outer, replace placeholders on outer + // + // However, if we're a bit more clever, we can replace the bound vars + // on the entire type before normalizing nested projections, meaning we + // replace bound vars on outer, project inner, + // project outer, replace placeholders on outer + // + // This is possible because the inner `'a` will already be a placeholder + // when we need to normalize the inner projection + // + // On the other hand, this does add a bit of complexity, since we only + // replace bound vars if the current type is a `Projection` and we need + // to make sure we don't forget to fold the args regardless. + + match kind { + ty::Opaque => { + // Only normalize `impl Trait` outside of type inference, usually in codegen. + match self.param_env.reveal() { + Reveal::UserFacing => ty.super_fold_with(self), + + Reveal::All => { + let recursion_limit = self.interner().recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.selcx.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + true, + |_| {}, + ); + } + + let args = data.args.fold_with(self); + let generic_ty = self.interner().type_of(data.def_id); + let concrete_ty = generic_ty.instantiate(self.interner(), args); + self.depth += 1; + let folded_ty = self.fold_ty(concrete_ty); + self.depth -= 1; + folded_ty + } + } + } + + ty::Projection if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + let data = data.fold_with(self); + let normalized_ty = project::normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ); + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty.ty().unwrap() + } + + ty::Projection => { + // If there are escaping bound vars, we temporarily replace the + // bound vars with placeholders. Note though, that in the case + // that we still can't project for whatever reason (e.g. self + // type isn't known enough), we *can't* register an obligation + // and return an inference variable (since then that obligation + // would have bound vars and that's a can of worms). Instead, + // we just give up and fall back to pretending like we never tried! + // + // Note: this isn't necessarily the final approach here; we may + // want to figure out how to register obligations with escaping vars + // or handle this some other way. + + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let normalized_ty = project::opt_normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + .ok() + .flatten() + .map(|term| term.ty().unwrap()) + .map(|normalized_ty| { + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + normalized_ty, + ) + }) + .unwrap_or_else(|| ty.super_fold_with(self)); + + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty + } + ty::Weak => { + let recursion_limit = self.interner().recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.selcx.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + false, + |diag| { + diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); + }, + ); + } + + let infcx = self.selcx.infcx; + self.obligations.extend( + infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map( + |(mut predicate, span)| { + if data.has_escaping_bound_vars() { + (predicate, ..) = BoundVarReplacer::replace_bound_vars( + infcx, + &mut self.universes, + predicate, + ); + } + let mut cause = self.cause.clone(); + cause.map_code(|code| { + ObligationCauseCode::TypeAlias(code, span, data.def_id) + }); + Obligation::new(infcx.tcx, cause, self.param_env, predicate) + }, + ), + ); + self.depth += 1; + let res = infcx + .tcx + .type_of(data.def_id) + .instantiate(infcx.tcx, data.args) + .fold_with(self); + self.depth -= 1; + res + } + + ty::Inherent if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + + let data = data.fold_with(self); + + project::normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + } + + ty::Inherent => { + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let ty = project::normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ); + + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + ty, + ) + } + } + } + + #[instrument(skip(self), level = "debug")] + fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { + let tcx = self.selcx.tcx(); + if tcx.features().generic_const_exprs + || !needs_normalization(&constant, self.param_env.reveal()) + { + constant + } else { + let constant = constant.super_fold_with(self); + debug!(?constant, ?self.param_env); + with_replaced_escaping_bound_vars( + self.selcx.infcx, + &mut self.universes, + constant, + |constant| constant.normalize(tcx, self.param_env), + ) + } + } + + #[inline] + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { + p.super_fold_with(self) + } else { + p + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 6825dd4ac71..c4110df45db 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -62,9 +62,10 @@ fn implied_outlives_bounds<'a, 'tcx>( }; let mut constraints = QueryRegionConstraints::default(); + let span = infcx.tcx.def_span(body_id); let Ok(InferOk { value: mut bounds, obligations }) = infcx .instantiate_nll_query_response_and_region_obligations( - &ObligationCause::dummy(), + &ObligationCause::dummy_with_span(span), param_env, &canonical_var_values, canonical_result, @@ -80,8 +81,6 @@ fn implied_outlives_bounds<'a, 'tcx>( bounds.retain(|bound| !bound.has_placeholders()); if !constraints.is_empty() { - let span = infcx.tcx.def_span(body_id); - debug!(?constraints); if !constraints.member_constraints.is_empty() { span_bug!(span, "{:#?}", constraints.member_constraints); @@ -101,7 +100,7 @@ fn implied_outlives_bounds<'a, 'tcx>( let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.dcx().span_delayed_bug( + infcx.dcx().span_bug( span, "implied_outlives_bounds failed to solve obligations from instantiation", ); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 279c0003187..f8de19043e1 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1,5 +1,7 @@ //! Code for projecting associated types out of trait references. +use std::ops::ControlFlow; + use super::check_args_compatible; use super::specialization_graph; use super::translate_args; @@ -18,8 +20,9 @@ use rustc_middle::traits::ImplSourceUserDefinedData; use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{BoundRegionConversionTime, InferCtxt, InferOk}; -use crate::traits::error_reporting::TypeErrCtxtExt as _; +use crate::infer::{BoundRegionConversionTime, InferOk}; +use crate::traits::normalize::normalize_with_depth; +use crate::traits::normalize::normalize_with_depth_to; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::select::ProjectionMatchesProjection; use rustc_data_structures::sso::SsoHashSet; @@ -27,21 +30,14 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::at::At; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_infer::traits::FulfillmentError; -use rustc_infer::traits::ObligationCauseCode; -use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; -use std::collections::BTreeMap; -use std::ops::ControlFlow; - pub use rustc_middle::traits::Reveal; pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; @@ -52,55 +48,6 @@ pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::AliasTy<'tcx>>; pub(super) struct InProgress; -#[extension(pub trait NormalizeExt<'tcx>)] -impl<'tcx> At<'_, 'tcx> { - /// Normalize a value using the `AssocTypeNormalizer`. - /// - /// This normalization should be used when the type contains inference variables or the - /// projection may be fallible. - fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> { - if self.infcx.next_trait_solver() { - InferOk { value, obligations: Vec::new() } - } else { - let mut selcx = SelectionContext::new(self.infcx); - let Normalized { value, obligations } = - normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value); - InferOk { value, obligations } - } - } - - /// Deeply normalizes `value`, replacing all aliases which can by normalized in - /// the current environment. In the new solver this errors in case normalization - /// fails or is ambiguous. This only normalizes opaque types with `Reveal::All`. - /// - /// In the old solver this simply uses `normalizes` and adds the nested obligations - /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the - /// same goals in both a temporary and the shared context which negatively impacts - /// performance as these don't share caching. - /// - /// FIXME(-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 - /// rename this function to `At::fully_normalize`. - fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>( - self, - value: T, - fulfill_cx: &mut dyn TraitEngine<'tcx>, - ) -> Result<T, Vec<FulfillmentError<'tcx>>> { - if self.infcx.next_trait_solver() { - crate::solve::deeply_normalize(self, value) - } else { - let value = self - .normalize(value) - .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); - let errors = fulfill_cx.select_where_possible(self.infcx); - let value = self.infcx.resolve_vars_if_possible(value); - if errors.is_empty() { Ok(value) } else { Err(errors) } - } - } -} - /// When attempting to resolve `<T as TraitRef>::Name` ... #[derive(Debug)] pub enum ProjectionError<'tcx> { @@ -338,770 +285,6 @@ fn project_and_unify_type<'cx, 'tcx>( } } -/// As `normalize`, but with a custom depth. -pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - value: T, -) -> Normalized<'tcx, T> -where - T: TypeFoldable<TyCtxt<'tcx>>, -{ - let mut obligations = Vec::new(); - let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations); - Normalized { value, obligations } -} - -#[instrument(level = "info", skip(selcx, param_env, cause, obligations))] -pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - value: T, - obligations: &mut Vec<PredicateObligation<'tcx>>, -) -> T -where - T: TypeFoldable<TyCtxt<'tcx>>, -{ - debug!(obligations.len = obligations.len()); - let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); - let result = ensure_sufficient_stack(|| normalizer.fold(value)); - debug!(?result, obligations.len = normalizer.obligations.len()); - debug!(?normalizer.obligations,); - result -} - -#[instrument(level = "info", skip(selcx, param_env, cause, obligations))] -pub(crate) fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - value: T, - obligations: &mut Vec<PredicateObligation<'tcx>>, -) -> T -where - T: TypeFoldable<TyCtxt<'tcx>>, -{ - debug!(obligations.len = obligations.len()); - let mut normalizer = AssocTypeNormalizer::new_without_eager_inference_replacement( - selcx, - param_env, - cause, - depth, - obligations, - ); - let result = ensure_sufficient_stack(|| normalizer.fold(value)); - debug!(?result, obligations.len = normalizer.obligations.len()); - debug!(?normalizer.obligations,); - result -} - -pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( - value: &T, - reveal: Reveal, -) -> bool { - match reveal { - Reveal::UserFacing => value.has_type_flags( - ty::TypeFlags::HAS_TY_PROJECTION - | ty::TypeFlags::HAS_TY_INHERENT - | ty::TypeFlags::HAS_CT_PROJECTION, - ), - Reveal::All => value.has_type_flags( - ty::TypeFlags::HAS_TY_PROJECTION - | ty::TypeFlags::HAS_TY_INHERENT - | ty::TypeFlags::HAS_TY_OPAQUE - | ty::TypeFlags::HAS_CT_PROJECTION, - ), - } -} - -struct AssocTypeNormalizer<'a, 'b, 'tcx> { - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - obligations: &'a mut Vec<PredicateObligation<'tcx>>, - depth: usize, - universes: Vec<Option<ty::UniverseIndex>>, - /// If true, when a projection is unable to be completed, an inference - /// variable will be created and an obligation registered to project to that - /// inference variable. Also, constants will be eagerly evaluated. - eager_inference_replacement: bool, -} - -impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { - fn new( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - obligations: &'a mut Vec<PredicateObligation<'tcx>>, - ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { - debug_assert!(!selcx.infcx.next_trait_solver()); - AssocTypeNormalizer { - selcx, - param_env, - cause, - obligations, - depth, - universes: vec![], - eager_inference_replacement: true, - } - } - - fn new_without_eager_inference_replacement( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - obligations: &'a mut Vec<PredicateObligation<'tcx>>, - ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { - AssocTypeNormalizer { - selcx, - param_env, - cause, - obligations, - depth, - universes: vec![], - eager_inference_replacement: false, - } - } - - fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T { - let value = self.selcx.infcx.resolve_vars_if_possible(value); - debug!(?value); - - assert!( - !value.has_escaping_bound_vars(), - "Normalizing {value:?} without wrapping in a `Binder`" - ); - - if !needs_normalization(&value, self.param_env.reveal()) { - value - } else { - value.fold_with(self) - } - } -} - -impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.selcx.tcx() - } - - fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.universes.push(None); - let t = t.super_fold_with(self); - self.universes.pop(); - t - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !needs_normalization(&ty, self.param_env.reveal()) { - return ty; - } - - let (kind, data) = match *ty.kind() { - ty::Alias(kind, alias_ty) => (kind, alias_ty), - _ => return ty.super_fold_with(self), - }; - - // We try to be a little clever here as a performance optimization in - // cases where there are nested projections under binders. - // For example: - // ``` - // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>) - // ``` - // We normalize the args on the projection before the projecting, but - // if we're naive, we'll - // replace bound vars on inner, project inner, replace placeholders on inner, - // replace bound vars on outer, project outer, replace placeholders on outer - // - // However, if we're a bit more clever, we can replace the bound vars - // on the entire type before normalizing nested projections, meaning we - // replace bound vars on outer, project inner, - // project outer, replace placeholders on outer - // - // This is possible because the inner `'a` will already be a placeholder - // when we need to normalize the inner projection - // - // On the other hand, this does add a bit of complexity, since we only - // replace bound vars if the current type is a `Projection` and we need - // to make sure we don't forget to fold the args regardless. - - match kind { - ty::Opaque => { - // Only normalize `impl Trait` outside of type inference, usually in codegen. - match self.param_env.reveal() { - Reveal::UserFacing => ty.super_fold_with(self), - - Reveal::All => { - let recursion_limit = self.interner().recursion_limit(); - if !recursion_limit.value_within_limit(self.depth) { - self.selcx.infcx.err_ctxt().report_overflow_error( - &ty, - self.cause.span, - true, - |_| {}, - ); - } - - let args = data.args.fold_with(self); - let generic_ty = self.interner().type_of(data.def_id); - let concrete_ty = generic_ty.instantiate(self.interner(), args); - self.depth += 1; - let folded_ty = self.fold_ty(concrete_ty); - self.depth -= 1; - folded_ty - } - } - } - - ty::Projection if !data.has_escaping_bound_vars() => { - // This branch is *mostly* just an optimization: when we don't - // have escaping bound vars, we don't need to replace them with - // placeholders (see branch below). *Also*, we know that we can - // register an obligation to *later* project, since we know - // there won't be bound vars there. - let data = data.fold_with(self); - let normalized_ty = if self.eager_inference_replacement { - normalize_projection_type( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - } else { - opt_normalize_projection_type( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - .ok() - .flatten() - .unwrap_or_else(|| ty.super_fold_with(self).into()) - }; - debug!( - ?self.depth, - ?ty, - ?normalized_ty, - obligations.len = ?self.obligations.len(), - "AssocTypeNormalizer: normalized type" - ); - normalized_ty.ty().unwrap() - } - - ty::Projection => { - // If there are escaping bound vars, we temporarily replace the - // bound vars with placeholders. Note though, that in the case - // that we still can't project for whatever reason (e.g. self - // type isn't known enough), we *can't* register an obligation - // and return an inference variable (since then that obligation - // would have bound vars and that's a can of worms). Instead, - // we just give up and fall back to pretending like we never tried! - // - // Note: this isn't necessarily the final approach here; we may - // want to figure out how to register obligations with escaping vars - // or handle this some other way. - - let infcx = self.selcx.infcx; - let (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - let data = data.fold_with(self); - let normalized_ty = opt_normalize_projection_type( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - .ok() - .flatten() - .map(|term| term.ty().unwrap()) - .map(|normalized_ty| { - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - normalized_ty, - ) - }) - .unwrap_or_else(|| ty.super_fold_with(self)); - - debug!( - ?self.depth, - ?ty, - ?normalized_ty, - obligations.len = ?self.obligations.len(), - "AssocTypeNormalizer: normalized type" - ); - normalized_ty - } - ty::Weak => { - let recursion_limit = self.interner().recursion_limit(); - if !recursion_limit.value_within_limit(self.depth) { - self.selcx.infcx.err_ctxt().report_overflow_error( - &ty, - self.cause.span, - false, - |diag| { - diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); - }, - ); - } - - let infcx = self.selcx.infcx; - self.obligations.extend( - infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map( - |(mut predicate, span)| { - if data.has_escaping_bound_vars() { - (predicate, ..) = BoundVarReplacer::replace_bound_vars( - infcx, - &mut self.universes, - predicate, - ); - } - let mut cause = self.cause.clone(); - cause.map_code(|code| { - ObligationCauseCode::TypeAlias(code, span, data.def_id) - }); - Obligation::new(infcx.tcx, cause, self.param_env, predicate) - }, - ), - ); - self.depth += 1; - let res = infcx - .tcx - .type_of(data.def_id) - .instantiate(infcx.tcx, data.args) - .fold_with(self); - self.depth -= 1; - res - } - - ty::Inherent if !data.has_escaping_bound_vars() => { - // This branch is *mostly* just an optimization: when we don't - // have escaping bound vars, we don't need to replace them with - // placeholders (see branch below). *Also*, we know that we can - // register an obligation to *later* project, since we know - // there won't be bound vars there. - - let data = data.fold_with(self); - - // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement` - // here like `ty::Projection`? - normalize_inherent_projection( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - } - - ty::Inherent => { - let infcx = self.selcx.infcx; - let (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - let data = data.fold_with(self); - let ty = normalize_inherent_projection( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ); - - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - ty, - ) - } - } - } - - #[instrument(skip(self), level = "debug")] - fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { - let tcx = self.selcx.tcx(); - if tcx.features().generic_const_exprs - || !needs_normalization(&constant, self.param_env.reveal()) - { - constant - } else { - let constant = constant.super_fold_with(self); - debug!(?constant, ?self.param_env); - with_replaced_escaping_bound_vars( - self.selcx.infcx, - &mut self.universes, - constant, - |constant| constant.normalize(tcx, self.param_env), - ) - } - } - - #[inline] - fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { - p.super_fold_with(self) - } else { - p - } - } -} - -pub struct BoundVarReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'tcx>, - // These three maps track the bound variable that were replaced by placeholders. It might be - // nice to remove these since we already have the `kind` in the placeholder; we really just need - // the `var` (but we *could* bring that into scope if we were to track them as we pass them). - mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, - mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, - mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - // The current depth relative to *this* folding, *not* the entire normalization. In other words, - // the depth of binders we've passed here. - current_index: ty::DebruijnIndex, - // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: - // we don't actually create a universe until we see a bound var we have to replace. - universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, -} - -/// Executes `f` on `value` after replacing all escaping bound variables with placeholders -/// and then replaces these placeholders with the original bound variables in the result. -/// -/// In most places, bound variables should be replaced right when entering a binder, making -/// this function unnecessary. However, normalization currently does not do that, so we have -/// to do this lazily. -/// -/// You should not add any additional uses of this function, at least not without first -/// discussing it with t-types. -/// -/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during -/// normalization as well, at which point this function will be unnecessary and can be removed. -pub fn with_replaced_escaping_bound_vars< - 'a, - 'tcx, - T: TypeFoldable<TyCtxt<'tcx>>, - R: TypeFoldable<TyCtxt<'tcx>>, ->( - infcx: &'a InferCtxt<'tcx>, - universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, - value: T, - f: impl FnOnce(T) -> R, -) -> R { - if value.has_escaping_bound_vars() { - let (value, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value); - let result = f(value); - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - universe_indices, - result, - ) - } else { - f(value) - } -} - -impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { - /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that - /// use a binding level above `universe_indices.len()`, we fail. - pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>( - infcx: &'me InferCtxt<'tcx>, - universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, - value: T, - ) -> ( - T, - BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, - BTreeMap<ty::PlaceholderType, ty::BoundTy>, - BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - ) { - let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new(); - let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new(); - let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new(); - - let mut replacer = BoundVarReplacer { - infcx, - mapped_regions, - mapped_types, - mapped_consts, - current_index: ty::INNERMOST, - universe_indices, - }; - - let value = value.fold_with(&mut replacer); - - (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) - } - - fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { - let infcx = self.infcx; - let index = - self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; - let universe = self.universe_indices[index].unwrap_or_else(|| { - for i in self.universe_indices.iter_mut().take(index + 1) { - *i = i.or_else(|| Some(infcx.create_next_universe())) - } - self.universe_indices[index].unwrap() - }); - universe - } -} - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - 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::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); - ty::Region::new_placeholder(self.infcx.tcx, p) - } - _ => r, - } - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match *t.kind() { - ty::Bound(debruijn, _) - if debruijn.as_usize() + 1 - > self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderType { universe, bound: bound_ty }; - self.mapped_types.insert(p, bound_ty); - Ty::new_placeholder(self.infcx.tcx, p) - } - _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), - _ => t, - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - match ct.kind() { - ty::ConstKind::Bound(debruijn, _) - if debruijn.as_usize() + 1 - > self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderConst { universe, bound: bound_const }; - self.mapped_consts.insert(p, bound_const); - ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty()) - } - _ => ct.super_fold_with(self), - } - } - - fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } - } -} - -/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. -pub struct PlaceholderReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'tcx>, - mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, - mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, - mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - universe_indices: &'me [Option<ty::UniverseIndex>], - current_index: ty::DebruijnIndex, -} - -impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { - pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>( - infcx: &'me InferCtxt<'tcx>, - mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, - mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, - mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, - universe_indices: &'me [Option<ty::UniverseIndex>], - value: T, - ) -> T { - let mut replacer = PlaceholderReplacer { - infcx, - mapped_regions, - mapped_types, - mapped_consts, - universe_indices, - current_index: ty::INNERMOST, - }; - value.fold_with(&mut replacer) - } -} - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - if !t.has_placeholders() && !t.has_infer() { - return t; - } - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - - fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { - let r1 = match *r0 { - ty::ReVar(vid) => self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.infcx.tcx, vid), - _ => r0, - }; - - let r2 = match *r1 { - ty::RePlaceholder(p) => { - let replace_var = self.mapped_regions.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - ty::Region::new_bound(self.interner(), db, *replace_var) - } - None => r1, - } - } - _ => r1, - }; - - debug!(?r0, ?r1, ?r2, "fold_region"); - - r2 - } - - 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); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - Ty::new_bound(self.infcx.tcx, db, *replace_var) - } - None => { - if ty.has_infer() { - ty.super_fold_with(self) - } else { - ty - } - } - } - } - - _ 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 { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) - } - None => { - if ct.has_infer() { - ct.super_fold_with(self) - } else { - ct - } - } - } - } else { - ct.super_fold_with(self) - } - } -} - /// The guts of `normalize`: normalize a specific projection like `<T /// as Trait>::Item`. The result is always a type (and possibly /// additional obligations). If ambiguity arises, which implies that @@ -1146,7 +329,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>( /// function takes an obligations vector and appends to it directly, which is /// slightly uglier but avoids the need for an extra short-lived allocation. #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] -fn opt_normalize_projection_type<'a, 'b, 'tcx>( +pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::AliasTy<'tcx>, @@ -1250,14 +433,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); let mut result = if projected_term.has_projections() { - let mut normalizer = AssocTypeNormalizer::new( + let normalized_ty = normalize_with_depth_to( selcx, param_env, cause, depth + 1, + projected_term, &mut projected_obligations, ); - let normalized_ty = normalizer.fold(projected_term); Normalized { value: normalized_ty, obligations: projected_obligations } } else { @@ -1462,11 +645,9 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>( match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { Ok(mut ok) => obligations.append(&mut ok.obligations), Err(_) => { - tcx.dcx().span_delayed_bug( + tcx.dcx().span_bug( cause.span, - format!( - "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not" - ), + format!("{self_ty:?} was equal to {impl_ty:?} during selection but now it is not"), ); } } @@ -2011,7 +1192,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( obligation.cause.span, format!("Cannot project an associated type from `{impl_source:?}`"), ); - return Err(()); + return Err(()) } }; diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 0f6c0abd280..70fd0b7e50b 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -1,12 +1,13 @@ //! Code for the 'normalization' query. This consists of a wrapper //! which folds deeply, invoking the underlying -//! `normalize_projection_ty` query when it encounters projections. +//! `normalize_canonicalized_projection_ty` query when it encounters projections. use crate::infer::at::At; use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; use crate::traits::error_reporting::TypeErrCtxtExt; -use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; +use crate::traits::normalize::needs_normalization; +use crate::traits::{BoundVarReplacer, PlaceholderReplacer}; use crate::traits::{ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -270,9 +271,9 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> debug!("QueryNormalizer: c_data = {:#?}", c_data); debug!("QueryNormalizer: orig_values = {:#?}", orig_values); let result = match kind { - ty::Projection => tcx.normalize_projection_ty(c_data), - ty::Weak => tcx.normalize_weak_ty(c_data), - ty::Inherent => tcx.normalize_inherent_projection_ty(c_data), + ty::Projection => tcx.normalize_canonicalized_projection_ty(c_data), + ty::Weak => tcx.normalize_canonicalized_weak_ty(c_data), + ty::Inherent => tcx.normalize_canonicalized_inherent_projection_ty(c_data), kind => unreachable!("did not expect {kind:?} due to match arm above"), }?; // We don't expect ambiguity. @@ -307,10 +308,10 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> } else { result.normalized_ty }; - // `tcx.normalize_projection_ty` may normalize to a type that still has - // unevaluated consts, so keep normalizing here if that's the case. - // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type - // and we need to continue folding it to reveal the TAIT behind it. + // `tcx.normalize_canonicalized_projection_ty` may normalize to a type that + // still has unevaluated consts, so keep normalizing here if that's the case. + // Similarly, `tcx.normalize_canonicalized_weak_ty` will only unwrap one layer + // of type and we need to continue folding it to reveal the TAIT behind it. if res != ty && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak) { @@ -335,7 +336,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> let constant = constant.try_super_fold_with(self)?; debug!(?constant, ?self.param_env); - Ok(crate::traits::project::with_replaced_escaping_bound_vars( + Ok(crate::traits::with_replaced_escaping_bound_vars( self.infcx, &mut self.universes, constant, 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 fb09f094d37..12ee778ee05 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 @@ -190,10 +190,9 @@ where } } if !progress { - return Err(infcx.dcx().span_delayed_bug( - span, - format!("ambiguity processing {obligations:?} from {self:?}"), - )); + infcx + .dcx() + .span_bug(span, format!("ambiguity processing {obligations:?} from {self:?}")); } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index e4a70c537d2..f76be876948 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::{ }; use rustc_span::def_id::DefId; -use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; +use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::vtable::{ count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5bcf46a96ed..1146f869fc1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -8,7 +8,6 @@ use self::SelectionCandidate::*; use super::coherence::{self, Conflict}; use super::const_evaluatable; use super::project; -use super::project::normalize_with_depth_to; use super::project::ProjectionTyObligation; use super::util; use super::util::closure_trait_ref_and_return_type; @@ -22,14 +21,15 @@ use super::{ use crate::infer::{InferCtxt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt; use crate::traits::error_reporting::TypeErrCtxtExt; -use crate::traits::project::try_normalize_with_depth_to; +use crate::traits::normalize::normalize_with_depth; +use crate::traits::normalize::normalize_with_depth_to; use crate::traits::project::ProjectAndUnifyResult; use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; use crate::traits::Unimplemented; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::Diagnostic; +use rustc_errors::{DiagnosticBuilder, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::BoundRegionConversionTime; @@ -70,7 +70,10 @@ pub enum IntercrateAmbiguityCause<'tcx> { 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) { + pub fn add_intercrate_ambiguity_hint<G: EmissionGuarantee>( + &self, + err: &mut DiagnosticBuilder<'_, G>, + ) { err.note(self.intercrate_ambiguity_hint()); } @@ -1070,7 +1073,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && fresh_trait_pred.is_global() { let mut nested_obligations = Vec::new(); - let predicate = try_normalize_with_depth_to( + let predicate = normalize_with_depth_to( this, param_env, obligation.cause.clone(), @@ -1662,7 +1665,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), @@ -1718,7 +1721,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); let infer_projection = if potentially_unnormalized_candidates { ensure_sufficient_stack(|| { - project::normalize_with_depth_to( + normalize_with_depth_to( self, obligation.param_env, obligation.cause.clone(), @@ -2383,7 +2386,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let placeholder_ty = self.infcx.enter_forall_and_leak_universe(ty); let Normalized { value: normalized_ty, mut obligations } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + normalize_with_depth( self, param_env, cause.clone(), @@ -2480,7 +2483,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index e1a49ecf1b6..56bc2f2cf25 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -20,7 +20,7 @@ use crate::traits::{ self, coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt, }; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{codes::*, DelayDm, Diagnostic}; +use rustc_errors::{codes::*, DelayDm, DiagnosticBuilder, EmissionGuarantee}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArgs, GenericArgsRef}; @@ -395,11 +395,11 @@ fn report_conflicting_impls<'tcx>( // Work to be done after we've built the DiagnosticBuilder. We have to define it // now because the lint emit methods don't return back the DiagnosticBuilder // that's passed in. - fn decorate<'tcx>( + fn decorate<'tcx, G: EmissionGuarantee>( tcx: TyCtxt<'tcx>, overlap: &OverlapError<'tcx>, impl_span: Span, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_, G>, ) { if (overlap.trait_ref, overlap.self_ty).references_error() { err.downgrade_to_delayed_bug(); diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index af172eb0713..d66c4004ef5 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,11 +1,14 @@ +use std::collections::BTreeMap; + use super::NormalizeExt; use super::{ObligationCause, PredicateObligation, SelectionContext}; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_hir::def_id::DefId; -use rustc_infer::infer::InferOk; +use rustc_infer::infer::{InferCtxt, InferOk}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_span::Span; use smallvec::SmallVec; @@ -43,7 +46,7 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> { /// trait aliases. pub fn label_with_exp_info( &self, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_>, top_label: &'static str, use_desc: &str, ) { @@ -382,3 +385,336 @@ pub fn check_args_compatible<'tcx>( let args = &args[0..generics.count().min(args.len())]; check_args_compatible_inner(tcx, generics, args) } + +/// Executes `f` on `value` after replacing all escaping bound variables with placeholders +/// and then replaces these placeholders with the original bound variables in the result. +/// +/// In most places, bound variables should be replaced right when entering a binder, making +/// this function unnecessary. However, normalization currently does not do that, so we have +/// to do this lazily. +/// +/// You should not add any additional uses of this function, at least not without first +/// discussing it with t-types. +/// +/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during +/// normalization as well, at which point this function will be unnecessary and can be removed. +pub fn with_replaced_escaping_bound_vars< + 'a, + 'tcx, + T: TypeFoldable<TyCtxt<'tcx>>, + R: TypeFoldable<TyCtxt<'tcx>>, +>( + infcx: &'a InferCtxt<'tcx>, + universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, + value: T, + f: impl FnOnce(T) -> R, +) -> R { + if value.has_escaping_bound_vars() { + let (value, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value); + let result = f(value); + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + result, + ) + } else { + f(value) + } +} + +pub struct BoundVarReplacer<'me, 'tcx> { + infcx: &'me InferCtxt<'tcx>, + // These three maps track the bound variable that were replaced by placeholders. It might be + // nice to remove these since we already have the `kind` in the placeholder; we really just need + // the `var` (but we *could* bring that into scope if we were to track them as we pass them). + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + // The current depth relative to *this* folding, *not* the entire normalization. In other words, + // the depth of binders we've passed here. + current_index: ty::DebruijnIndex, + // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: + // we don't actually create a universe until we see a bound var we have to replace. + universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, +} + +impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { + /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that + /// use a binding level above `universe_indices.len()`, we fail. + pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &'me InferCtxt<'tcx>, + universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, + value: T, + ) -> ( + T, + BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + BTreeMap<ty::PlaceholderType, ty::BoundTy>, + BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + ) { + let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new(); + let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new(); + let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new(); + + let mut replacer = BoundVarReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + current_index: ty::INNERMOST, + universe_indices, + }; + + let value = value.fold_with(&mut replacer); + + (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) + } + + fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { + let infcx = self.infcx; + let index = + self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; + let universe = self.universe_indices[index].unwrap_or_else(|| { + for i in self.universe_indices.iter_mut().take(index + 1) { + *i = i.or_else(|| Some(infcx.create_next_universe())) + } + self.universe_indices[index].unwrap() + }); + universe + } +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + 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::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); + ty::Region::new_placeholder(self.infcx.tcx, p) + } + _ => r, + } + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderType { universe, bound: bound_ty }; + self.mapped_types.insert(p, bound_ty); + Ty::new_placeholder(self.infcx.tcx, p) + } + _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), + _ => t, + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.kind() { + ty::ConstKind::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderConst { universe, bound: bound_const }; + self.mapped_consts.insert(p, bound_const); + ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty()) + } + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } +} + +/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. +pub struct PlaceholderReplacer<'me, 'tcx> { + infcx: &'me InferCtxt<'tcx>, + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + universe_indices: &'me [Option<ty::UniverseIndex>], + current_index: ty::DebruijnIndex, +} + +impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { + pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &'me InferCtxt<'tcx>, + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + universe_indices: &'me [Option<ty::UniverseIndex>], + value: T, + ) -> T { + let mut replacer = PlaceholderReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + current_index: ty::INNERMOST, + }; + value.fold_with(&mut replacer) + } +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + if !t.has_placeholders() && !t.has_infer() { + return t; + } + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { + let r1 = match *r0 { + ty::ReVar(vid) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid), + _ => r0, + }; + + let r2 = match *r1 { + ty::RePlaceholder(p) => { + let replace_var = self.mapped_regions.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + ty::Region::new_bound(self.interner(), db, *replace_var) + } + None => r1, + } + } + _ => r1, + }; + + debug!(?r0, ?r1, ?r2, "fold_region"); + + r2 + } + + 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); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Ty::new_bound(self.infcx.tcx, db, *replace_var) + } + None => { + if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + } + } + + _ 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 { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) + } + None => { + if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } + } + } else { + ct.super_fold_with(self) + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index c727ef53d55..15059bc6613 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -313,7 +313,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // Don't normalize the whole obligation, the param env is either // already normalized, or we're currently normalizing the // param_env. Either way we should only normalize the predicate. - let normalized_predicate = traits::project::normalize_with_depth_to( + let normalized_predicate = traits::normalize::normalize_with_depth_to( &mut selcx, param_env, cause.clone(), |
