Skip to content

Treat normalizing consts like normalizing types in deeply normalize #142126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 8, 2025
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
7 changes: 7 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,13 @@ impl<'tcx> InferCtxt<'tcx> {
ty::Region::new_var(self.tcx, region_var)
}

pub fn next_term_var_of_kind(&self, term: ty::Term<'tcx>, span: Span) -> ty::Term<'tcx> {
match term.kind() {
ty::TermKind::Ty(_) => self.next_ty_var(span).into(),
ty::TermKind::Const(_) => self.next_const_var(span).into(),
}
}

/// Return the universe that the region `r` was created in. For
/// most regions (e.g., `'static`, named regions from the user,
/// etc) this is the root universe U0. For inference variables or
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
let infcx = self.goal.infcx;
match goal.predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
let unconstrained_term = match term.kind() {
ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
};
let unconstrained_term = infcx.next_term_var_of_kind(term, span);
let goal =
goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
// We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
Expand Down
91 changes: 25 additions & 66 deletions compiler/rustc_trait_selection/src/solve/normalize.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::assert_matches::assert_matches;
use std::fmt::Debug;

use rustc_data_structures::stack::ensure_sufficient_stack;
Expand All @@ -16,7 +15,6 @@ use tracing::instrument;
use super::{FulfillmentCtxt, NextSolverError};
use crate::error_reporting::InferCtxtErrorExt;
use crate::error_reporting::traits::OverflowCause;
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};

/// Deeply normalize all aliases in `value`. This does not handle inference and expects
Expand Down Expand Up @@ -97,19 +95,18 @@ impl<'tcx, E> NormalizationFolder<'_, 'tcx, E>
where
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
fn normalize_alias_ty(&mut self, alias_ty: Ty<'tcx>) -> Result<Ty<'tcx>, Vec<E>> {
assert_matches!(alias_ty.kind(), ty::Alias(..));

fn normalize_alias_term(
&mut self,
alias_term: ty::Term<'tcx>,
) -> Result<ty::Term<'tcx>, Vec<E>> {
let infcx = self.at.infcx;
let tcx = infcx.tcx;
let recursion_limit = tcx.recursion_limit();
if !recursion_limit.value_within_limit(self.depth) {
let ty::Alias(_, data) = *alias_ty.kind() else {
unreachable!();
};
let term = alias_term.to_alias_term().unwrap();

self.at.infcx.err_ctxt().report_overflow_error(
OverflowCause::DeeplyNormalize(data.into()),
OverflowCause::DeeplyNormalize(term),
self.at.cause.span,
true,
|_| {},
Expand All @@ -118,14 +115,14 @@ where

self.depth += 1;

let new_infer_ty = infcx.next_ty_var(self.at.cause.span);
let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span);
let obligation = Obligation::new(
tcx,
self.at.cause.clone(),
self.at.param_env,
ty::PredicateKind::AliasRelate(
alias_ty.into(),
new_infer_ty.into(),
alias_term.into(),
infer_term.into(),
ty::AliasRelationDirection::Equate,
),
);
Expand All @@ -135,50 +132,13 @@ where

// 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)
}

fn normalize_unevaluated_const(
&mut self,
uv: ty::UnevaluatedConst<'tcx>,
) -> Result<ty::Const<'tcx>, Vec<E>> {
let infcx = self.at.infcx;
let tcx = infcx.tcx;
let recursion_limit = tcx.recursion_limit();
if !recursion_limit.value_within_limit(self.depth) {
self.at.infcx.err_ctxt().report_overflow_error(
OverflowCause::DeeplyNormalize(uv.into()),
self.at.cause.span,
true,
|_| {},
);
}

self.depth += 1;

let new_infer_ct = infcx.next_const_var(self.at.cause.span);
let obligation = Obligation::new(
tcx,
self.at.cause.clone(),
self.at.param_env,
ty::NormalizesTo { alias: uv.into(), term: new_infer_ct.into() },
);

let result = if infcx.predicate_may_hold(&obligation) {
self.fulfill_cx.register_predicate_obligation(infcx, obligation);
let errors = self.fulfill_cx.select_where_possible(infcx);
if !errors.is_empty() {
return Err(errors);
}
let ct = infcx.resolve_vars_if_possible(new_infer_ct);
ct.try_fold_with(self)?
} else {
ty::Const::new_unevaluated(tcx, uv).try_super_fold_with(self)?
let term = infcx.resolve_vars_if_possible(infer_term);
// super-folding the `term` will directly fold the `Ty` or `Const` so
// we have to match on the term and super-fold them manually.
let result = match term.kind() {
ty::TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(),
ty::TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(),
};

self.depth -= 1;
Ok(result)
}
Expand Down Expand Up @@ -238,7 +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);
let result = ensure_sufficient_stack(|| self.normalize_alias_ty(ty))?;
let result =
ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?.expect_type();
Ok(PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
Expand All @@ -248,7 +209,7 @@ where
result,
))
} else {
ensure_sufficient_stack(|| self.normalize_alias_ty(ty))
Ok(ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?.expect_type())
}
}

