Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,26 @@ 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): The `hybrid_preds`
// should be well-formed. However, using them may result in
// region errors as we currently don't track placeholder
// assumptions.
//
// To avoid being backwards incompatible with the old solver,
// we also eagerly normalize the where-bounds in the new solver
// here while ignoring region constraints. This means we can then
// use where-bounds whose normalization results in placeholder
// errors further down without getting any errors.
//
// It should be sound to do so as the only region errors here
// should be due to missing implied bounds.
//
// cc trait-system-refactor-initiative/issues/166.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// cc trait-system-refactor-initiative/issues/166.
// FIXME(-Zhigher-ranked-assumptions): The `hybrid_preds`
// should be well-formed. However, using them may result in
// region errors as we currently don't track placeholder
// assumptions.
//
// To avoid being backwards incompatible with the old solver,
// we also eagerly normalize the where-bounds in the new solver
// here while ignoring region constraints. This means we can then
// use where-bounds whose normalization results in placeholder
// errors further down without getting any errors.
//
// It should be sound to do so as the only region errors here
// should be due to missing implied bounds.
//
// cc trait-system-refactor-initiative/issues/166.

elaborated a bit here

let param_env = if tcx.next_trait_solver_globally() {
traits::deeply_normalize_param_env_ignoring_regions(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());
Expand Down
63 changes: 63 additions & 0 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,69 @@ pub fn normalize_param_env_or_error<'tcx>(
ty::ParamEnv::new(tcx.mk_clauses(&predicates))
}

/// Deeply normalize the param env using the next solver ignoring
/// region errors.
///
/// FIXME(-Zhigher-ranked-assumptions): this is a hack to work around
/// the fact that we don't support placeholder assumptions right now
/// and is necessary for `compare_method_predicate_entailment`, see the
/// use of this function for more info. We should remove this once we
/// have proper support for implied bounds on binders.
#[instrument(level = "debug", skip(tcx))]
pub fn deeply_normalize_param_env_ignoring_regions<'tcx>(
tcx: TyCtxt<'tcx>,
unnormalized_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
) -> ty::ParamEnv<'tcx> {
let 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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think we have to create a new param_env at all here 🤔 unsure why we do it in the old solver...

wanna update the new solver to just use the unnormalized_env instead?

Copy link
Contributor Author

@adwinwhite adwinwhite Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we need to use the elaborated_env as the param_env of normalization/resolving and the return value in the error path?
unnormalized_env doesn't contain those elaborated predicates. And using it causes some regressions in my testing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh 😅 I am blind xd

if !elaborated_env.has_aliases() {
return elaborated_env;
}

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) => {
infcx.err_ctxt().report_fulfillment_errors(errors);
// An unnormalized env is better than nothing.
debug!("normalize_param_env_or_error: errored resolving predicates");
return elaborated_env;
}
};

debug!("do_normalize_predicates: normalized predicates = {:?}", predicates);
// FIXME(-Zhigher-ranked-assumptions): We're ignoring region errors for now.
// There're placeholder constraints `leaking` out.
// See the fixme in the enclosing function's docs for more.
let _errors = infcx.resolve_regions(cause.body_id, elaborated_env, []);

let predicates = match infcx.fully_resolve(predicates) {
Ok(predicates) => predicates,
Err(fixup_err) => {
span_bug!(
span,
"inference variables in normalized parameter environment: {}",
fixup_err
)
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't need to split outlives vs non-outlives predicate norm in the new solver. This only matters for the old one

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that way you also don't need a separate do_deeply_normalize_predicates function as you only call it once

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*,
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/higher-ranked/trait-bounds/issue-100689.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//@ check-pass
//@ revisions: old next
//@[next] compile-flags: -Znext-solver

struct Foo<'a> {
foo: &'a mut usize,
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/higher-ranked/trait-bounds/issue-102899.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//@ check-pass
//@ revisions: old next
//@[next] compile-flags: -Znext-solver

pub trait BufferTrait<'buffer> {
type Subset<'channel>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T> {}
trait Entailment<T: Trait> {
fn method()
where
Self: for<'a> Bound<<T as Trait>::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() {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc == _`
--> $DIR/normalize-param-env-2.rs:22:5
|
LL | / fn f()
LL | | where
LL | | Self::Assoc: A<T>,
| |__________________________^

error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc: A<T>`
--> $DIR/normalize-param-env-2.rs:24:22
|
Expand Down Expand Up @@ -46,6 +54,6 @@ LL | where
LL | Self::Assoc: A<T>,
| ^^^^ 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`.
Loading