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
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
self.param_env.caller_bounds().iter().filter_map(|clause| {
match clause.kind().skip_binder() {
ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => {
Some((clause, span))
Some((ty::set_aliases_to_non_rigid(tcx, clause).skip_norm_wip(), span))
}
_ => None,
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_infer/src/infer/outlives/test_type_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ pub(super) fn can_match_erased_ty<'tcx>(
// deal with rigid aliases, making sure we do so correctly
// everywhere is effort, so we're just using `No` everywhere
// for now. This should change soon.
let outlives_ty = ty::set_aliases_to_non_rigid(tcx, outlives_ty).skip_normalization();
let (outlives_ty, erased_ty) =
ty::set_aliases_to_non_rigid(tcx, (outlives_ty, erased_ty)).skip_normalization();
if outlives_ty == erased_ty {
// pointless micro-optimization
true
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_infer/src/infer/outlives/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
let erased_p_ty = self.tcx.erase_and_anonymize_regions(
ty::set_aliases_to_non_rigid(self.tcx, p_ty).skip_norm_wip(),
);
let erased_ty = ty::set_aliases_to_non_rigid(self.tcx, erased_ty).skip_norm_wip();
(erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r)))
}));

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2699,6 +2699,10 @@ impl<'tcx> TyCtxt<'tcx> {
self.sess.opts.unstable_opts.disable_fast_paths
}

pub fn disable_param_env_normalization_hack(self) -> bool {
self.sess.opts.unstable_opts.disable_param_env_normalization_hack
}

pub fn renormalize_rigid_aliases(self) -> bool {
self.sess.opts.unstable_opts.renormalize_rigid_aliases
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2353,6 +2353,8 @@ options! {
"disable various performance optimizations in trait solving"),
disable_incr_comp_backend_caching: bool = (false, parse_bool, [TRACKED],
"disable caching of compiled objects by the codegen backend during incremental compilation"),
disable_param_env_normalization_hack: bool = (false, parse_bool, [TRACKED],
"do not treat all aliases in the environment as rigid with `-Znext-solver`"),
dual_proc_macros: bool = (false, parse_bool, [TRACKED],
"load proc macros for both target and host, but only link to the target (default: no)"),
dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
Expand Down
51 changes: 48 additions & 3 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,25 @@ fn pred_known_to_hold_modulo_regions<'tcx>(
}
}

fn set_projection_term_to_non_rigid<'tcx>(
tcx: TyCtxt<'tcx>,
predicates: impl IntoIterator<Item = ty::Clause<'tcx>>,
) -> impl Iterator<Item = ty::Clause<'tcx>> {
predicates.into_iter().map(move |clause| {
if let ty::ClauseKind::Projection(projection_pred) = clause.kind().skip_binder() {
clause
.kind()
.rebind(ty::ProjectionPredicate {
projection_term: projection_pred.projection_term,
term: ty::set_aliases_to_non_rigid(tcx, projection_pred.term).skip_norm_wip(),
})
.upcast(tcx)
} else {
clause
}
})
}