Expand All @@ -260,15 +221,13 @@ where
return Ok(ct);
}

let uv = match ct.kind() {
ty::ConstKind::Unevaluated(ct) => ct,
_ => return ct.try_super_fold_with(self),
};
let ty::ConstKind::Unevaluated(..) = ct.kind() else { return ct.try_super_fold_with(self) };

if uv.has_escaping_bound_vars() {
let (uv, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv);
let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(uv))?;
if ct.has_escaping_bound_vars() {
let (ct, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ct);
let result =
ensure_sufficient_stack(|| self.normalize_alias_term(ct.into()))?.expect_const();
Ok(PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
Expand All @@ -278,7 +237,7 @@ where
result,
))
} else {
ensure_sufficient_stack(|| self.normalize_unevaluated_const(uv))
Ok(ensure_sufficient_stack(|| self.normalize_alias_term(ct.into()))?.expect_const())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ impl<'tcx> At<'_, 'tcx> {
return Ok(term);
}

let new_infer = match term.kind() {
ty::TermKind::Ty(_) => self.infcx.next_ty_var(self.cause.span).into(),
ty::TermKind::Const(_) => self.infcx.next_const_var(self.cause.span).into(),
};
let new_infer = self.infcx.next_term_var_of_kind(term, self.cause.span);

// We simply emit an `alias-eq` goal here, since that will take care of
// normalizing the LHS of the projection until it is a rigid projection
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
//@ known-bug: #140571
// Regression test for #140571. The compiler used to ICE

#![feature(associated_const_equality, specialization)]
//~^ WARN the feature `specialization` is incomplete

pub trait IsVoid {
const IS_VOID: bool;
}
impl<T> IsVoid for T {
default const IS_VOID: bool = false;
}
impl<T> Maybe<T> for () where T: NotVoid + ?Sized {}

pub trait NotVoid {}
impl<T> NotVoid for T where T: IsVoid<IS_VOID = false> + ?Sized {}

pub trait Maybe<T> {}
impl<T> Maybe<T> for T {}
impl<T> Maybe<T> for () where T: NotVoid + ?Sized {}
//~^ ERROR conflicting implementations of trait `Maybe<()>` for type `()`

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/overlap-due-to-unsatisfied-const-bound.rs:3:39
|
LL | #![feature(associated_const_equality, specialization)]
| ^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: consider using `min_specialization` instead, which is more stable and complete
= note: `#[warn(incomplete_features)]` on by default

error[E0119]: conflicting implementations of trait `Maybe<()>` for type `()`
--> $DIR/overlap-due-to-unsatisfied-const-bound.rs:18:1
|
LL | impl<T> Maybe<T> for T {}
| ---------------------- first implementation here
LL | impl<T> Maybe<T> for () where T: NotVoid + ?Sized {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()`

error: aborting due to 1 previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0119`.
Loading