From 1fa0851481dd669d80561c475217ab81fddccd1b Mon Sep 17 00:00:00 2001 From: Adwin White Date: Tue, 25 Nov 2025 11:08:58 +0800 Subject: [PATCH] deeply normalize param env in compare_impl_item --- .../src/check/compare_impl_item.rs | 9 +- .../src/solve/normalize.rs | 2 + .../rustc_trait_selection/src/traits/mod.rs | 112 ++++++++++++++++++ .../trait-bounds/issue-100689.rs | 2 + .../trait-bounds/issue-102899.rs | 2 + ...eply-normalize-env-in-compare-impl-item.rs | 38 ++++++ .../normalize/normalize-param-env-2.stderr | 10 +- 7 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/next-solver/normalize/deeply-normalize-env-in-compare-impl-item.rs diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 6a07d2988fdfc..869aa483d7e84 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -236,7 +236,14 @@ fn compare_method_predicate_entailment<'tcx>( let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id); let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds)); - let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); + // FIXME(-Zhigher-ranked-assumptions): lazy normalization may postpone region constraints to + // an infcx that checks regions. Deeply normalize the param env in the next solver as well. + // cc trait-system-refactor-initiative/issues/166. + let param_env = if tcx.next_trait_solver_globally() { + traits::deeply_normalize_param_env_or_error(tcx, param_env, normalize_cause) + } else { + traits::normalize_param_env_or_error(tcx, param_env, normalize_cause) + }; debug!(caller_bounds=?param_env.caller_bounds()); let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 567f660a59383..d06e4f801b417 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -198,6 +198,8 @@ where if ty.has_escaping_bound_vars() { let (ty, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty); + // FIXME: Temporary placeholders may get added to infcx's region constraints/obligations, + // which can cause problem for `resolve_regions`. let result = ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?.expect_type(); Ok(PlaceholderReplacer::replace_placeholders( diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 6032bcacec6a4..8ad4788782848 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -317,6 +317,58 @@ fn do_normalize_predicates<'tcx>( } } +// A temporary hack. Do not use this. +// See `deeply_normalize_param_env_or_error`. +#[instrument(level = "debug", skip(tcx, elaborated_env))] +fn do_deeply_normalize_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + cause: ObligationCause<'tcx>, + elaborated_env: ty::ParamEnv<'tcx>, + predicates: Vec>, +) -> Result>, ErrorGuaranteed> { + let span = cause.span; + let infcx = tcx + .infer_ctxt() + .with_next_trait_solver(true) + .ignoring_regions() + .build(TypingMode::non_body_analysis()); + let predicates = match crate::solve::deeply_normalize::<_, FulfillmentError<'tcx>>( + infcx.at(&cause, elaborated_env), + predicates, + ) { + Ok(predicates) => predicates, + Err(errors) => { + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); + return Err(reported); + } + }; + + debug!("do_normalize_predicates: normalized predicates = {:?}", predicates); + + // The next solver doesn't ignore region constraints so we do it manually. + drop(infcx.take_registered_region_obligations()); + drop(infcx.take_registered_region_assumptions()); + drop(infcx.take_and_reset_region_constraints()); + let errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); + if !errors.is_empty() { + tcx.dcx().span_delayed_bug( + span, + format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"), + ); + } + + match infcx.fully_resolve(predicates) { + Ok(predicates) => Ok(predicates), + Err(fixup_err) => { + span_bug!( + span, + "inference variables in normalized parameter environment: {}", + fixup_err + ) + } + } +} + // FIXME: this is gonna need to be removed ... /// Normalizes the parameter environment, reporting errors if they occur. #[instrument(level = "debug", skip(tcx))] @@ -477,6 +529,66 @@ pub fn normalize_param_env_or_error<'tcx>( ty::ParamEnv::new(tcx.mk_clauses(&predicates)) } +// FIXME(-Zhigher-ranked-assumptions): this is a hack to walk around the fact that we don't support +// placeholder assumptions right now. +// We should remove this once we have proper support for implied bounds on binders. +/// Deeply normalize the param env using the next solver. +#[instrument(level = "debug", skip(tcx))] +pub fn deeply_normalize_param_env_or_error<'tcx>( + tcx: TyCtxt<'tcx>, + unnormalized_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, +) -> ty::ParamEnv<'tcx> { + let mut predicates: Vec<_> = + util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect(); + + debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); + + let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates)); + if !elaborated_env.has_aliases() { + return elaborated_env; + } + + let outlives_predicates: Vec<_> = predicates + .extract_if(.., |predicate| { + matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..)) + }) + .collect(); + + debug!( + "normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})", + predicates, outlives_predicates + ); + let Ok(non_outlives_predicates) = + do_deeply_normalize_predicates(tcx, cause.clone(), elaborated_env, predicates) + else { + // An unnormalized env is better than nothing. + debug!("normalize_param_env_or_error: errored resolving non-outlives predicates"); + return elaborated_env; + }; + + debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates); + + // Not sure whether it is better to include the unnormalized TypeOutlives predicates + // here. I believe they should not matter, because we are ignoring TypeOutlives param-env + // predicates here anyway. Keeping them here anyway because it seems safer. + let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned(); + let outlives_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(outlives_env)); + let Ok(outlives_predicates) = + do_deeply_normalize_predicates(tcx, cause, outlives_env, outlives_predicates) + else { + // An unnormalized env is better than nothing. + debug!("normalize_param_env_or_error: errored resolving outlives predicates"); + return elaborated_env; + }; + debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates); + + let mut predicates = non_outlives_predicates; + predicates.extend(outlives_predicates); + debug!("normalize_param_env_or_error: final predicates={:?}", predicates); + ty::ParamEnv::new(tcx.mk_clauses(&predicates)) +} + #[derive(Debug)] pub enum EvaluateConstErr { /// The constant being evaluated was either a generic parameter or inference variable, *or*, diff --git a/tests/ui/higher-ranked/trait-bounds/issue-100689.rs b/tests/ui/higher-ranked/trait-bounds/issue-100689.rs index f405abfb2a2ef..6bfad56b93d29 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-100689.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-100689.rs @@ -1,4 +1,6 @@ //@ check-pass +//@ revisions: old next +//@[next] compile-flags: -Znext-solver struct Foo<'a> { foo: &'a mut usize, diff --git a/tests/ui/higher-ranked/trait-bounds/issue-102899.rs b/tests/ui/higher-ranked/trait-bounds/issue-102899.rs index b4ef75319e5c1..77d4d0179b1ba 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-102899.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-102899.rs @@ -1,4 +1,6 @@ //@ check-pass +//@ revisions: old next +//@[next] compile-flags: -Znext-solver pub trait BufferTrait<'buffer> { type Subset<'channel> diff --git a/tests/ui/traits/next-solver/normalize/deeply-normalize-env-in-compare-impl-item.rs b/tests/ui/traits/next-solver/normalize/deeply-normalize-env-in-compare-impl-item.rs new file mode 100644 index 0000000000000..e68f82da88af8 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/deeply-normalize-env-in-compare-impl-item.rs @@ -0,0 +1,38 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// See trait-system-refactor-initiative/issues/166. +// The old solver doesn't check normalization constraints in `compare_impl_item`. +// The new solver performs lazy normalization so those region constraints may get postponed to +// an infcx that considers regions. +trait Trait { + type Assoc<'a> + where + Self: 'a; +} +impl<'b> Trait for &'b u32 { + type Assoc<'a> = &'a u32 + where + Self: 'a; +} + +trait Bound {} +trait Entailment { + fn method() + where + Self: for<'a> Bound<::Assoc<'a>>; +} + +impl<'b, T> Entailment<&'b u32> for T { + // Instantiates trait where-clauses with `&'b u32` and then normalizes + // `T: for<'a> Bound<<&'b u32 as Trait>::Assoc<'a>>` in a separate infcx + // without checking region constraints. + // + // It normalizes to `T: Bound<&'a u32>`, dropping the `&'b u32: 'a` constraint. + fn method() + where + Self: for<'a> Bound<&'a u32> + {} +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr index d179c80596238..82a5f33628e61 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr @@ -1,3 +1,11 @@ +error[E0275]: overflow evaluating the requirement `<() as A>::Assoc == _` + --> $DIR/normalize-param-env-2.rs:22:5 + | +LL | / fn f() +LL | | where +LL | | Self::Assoc: A, + | |__________________________^ + error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` --> $DIR/normalize-param-env-2.rs:24:22 | @@ -46,6 +54,6 @@ LL | where LL | Self::Assoc: A, | ^^^^ required by this bound in `A::f` -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0275`.