#[instrument(level = "debug", skip(tcx, elaborated_env))]
fn do_normalize_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
Expand All @@ -272,10 +291,36 @@ fn do_normalize_predicates<'tcx>(
let span = cause.span;
let infcx = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis());
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
// FIXME: `elaborated_env` is not really rigid. We do this to be
// consistent with the old solver.
let elaborated_env = if tcx.next_trait_solver_globally()
&& !tcx.disable_param_env_normalization_hack()
{
let elaborated_env = ty::set_aliases_to_rigid(tcx, elaborated_env);
let elaborated_env = set_projection_term_to_non_rigid(tcx, elaborated_env.caller_bounds());
ty::ParamEnv::new(tcx.mk_clauses_from_iter(elaborated_env))
} else {
elaborated_env
};
let predicates = ocx.normalize(&cause, elaborated_env, Unnormalized::new_wip(predicates));
// FIXME: opaque types in param env might be in defining scope but we're
// using non body analysis for here. So the rigidness marker is wrong.
let predicates = ty::set_aliases_to_non_rigid(tcx, predicates).skip_norm_wip();
let predicates = if tcx.next_trait_solver_globally() {
if !tcx.disable_param_env_normalization_hack() {
let predicates: Vec<_> = set_projection_term_to_non_rigid(tcx, predicates).collect();
// FIXME(type_alias_impl_trait): opaque types in param env might be
// in defining scope but we're using non body analysis here.
// So the rigidness marker is wrong.
ty::set_opaques_to_non_rigid(tcx, predicates).skip_norm_wip()
} else {
// Param env is used in different typing modes but itself
// is normalized in `non_body_analysis`.
// That not only makes the rigidness of opaques types wrong,
// other aliases can be indirectly affected as well.
// So we conservatively set everything to be non-rigid.
ty::set_aliases_to_non_rigid(tcx, predicates).skip_norm_wip()

@lcnr lcnr Jul 2, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

keep a comment for why we need to do this if the hack is disabled

(param_env used in different typingmode)

View changes since the review

}
} else {
predicates
};

let errors = ocx.evaluate_obligations_error_on_ambiguity();
if !errors.is_empty() {
Expand Down
97 changes: 84 additions & 13 deletions compiler/rustc_type_ir/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,20 +555,67 @@ where
}
}

/// This function should ideally only be used if either the `TypingMode`
/// or the `ParamEnv` differs from the environment the aliases were normalized
/// in.
///
/// Cases outside these two should consider whether the problem can be
/// fixed at the root instead.
pub fn set_aliases_to_non_rigid<I: Interner, T>(cx: I, value: T) -> ty::Unnormalized<I, T>
where
T: TypeFoldable<I>,
{
if !value.has_rigid_aliases() {
return ty::Unnormalized::new(value);
let folded = set_aliases_rigidness_with_mode(cx, value, RigidnessFoldMode::AllToNonRigid);
ty::Unnormalized::new(folded)
}

pub fn set_opaques_to_non_rigid<I: Interner, T>(cx: I, value: T) -> ty::Unnormalized<I, T>
where
T: TypeFoldable<I>,
{
let folded = set_aliases_rigidness_with_mode(cx, value, RigidnessFoldMode::OpaqueToNonRigid);
ty::Unnormalized::new(folded)
}

pub fn set_aliases_to_rigid<I: Interner, T>(cx: I, value: T) -> T
where
T: TypeFoldable<I>,
{
set_aliases_rigidness_with_mode(cx, value, RigidnessFoldMode::AllToRigid)
}

fn set_aliases_rigidness_with_mode<I: Interner, T>(cx: I, value: T, mode: RigidnessFoldMode) -> T
where
T: TypeFoldable<I>,
{
if !mode.needs_change(&value) {
return value;
}

let mut folder = RigidnessFolder { cx };
ty::Unnormalized::new(value.fold_with(&mut folder))
let mut folder = RigidnessFolder { cx, mode };
value.fold_with(&mut folder)
}

enum RigidnessFoldMode {
AllToNonRigid,
AllToRigid,
OpaqueToNonRigid,
}

impl RigidnessFoldMode {
fn needs_change<I: Interner, T: TypeVisitable<I>>(&self, v: &T) -> bool {
match self {
RigidnessFoldMode::AllToRigid => v.has_non_rigid_aliases(),
RigidnessFoldMode::AllToNonRigid => v.has_rigid_aliases(),
RigidnessFoldMode::OpaqueToNonRigid => v.has_rigid_aliases() && v.has_opaque_types(),
}
}
}

// Set aliases to be rigid or non-rigid according to the mode.
struct RigidnessFolder<I: Interner> {
cx: I,
mode: RigidnessFoldMode,
}

impl<I: Interner> TypeFolder<I> for RigidnessFolder<I> {
Expand All @@ -578,42 +625,66 @@ impl<I: Interner> TypeFolder<I> for RigidnessFolder<I> {
}

fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
if t.has_rigid_aliases() { t.super_fold_with(self) } else { t }
if self.mode.needs_change(&t) { t.super_fold_with(self) } else { t }
}

fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
if !t.has_rigid_aliases() {
if !self.mode.needs_change(&t) {
return t;
}

match t.kind() {
ty::Alias(ty::IsRigid::Yes, alias_ty) => {
ty::Alias(is_rigid, alias_ty) => {
let alias_ty = alias_ty.fold_with(self);
I::Ty::new_alias(self.cx(), ty::IsRigid::No, alias_ty)
match self.mode {
RigidnessFoldMode::AllToRigid => {
I::Ty::new_alias(self.cx(), ty::IsRigid::Yes, alias_ty)
}
RigidnessFoldMode::AllToNonRigid => {
I::Ty::new_alias(self.cx(), ty::IsRigid::No, alias_ty)
}
RigidnessFoldMode::OpaqueToNonRigid => {
if let ty::AliasTyKind::Opaque { .. } = alias_ty.kind {
I::Ty::new_alias(self.cx(), ty::IsRigid::No, alias_ty)
} else {
I::Ty::new_alias(self.cx(), is_rigid, alias_ty)
}
}
}
}
_ => t.super_fold_with(self),
}
}

fn fold_const(&mut self, c: I::Const) -> I::Const {
if !c.has_rigid_aliases() {
if !self.mode.needs_change(&c) {
return c;
}

match c.kind() {
ty::ConstKind::Alias(ty::IsRigid::Yes, alias_const) => {
ty::ConstKind::Alias(is_rigid, alias_const) => {
let alias_const = alias_const.fold_with(self);
I::Const::new_alias(self.cx, ty::IsRigid::No, alias_const)
match self.mode {
RigidnessFoldMode::AllToRigid => {
I::Const::new_alias(self.cx, ty::IsRigid::Yes, alias_const)
}
RigidnessFoldMode::AllToNonRigid => {
I::Const::new_alias(self.cx(), ty::IsRigid::No, alias_const)
}
RigidnessFoldMode::OpaqueToNonRigid => {
I::Const::new_alias(self.cx(), is_rigid, alias_const)
}
}
}
_ => c.super_fold_with(self),
}
}

fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
if p.has_rigid_aliases() { p.super_fold_with(self) } else { p }
if self.mode.needs_change(&p) { p.super_fold_with(self) } else { p }
}

fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
if c.has_rigid_aliases() { c.super_fold_with(self) } else { c }
if self.mode.needs_change(&c) { c.super_fold_with(self) } else { c }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
--> $DIR/false-positive-predicate-entailment-error.rs:36:5
--> $DIR/false-positive-predicate-entailment-error.rs:41:5
|
LL | / fn autobatch<F>(self) -> impl Trait
... |
Expand All @@ -8,7 +8,7 @@ LL | | F: Callback<Self::CallbackArg>,
| |_______________________________________^ the trait `MyFn<i32>` is not implemented for `F`
|
note: required for `F` to implement `Callback<i32>`
--> $DIR/false-positive-predicate-entailment-error.rs:14:21
--> $DIR/false-positive-predicate-entailment-error.rs:19:21
|
LL | impl<A, F: MyFn<A>> Callback<A> for F {
| ------- ^^^^^^^^^^^ ^
Expand All @@ -20,7 +20,7 @@ LL | F: Callback<Self::CallbackArg> + MyFn<i32>,
| +++++++++++

error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
--> $DIR/false-positive-predicate-entailment-error.rs:36:5
--> $DIR/false-positive-predicate-entailment-error.rs:41:5
|
LL | / fn autobatch<F>(self) -> impl Trait
... |
Expand All @@ -29,7 +29,7 @@ LL | | F: Callback<Self::CallbackArg>,
| |_______________________________________^ the trait `MyFn<i32>` is not implemented for `F`
|
note: required for `F` to implement `Callback<i32>`
--> $DIR/false-positive-predicate-entailment-error.rs:14:21
--> $DIR/false-positive-predicate-entailment-error.rs:19:21
|
LL | impl<A, F: MyFn<A>> Callback<A> for F {
| ------- ^^^^^^^^^^^ ^
Expand All @@ -42,20 +42,20 @@ LL | F: Callback<Self::CallbackArg> + MyFn<i32>,
| +++++++++++

error[E0277]: the trait bound `F: Callback<i32>` is not satisfied
--> $DIR/false-positive-predicate-entailment-error.rs:42:12
--> $DIR/false-positive-predicate-entailment-error.rs:48:12
|
LL | F: Callback<Self::CallbackArg>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`
|
note: required for `F` to implement `Callback<i32>`
--> $DIR/false-positive-predicate-entailment-error.rs:14:21
--> $DIR/false-positive-predicate-entailment-error.rs:19:21
|
LL | impl<A, F: MyFn<A>> Callback<A> for F {
| ------- ^^^^^^^^^^^ ^
| |
| unsatisfied trait bound introduced here
note: the requirement `F: Callback<i32>` appears on the `impl`'s method `autobatch` but not on the corresponding trait's method
--> $DIR/false-positive-predicate-entailment-error.rs:25:8
--> $DIR/false-positive-predicate-entailment-error.rs:30:8
|
LL | trait ChannelSender {
| ------------- in this trait
Expand All @@ -68,35 +68,35 @@ LL | F: Callback<Self::CallbackArg> + MyFn<i32>,
| +++++++++++

error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
--> $DIR/false-positive-predicate-entailment-error.rs:36:30
--> $DIR/false-positive-predicate-entailment-error.rs:41:30
|
LL | fn autobatch<F>(self) -> impl Trait
| ^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`
|
note: required for `F` to implement `Callback<i32>`
--> $DIR/false-positive-predicate-entailment-error.rs:14:21
--> $DIR/false-positive-predicate-entailment-error.rs:19:21
|
LL | impl<A, F: MyFn<A>> Callback<A> for F {
| ------- ^^^^^^^^^^^ ^
| |
| unsatisfied trait bound introduced here

error[E0277]: the trait bound `F: Callback<i32>` is not satisfied
--> $DIR/false-positive-predicate-entailment-error.rs:27:12
--> $DIR/false-positive-predicate-entailment-error.rs:32:12
|
LL | F: Callback<Self::CallbackArg>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`
|
note: required for `F` to implement `Callback<i32>`
--> $DIR/false-positive-predicate-entailment-error.rs:14:21
--> $DIR/false-positive-predicate-entailment-error.rs:19:21
|
LL | impl<A, F: MyFn<A>> Callback<A> for F {
| ------- ^^^^^^^^^^^ ^
| |
| unsatisfied trait bound introduced here

error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
--> $DIR/false-positive-predicate-entailment-error.rs:36:5
--> $DIR/false-positive-predicate-entailment-error.rs:41:5
|
LL | / fn autobatch<F>(self) -> impl Trait
... |
Expand All @@ -105,7 +105,7 @@ LL | | F: Callback<Self::CallbackArg>,
| |_______________________________________^ the trait `MyFn<i32>` is not implemented for `F`
|
note: required for `F` to implement `Callback<i32>`
--> $DIR/false-positive-predicate-entailment-error.rs:14:21
--> $DIR/false-positive-predicate-entailment-error.rs:19:21
|
LL | impl<A, F: MyFn<A>> Callback<A> for F {
| ------- ^^^^^^^^^^^ ^
Expand All @@ -118,13 +118,13 @@ LL | F: Callback<Self::CallbackArg> + MyFn<i32>,
| +++++++++++

error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
--> $DIR/false-positive-predicate-entailment-error.rs:42:12
--> $DIR/false-positive-predicate-entailment-error.rs:48:12
|
LL | F: Callback<Self::CallbackArg>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`
|
note: required by a bound in `Callback`
--> $DIR/false-positive-predicate-entailment-error.rs:10:20
--> $DIR/false-positive-predicate-entailment-error.rs:15:20
|
LL | trait Callback<A>: MyFn<A, Output = Self::Ret> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Callback`
Expand Down
Loading
Loading