From da55797fc89aab92f49f80d826cb206c90870462 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Tue, 16 Jun 2026 15:37:46 -0300 Subject: [PATCH 01/17] Capture binder region constraints while relating --- .../src/canonical/mod.rs | 27 +++- .../src/solve/eval_ctxt/mod.rs | 108 ++++++++++++++- .../src/relate/solver_relating.rs | 128 +++++++++++++++++- ...principal-upcast-region-eq-issue-157859.rs | 19 +++ ...cipal-upcast-region-eq-issue-157859.stderr | 13 ++ .../trait-upcast-projection-region-eq.rs | 23 ++++ .../trait-upcast-projection-region-eq.stderr | 13 ++ 7 files changed, 321 insertions(+), 10 deletions(-) create mode 100644 tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs create mode 100644 tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr create mode 100644 tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.rs create mode 100644 tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.stderr diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 128735965ba73..ecd9868b92f7a 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -295,6 +295,7 @@ where struct ResponseRelating<'infcx, Infcx, I: Interner> { infcx: &'infcx Infcx, span: I::Span, + region_constraints: Option>>, } impl<'infcx, Infcx, I> ResponseRelating<'infcx, Infcx, I> @@ -302,8 +303,12 @@ where Infcx: InferCtxtLike, I: Interner, { - fn new(infcx: &'infcx Infcx, span: I::Span) -> Self { - ResponseRelating { infcx, span } + fn new(infcx: &'infcx Infcx, span: I::Span, collect_region_constraints: bool) -> Self { + ResponseRelating { + infcx, + span, + region_constraints: collect_region_constraints.then(Vec::new), + } } } @@ -415,7 +420,14 @@ where #[instrument(skip(self), level = "trace")] fn regions(&mut self, a: Region, b: Region) -> RelateResult> { - self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span); + if let Some(region_constraints) = &mut self.region_constraints { + if a != b { + region_constraints.push(ty::RegionConstraint::RegionOutlives(a, b)); + region_constraints.push(ty::RegionConstraint::RegionOutlives(b, a)); + } + } else { + self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span); + } Ok(a) } @@ -500,8 +512,15 @@ fn unify_query_var_values( assert_eq!(original_values.len(), var_values.len()); for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { - let mut must_eq = ResponseRelating::new(&**delegate, span); + let collect_region_constraints = delegate.cx().assumptions_on_binders(); + let mut must_eq = + ResponseRelating::new(&**delegate, span, collect_region_constraints); must_eq.relate(orig, response).unwrap(); + if let Some(region_constraints) = must_eq.region_constraints { + delegate.register_solver_region_constraint(ty::RegionConstraint::And( + region_constraints.into_boxed_slice(), + )); + } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index aa7e1e79b5866..b7aee3ca4d2e7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1220,6 +1220,78 @@ where self.relate(param_env, lhs, ty::Variance::Invariant, rhs) } + /// This should be used when relating a rigid alias with another type. + /// + /// Normally we emit a nested `AliasRelate` when equating an inference + /// variable and an alias. This causes us to instead constrain the inference + /// variable to the alias without emitting a nested alias relate goals. + #[instrument(level = "trace", skip(self, param_env), ret)] + pub(super) fn relate_rigid_alias_non_alias( + &mut self, + param_env: I::ParamEnv, + alias: ty::AliasTerm, + variance: ty::Variance, + term: I::Term, + ) -> Result<(), NoSolutionOrRerunNonErased> { + // NOTE: this check is purely an optimization, the structural eq would + // always fail if the term is not an inference variable. + if term.is_infer() { + let cx = self.cx(); + // We need to relate `alias` to `term` treating only the outermost + // constructor as rigid, relating any contained generic arguments as + // normal. We do this by first structurally equating the `term` + // with the alias constructor instantiated with unconstrained infer vars, + // and then relate this with the whole `alias`. + // + // Alternatively we could modify `Equate` for this case by adding another + // variant to `StructurallyRelateAliases`. + let def_id = match alias.kind { + ty::AliasTermKind::ProjectionTy { def_id } => def_id.into(), + ty::AliasTermKind::InherentTy { def_id } => def_id.into(), + ty::AliasTermKind::OpaqueTy { def_id } => def_id.into(), + ty::AliasTermKind::FreeTy { def_id } => def_id.into(), + ty::AliasTermKind::AnonConst { def_id } => def_id.into(), + ty::AliasTermKind::ProjectionConst { def_id } => def_id.into(), + ty::AliasTermKind::FreeConst { def_id } => def_id.into(), + ty::AliasTermKind::InherentConst { def_id } => def_id.into(), + }; + let identity_args = self.fresh_args_for_item(def_id); + let rigid_ctor = alias.with_args(cx, identity_args); + let ctor_term = rigid_ctor.to_term(cx); + self.eq_structurally_relating_aliases(param_env, term, ctor_term)?; + self.relate(param_env, alias, variance, rigid_ctor) + } else { + Err(NoSolution.into()) + } + } + + /// This should only be used when we're either instantiating a previously + /// unconstrained "return value" or when we're sure that all aliases in + /// the types are rigid. + #[instrument(level = "trace", skip(self, param_env), ret)] + pub(super) fn eq_structurally_relating_aliases>( + &mut self, + param_env: I::ParamEnv, + lhs: T, + rhs: T, + ) -> Result<(), NoSolutionOrRerunNonErased> { + let result = if self.cx().assumptions_on_binders() { + let (goals, region_constraints) = + self.delegate.eq_structurally_relating_aliases_with_region_constraints( + param_env, + lhs, + rhs, + self.origin_span, + )?; + self.register_solver_region_constraint(region_constraints); + goals + } else { + self.delegate.eq_structurally_relating_aliases(param_env, lhs, rhs, self.origin_span)? + }; + assert_eq!(result, vec![]); + Ok(()) + } + #[instrument(level = "trace", skip(self, param_env), ret)] pub(super) fn sub>( &mut self, @@ -1238,7 +1310,19 @@ where variance: ty::Variance, rhs: T, ) -> Result<(), NoSolutionOrRerunNonErased> { - let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; + let goals = if self.cx().assumptions_on_binders() { + let (goals, region_constraints) = self.delegate.relate_with_region_constraints( + param_env, + lhs, + variance, + rhs, + self.origin_span, + )?; + self.register_solver_region_constraint(region_constraints); + goals + } else { + self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)? + }; for &goal in goals.iter() { let source = match goal.predicate.kind().skip_binder() { ty::PredicateKind::Subtype { .. } @@ -1261,12 +1345,30 @@ where /// goals correctly. #[instrument(level = "trace", skip(self, param_env), ret)] pub(super) fn eq_and_get_goals>( - &self, + &mut self, param_env: I::ParamEnv, lhs: T, rhs: T, ) -> Result>, NoSolution> { - Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs, self.origin_span)?) + if self.cx().assumptions_on_binders() { + let (goals, region_constraints) = self.delegate.relate_with_region_constraints( + param_env, + lhs, + ty::Variance::Invariant, + rhs, + self.origin_span, + )?; + self.register_solver_region_constraint(region_constraints); + Ok(goals) + } else { + Ok(self.delegate.relate( + param_env, + lhs, + ty::Variance::Invariant, + rhs, + self.origin_span, + )?) + } } pub(super) fn instantiate_binder_with_infer + Copy>( diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 1e8ff77e4d395..1e9328aa129dc 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -2,6 +2,7 @@ use tracing::{debug, instrument}; use self::combine::{PredicateEmittingRelation, super_combine_consts, super_combine_tys}; use crate::data_structures::DelayedSet; +use crate::region_constraint::RegionConstraint; use crate::relate::combine::combine_ty_args; pub use crate::relate::*; use crate::solve::{Goal, VisibleForLeakCheck}; @@ -19,6 +20,53 @@ pub trait RelateExt: InferCtxtLike { Vec::Predicate>>, TypeError, >; + fn relate_with_region_constraints>( + &self, + param_env: ::ParamEnv, + lhs: T, + variance: ty::Variance, + rhs: T, + span: ::Span, + ) -> Result< + ( + Vec::Predicate>>, + RegionConstraint, + ), + TypeError, + >; +} + +fn relate_with_options( + infcx: &Infcx, + param_env: ::ParamEnv, + lhs: T, + variance: ty::Variance, + rhs: T, + span: ::Span, + collect_region_constraints: bool, +) -> Result< + ( + Vec::Predicate>>, + Option>, + ), + TypeError, +> +where + Infcx: InferCtxtLike, + T: Relate, +{ + let mut relate = SolverRelating::build( + infcx, + variance, + param_env, + span, + collect_region_constraints.then(Vec::new), + ); + relate.relate(lhs, rhs)?; + Ok(( + relate.goals, + relate.region_constraints.map(|c| RegionConstraint::And(c.into_boxed_slice())), + )) } impl RelateExt for Infcx { @@ -33,9 +81,42 @@ impl RelateExt for Infcx { Vec::Predicate>>, TypeError, > { - let mut relate = SolverRelating::new(self, variance, param_env, span); - relate.relate(lhs, rhs)?; - Ok(relate.goals) + let (goals, _) = relate_with_options( + self, + param_env, + lhs, + variance, + rhs, + span, + false, + )?; + Ok(goals) + } + + fn relate_with_region_constraints>( + &self, + param_env: ::ParamEnv, + lhs: T, + variance: ty::Variance, + rhs: T, + span: ::Span, + ) -> Result< + ( + Vec::Predicate>>, + RegionConstraint, + ), + TypeError, + > { + let (goals, region_constraints) = relate_with_options( + self, + param_env, + lhs, + variance, + rhs, + span, + true, + )?; + Ok((goals, region_constraints.unwrap())) } } @@ -48,6 +129,7 @@ pub struct SolverRelating<'infcx, Infcx, I: Interner> { // Mutable fields. ambient_variance: ty::Variance, goals: Vec>, + region_constraints: Option>>, /// The cache only tracks the `ambient_variance` as it's the /// only field which is mutable and which meaningfully changes /// the result when relating types. @@ -83,6 +165,16 @@ where ambient_variance: ty::Variance, param_env: I::ParamEnv, span: I::Span, + ) -> Self { + Self::build(infcx, ambient_variance, param_env, span, None) + } + + fn build( + infcx: &'infcx Infcx, + ambient_variance: ty::Variance, + param_env: I::ParamEnv, + span: I::Span, + region_constraints: Option>>, ) -> Self { SolverRelating { infcx, @@ -90,6 +182,7 @@ where ambient_variance, param_env, goals: vec![], + region_constraints, cache: Default::default(), } } @@ -249,6 +342,35 @@ where } } + let resolve_region = |r: I::Region| match r.kind() { + ty::ReVar(vid) => self.infcx.opportunistic_resolve_lt_var(vid), + _ => r, + }; + let a = resolve_region(a); + let b = resolve_region(b); + + if let Some(region_constraints) = &mut self.region_constraints { + if a == b { + return Ok(a); + } else { + match self.ambient_variance { + ty::Covariant => { + region_constraints.push(RegionConstraint::RegionOutlives(a, b)); + } + ty::Contravariant => { + region_constraints.push(RegionConstraint::RegionOutlives(b, a)); + } + ty::Invariant => { + region_constraints.push(RegionConstraint::RegionOutlives(a, b)); + region_constraints.push(RegionConstraint::RegionOutlives(b, a)); + } + ty::Bivariant => { + unreachable!("Expected bivariance to be handled in relate_with_variance") + } + } + } + } + Ok(a) } diff --git a/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs new file mode 100644 index 0000000000000..52b90750e01a9 --- /dev/null +++ b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs @@ -0,0 +1,19 @@ +//@compile-flags: -Zassumptions-on-binders -Znext-solver=globally +//@ dont-require-annotations: ERROR + +trait Super { + fn a(&self) { + let a: &dyn Sub = &(); + let b: &dyn Super fn(&'a ())> = a; + } +} + +impl Super for () {} + +trait Sub: Super {} + +impl Sub for () {} + +fn main() { + let a: &dyn Sub = &(); +} diff --git a/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr new file mode 100644 index 0000000000000..5ed8cb2dce80a --- /dev/null +++ b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `&dyn Sub: CoerceUnsized<&dyn Super fn(&'a ())>>` is not satisfied + --> $DIR/principal-upcast-region-eq-issue-157859.rs:7:49 + | +LL | let b: &dyn Super fn(&'a ())> = a; + | ^ the nightly-only, unstable trait `Unsize fn(&'a ())>>` is not implemented for `dyn Sub` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: required for `&dyn Sub` to implement `CoerceUnsized<&dyn Super fn(&'a ())>>` + = note: required for the cast from `&dyn Sub` to `&dyn Super fn(&'a ())>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.rs b/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.rs new file mode 100644 index 0000000000000..d32cdd33e6e8f --- /dev/null +++ b/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +trait Super { + type Assoc; + + fn a(&self) { + let a: &dyn Sub = &(); + let b: &dyn Super fn(&'a ())> = a; + //~^ ERROR the trait bound `&dyn Sub: CoerceUnsized<&dyn Super fn(&'a ())>>` is not satisfied + } +} + +impl Super for () { + type Assoc = fn(&'static ()); +} + +trait Sub: Super {} + +impl Sub for () {} + +fn main() { + let a: &dyn Sub = &(); +} diff --git a/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.stderr b/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.stderr new file mode 100644 index 0000000000000..fb55bab0ad20c --- /dev/null +++ b/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `&dyn Sub: CoerceUnsized<&dyn Super fn(&'a ())>>` is not satisfied + --> $DIR/trait-upcast-projection-region-eq.rs:8:57 + | +LL | let b: &dyn Super fn(&'a ())> = a; + | ^ the nightly-only, unstable trait `Unsize fn(&'a ())>>` is not implemented for `dyn Sub` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: required for `&dyn Sub` to implement `CoerceUnsized<&dyn Super fn(&'a ())>>` + = note: required for the cast from `&dyn Sub` to `&dyn Super fn(&'a ())>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 31a892ed0af7d81e2fa9ba2460af568c77d35571 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 20 Jun 2026 14:52:59 -0300 Subject: [PATCH 02/17] Register solver region constraints while relating --- .../src/canonical/mod.rs | 26 +-- .../src/solve/eval_ctxt/mod.rs | 55 +----- .../src/relate/solver_relating.rs | 163 ++++-------------- 3 files changed, 51 insertions(+), 193 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index ecd9868b92f7a..edec3f03ed378 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -295,7 +295,6 @@ where struct ResponseRelating<'infcx, Infcx, I: Interner> { infcx: &'infcx Infcx, span: I::Span, - region_constraints: Option>>, } impl<'infcx, Infcx, I> ResponseRelating<'infcx, Infcx, I> @@ -303,12 +302,8 @@ where Infcx: InferCtxtLike, I: Interner, { - fn new(infcx: &'infcx Infcx, span: I::Span, collect_region_constraints: bool) -> Self { - ResponseRelating { - infcx, - span, - region_constraints: collect_region_constraints.then(Vec::new), - } + fn new(infcx: &'infcx Infcx, span: I::Span) -> Self { + ResponseRelating { infcx, span } } } @@ -420,10 +415,12 @@ where #[instrument(skip(self), level = "trace")] fn regions(&mut self, a: Region, b: Region) -> RelateResult> { - if let Some(region_constraints) = &mut self.region_constraints { + if self.cx().assumptions_on_binders() { if a != b { - region_constraints.push(ty::RegionConstraint::RegionOutlives(a, b)); - region_constraints.push(ty::RegionConstraint::RegionOutlives(b, a)); + self.infcx + .register_solver_region_constraint(ty::RegionConstraint::RegionOutlives(a, b)); + self.infcx + .register_solver_region_constraint(ty::RegionConstraint::RegionOutlives(b, a)); } } else { self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span); @@ -512,15 +509,8 @@ fn unify_query_var_values( assert_eq!(original_values.len(), var_values.len()); for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { - let collect_region_constraints = delegate.cx().assumptions_on_binders(); - let mut must_eq = - ResponseRelating::new(&**delegate, span, collect_region_constraints); + let mut must_eq = ResponseRelating::new(&**delegate, span); must_eq.relate(orig, response).unwrap(); - if let Some(region_constraints) = must_eq.region_constraints { - delegate.register_solver_region_constraint(ty::RegionConstraint::And( - region_constraints.into_boxed_slice(), - )); - } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index b7aee3ca4d2e7..9a731fac9b63a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1275,19 +1275,12 @@ where lhs: T, rhs: T, ) -> Result<(), NoSolutionOrRerunNonErased> { - let result = if self.cx().assumptions_on_binders() { - let (goals, region_constraints) = - self.delegate.eq_structurally_relating_aliases_with_region_constraints( - param_env, - lhs, - rhs, - self.origin_span, - )?; - self.register_solver_region_constraint(region_constraints); - goals - } else { - self.delegate.eq_structurally_relating_aliases(param_env, lhs, rhs, self.origin_span)? - }; + let result = self.delegate.eq_structurally_relating_aliases( + param_env, + lhs, + rhs, + self.origin_span, + )?; assert_eq!(result, vec![]); Ok(()) } @@ -1310,19 +1303,7 @@ where variance: ty::Variance, rhs: T, ) -> Result<(), NoSolutionOrRerunNonErased> { - let goals = if self.cx().assumptions_on_binders() { - let (goals, region_constraints) = self.delegate.relate_with_region_constraints( - param_env, - lhs, - variance, - rhs, - self.origin_span, - )?; - self.register_solver_region_constraint(region_constraints); - goals - } else { - self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)? - }; + let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; for &goal in goals.iter() { let source = match goal.predicate.kind().skip_binder() { ty::PredicateKind::Subtype { .. } @@ -1345,30 +1326,12 @@ where /// goals correctly. #[instrument(level = "trace", skip(self, param_env), ret)] pub(super) fn eq_and_get_goals>( - &mut self, + &self, param_env: I::ParamEnv, lhs: T, rhs: T, ) -> Result>, NoSolution> { - if self.cx().assumptions_on_binders() { - let (goals, region_constraints) = self.delegate.relate_with_region_constraints( - param_env, - lhs, - ty::Variance::Invariant, - rhs, - self.origin_span, - )?; - self.register_solver_region_constraint(region_constraints); - Ok(goals) - } else { - Ok(self.delegate.relate( - param_env, - lhs, - ty::Variance::Invariant, - rhs, - self.origin_span, - )?) - } + Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs, self.origin_span)?) } pub(super) fn instantiate_binder_with_infer + Copy>( diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 1e9328aa129dc..47a2ddfb269ab 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -20,53 +20,6 @@ pub trait RelateExt: InferCtxtLike { Vec::Predicate>>, TypeError, >; - fn relate_with_region_constraints>( - &self, - param_env: ::ParamEnv, - lhs: T, - variance: ty::Variance, - rhs: T, - span: ::Span, - ) -> Result< - ( - Vec::Predicate>>, - RegionConstraint, - ), - TypeError, - >; -} - -fn relate_with_options( - infcx: &Infcx, - param_env: ::ParamEnv, - lhs: T, - variance: ty::Variance, - rhs: T, - span: ::Span, - collect_region_constraints: bool, -) -> Result< - ( - Vec::Predicate>>, - Option>, - ), - TypeError, -> -where - Infcx: InferCtxtLike, - T: Relate, -{ - let mut relate = SolverRelating::build( - infcx, - variance, - param_env, - span, - collect_region_constraints.then(Vec::new), - ); - relate.relate(lhs, rhs)?; - Ok(( - relate.goals, - relate.region_constraints.map(|c| RegionConstraint::And(c.into_boxed_slice())), - )) } impl RelateExt for Infcx { @@ -81,42 +34,9 @@ impl RelateExt for Infcx { Vec::Predicate>>, TypeError, > { - let (goals, _) = relate_with_options( - self, - param_env, - lhs, - variance, - rhs, - span, - false, - )?; - Ok(goals) - } - - fn relate_with_region_constraints>( - &self, - param_env: ::ParamEnv, - lhs: T, - variance: ty::Variance, - rhs: T, - span: ::Span, - ) -> Result< - ( - Vec::Predicate>>, - RegionConstraint, - ), - TypeError, - > { - let (goals, region_constraints) = relate_with_options( - self, - param_env, - lhs, - variance, - rhs, - span, - true, - )?; - Ok((goals, region_constraints.unwrap())) + let mut relate = SolverRelating::new(self, variance, param_env, span); + relate.relate(lhs, rhs)?; + Ok(relate.goals) } } @@ -129,7 +49,6 @@ pub struct SolverRelating<'infcx, Infcx, I: Interner> { // Mutable fields. ambient_variance: ty::Variance, goals: Vec>, - region_constraints: Option>>, /// The cache only tracks the `ambient_variance` as it's the /// only field which is mutable and which meaningfully changes /// the result when relating types. @@ -165,16 +84,6 @@ where ambient_variance: ty::Variance, param_env: I::ParamEnv, span: I::Span, - ) -> Self { - Self::build(infcx, ambient_variance, param_env, span, None) - } - - fn build( - infcx: &'infcx Infcx, - ambient_variance: ty::Variance, - param_env: I::ParamEnv, - span: I::Span, - region_constraints: Option>>, ) -> Self { SolverRelating { infcx, @@ -182,7 +91,6 @@ where ambient_variance, param_env, goals: vec![], - region_constraints, cache: Default::default(), } } @@ -331,42 +239,39 @@ where #[instrument(skip(self), level = "trace")] fn regions(&mut self, a: Region, b: Region) -> RelateResult> { - match self.ambient_variance { - // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a) - ty::Covariant => self.infcx.sub_regions(b, a, VisibleForLeakCheck::Yes, self.span), - // Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b) - ty::Contravariant => self.infcx.sub_regions(a, b, VisibleForLeakCheck::Yes, self.span), - ty::Invariant => self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span), - ty::Bivariant => { - unreachable!("Expected bivariance to be handled in relate_with_variance") - } - } - - let resolve_region = |r: I::Region| match r.kind() { - ty::ReVar(vid) => self.infcx.opportunistic_resolve_lt_var(vid), - _ => r, - }; - let a = resolve_region(a); - let b = resolve_region(b); - - if let Some(region_constraints) = &mut self.region_constraints { + if self.cx().assumptions_on_binders() { if a == b { return Ok(a); - } else { - match self.ambient_variance { - ty::Covariant => { - region_constraints.push(RegionConstraint::RegionOutlives(a, b)); - } - ty::Contravariant => { - region_constraints.push(RegionConstraint::RegionOutlives(b, a)); - } - ty::Invariant => { - region_constraints.push(RegionConstraint::RegionOutlives(a, b)); - region_constraints.push(RegionConstraint::RegionOutlives(b, a)); - } - ty::Bivariant => { - unreachable!("Expected bivariance to be handled in relate_with_variance") - } + } + + match self.ambient_variance { + ty::Covariant => self + .infcx + .register_solver_region_constraint(RegionConstraint::RegionOutlives(a, b)), + ty::Contravariant => self + .infcx + .register_solver_region_constraint(RegionConstraint::RegionOutlives(b, a)), + ty::Invariant => { + self.infcx + .register_solver_region_constraint(RegionConstraint::RegionOutlives(a, b)); + self.infcx + .register_solver_region_constraint(RegionConstraint::RegionOutlives(b, a)); + } + ty::Bivariant => { + unreachable!("Expected bivariance to be handled in relate_with_variance") + } + } + } else { + match self.ambient_variance { + ty::Covariant => self.infcx.sub_regions(b, a, VisibleForLeakCheck::Yes, self.span), + ty::Contravariant => { + self.infcx.sub_regions(a, b, VisibleForLeakCheck::Yes, self.span) + } + ty::Invariant => { + self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span) + } + ty::Bivariant => { + unreachable!("Expected bivariance to be handled in relate_with_variance") } } } From 44ce530645fc060cc21238081aa11326e1dcf610 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Tue, 23 Jun 2026 14:10:30 -0300 Subject: [PATCH 03/17] Reproduce direct solver region registration failures --- .../src/solve/eval_ctxt/mod.rs | 65 ------------------- 1 file changed, 65 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 9a731fac9b63a..aa7e1e79b5866 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1220,71 +1220,6 @@ where self.relate(param_env, lhs, ty::Variance::Invariant, rhs) } - /// This should be used when relating a rigid alias with another type. - /// - /// Normally we emit a nested `AliasRelate` when equating an inference - /// variable and an alias. This causes us to instead constrain the inference - /// variable to the alias without emitting a nested alias relate goals. - #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn relate_rigid_alias_non_alias( - &mut self, - param_env: I::ParamEnv, - alias: ty::AliasTerm, - variance: ty::Variance, - term: I::Term, - ) -> Result<(), NoSolutionOrRerunNonErased> { - // NOTE: this check is purely an optimization, the structural eq would - // always fail if the term is not an inference variable. - if term.is_infer() { - let cx = self.cx(); - // We need to relate `alias` to `term` treating only the outermost - // constructor as rigid, relating any contained generic arguments as - // normal. We do this by first structurally equating the `term` - // with the alias constructor instantiated with unconstrained infer vars, - // and then relate this with the whole `alias`. - // - // Alternatively we could modify `Equate` for this case by adding another - // variant to `StructurallyRelateAliases`. - let def_id = match alias.kind { - ty::AliasTermKind::ProjectionTy { def_id } => def_id.into(), - ty::AliasTermKind::InherentTy { def_id } => def_id.into(), - ty::AliasTermKind::OpaqueTy { def_id } => def_id.into(), - ty::AliasTermKind::FreeTy { def_id } => def_id.into(), - ty::AliasTermKind::AnonConst { def_id } => def_id.into(), - ty::AliasTermKind::ProjectionConst { def_id } => def_id.into(), - ty::AliasTermKind::FreeConst { def_id } => def_id.into(), - ty::AliasTermKind::InherentConst { def_id } => def_id.into(), - }; - let identity_args = self.fresh_args_for_item(def_id); - let rigid_ctor = alias.with_args(cx, identity_args); - let ctor_term = rigid_ctor.to_term(cx); - self.eq_structurally_relating_aliases(param_env, term, ctor_term)?; - self.relate(param_env, alias, variance, rigid_ctor) - } else { - Err(NoSolution.into()) - } - } - - /// This should only be used when we're either instantiating a previously - /// unconstrained "return value" or when we're sure that all aliases in - /// the types are rigid. - #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn eq_structurally_relating_aliases>( - &mut self, - param_env: I::ParamEnv, - lhs: T, - rhs: T, - ) -> Result<(), NoSolutionOrRerunNonErased> { - let result = self.delegate.eq_structurally_relating_aliases( - param_env, - lhs, - rhs, - self.origin_span, - )?; - assert_eq!(result, vec![]); - Ok(()) - } - #[instrument(level = "trace", skip(self, param_env), ret)] pub(super) fn sub>( &mut self, From 12d527f2cd34b4a8ad7a57f00b4f05d53085d24a Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Fri, 10 Jul 2026 14:50:44 -0300 Subject: [PATCH 04/17] Handle reflexive solver region constraints --- compiler/rustc_type_ir/src/region_constraint.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 2592c0579c741..70ba2ada96fe4 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -681,6 +681,10 @@ fn pull_region_outlives_constraints_out_of_universe< constraint } RegionOutlives(region_1, region_2, ()) => { + if region_1 == region_2 { + // Reflexive constraints are always satisfied, even if the region is from `u`. + return RegionConstraint::new_true(); + } let region_1_u = max_universe(infcx, region_1); let region_2_u = max_universe(infcx, region_2); From 6766f9f4afbba201f0eadf6eb0b7062d787d855a Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Fri, 10 Jul 2026 14:51:48 -0300 Subject: [PATCH 05/17] Normalize equated region vars in solver constraints --- .../rustc_type_ir/src/region_constraint.rs | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 70ba2ada96fe4..f2bb742ba885b 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -485,6 +485,10 @@ pub fn eagerly_handle_placeholders_in_universe, I: Interner>( + infcx: &Infcx, + constraint: RegionConstraint, + u: UniverseIndex, +) -> RegionConstraint { + use RegionConstraint::*; + + match constraint { + Ambiguity | RegionOutlives(..) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { + constraint + } + Or(constraints) => Or(constraints + .into_iter() + .map(|constraint| normalize_equated_region_vars(infcx, constraint, u)) + .collect()), + And(constraints) => { + let constraint = And(constraints + .into_iter() + .map(|constraint| normalize_equated_region_vars(infcx, constraint, u)) + .collect()); + + let mut region_outlives = vec![]; + collect_conjunctive_region_outlives(&constraint, &mut region_outlives); + + let mut replacements = vec![]; + for (r1, r2) in region_outlives.iter().copied() { + if let Some(partner) = equated_non_var_partner(infcx, ®ion_outlives, r1, r2, u) { + replacements.push((r1, partner)); + } + + if let Some(partner) = equated_non_var_partner(infcx, ®ion_outlives, r2, r1, u) { + replacements.push((r2, partner)); + } + } + + if replacements.is_empty() { + constraint + } else { + constraint.fold_with(&mut EquatedRegionVarReplacer { cx: infcx.cx(), replacements }) + } + } + } +} + +fn equated_non_var_partner, I: Interner>( + infcx: &Infcx, + region_outlives: &[(I::Region, I::Region)], + candidate: I::Region, + partner: I::Region, + u: UniverseIndex, +) -> Option { + if is_current_universe_region_var(infcx, candidate, u) + && !is_region_var::(partner) + && region_outlives + .iter() + .any(|(outlives, outlived)| *outlives == partner && *outlived == candidate) + { + Some(partner) + } else { + None + } +} + +fn collect_conjunctive_region_outlives( + constraint: &RegionConstraint, + out: &mut Vec<(I::Region, I::Region)>, +) { + use RegionConstraint::*; + + match constraint { + RegionOutlives(r1, r2) => out.push((*r1, *r2)), + And(constraints) => { + for constraint in constraints.iter() { + collect_conjunctive_region_outlives(constraint, out); + } + } + Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) | Or(..) => {} + } +} + +fn is_current_universe_region_var, I: Interner>( + infcx: &Infcx, + region: I::Region, + u: UniverseIndex, +) -> bool { + is_region_var::(region) && max_universe(infcx, region) == u +} + +fn is_region_var(region: I::Region) -> bool { + matches!(region.kind(), RegionKind::ReVar(_)) +} + +struct EquatedRegionVarReplacer { + cx: I, + replacements: Vec<(I::Region, I::Region)>, +} + +impl TypeFolder for EquatedRegionVarReplacer { + fn cx(&self) -> I { + self.cx + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + // If a region variable has multiple non-var partners, the remaining folded + // constraints still relate those partners, so first-match only affects representation. + self.replacements.iter().find_map(|(from, to)| (*from == r).then_some(*to)).unwrap_or(r) + } +} + /// Filter our region constraints to not include constraints between region variables from `u` and /// other regions as those are always satisfied. This requires some care to handle correctly for example: /// `'!a_u1: '?x_u1: '!b_u1` should result in us requiring `'!a_u1: '!b_u1` rather than dropping the two From 167bdbae6769b7a4d044a4fcc143386fbdff6029 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 11 Jul 2026 11:01:03 -0300 Subject: [PATCH 06/17] Normalize transitive equated region variables --- .../rustc_type_ir/src/region_constraint.rs | 109 ++++++++++++++---- 1 file changed, 85 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index f2bb742ba885b..9ef23466b9e3b 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -548,17 +548,7 @@ fn normalize_equated_region_vars, I: Interner let mut region_outlives = vec![]; collect_conjunctive_region_outlives(&constraint, &mut region_outlives); - - let mut replacements = vec![]; - for (r1, r2) in region_outlives.iter().copied() { - if let Some(partner) = equated_non_var_partner(infcx, ®ion_outlives, r1, r2, u) { - replacements.push((r1, partner)); - } - - if let Some(partner) = equated_non_var_partner(infcx, ®ion_outlives, r2, r1, u) { - replacements.push((r2, partner)); - } - } + let replacements = compute_equated_region_var_replacements(infcx, ®ion_outlives, u); if replacements.is_empty() { constraint @@ -569,23 +559,67 @@ fn normalize_equated_region_vars, I: Interner } } -fn equated_non_var_partner, I: Interner>( +fn compute_equated_region_var_replacements, I: Interner>( infcx: &Infcx, region_outlives: &[(I::Region, I::Region)], - candidate: I::Region, - partner: I::Region, u: UniverseIndex, -) -> Option { - if is_current_universe_region_var(infcx, candidate, u) - && !is_region_var::(partner) - && region_outlives - .iter() - .any(|(outlives, outlived)| *outlives == partner && *outlived == candidate) - { - Some(partner) - } else { - None +) -> Vec<(I::Region, I::Region)> { + compute_equated_region_var_replacements_from( + region_outlives, + |r| is_current_universe_region_var(infcx, r, u), + is_region_var::, + ) +} + +fn compute_equated_region_var_replacements_from( + region_outlives: &[(R, R)], + mut is_current_universe_region_var: impl FnMut(R) -> bool, + mut is_region_var: impl FnMut(R) -> bool, +) -> Vec<(R, R)> +where + R: Copy + Eq + std::hash::Hash, +{ + let mut equated_regions_builder = TransitiveRelationBuilder::default(); + let mut has_equated_regions = false; + for (r1, r2) in region_outlives.iter().copied() { + // Paired outlives constraints represent region equality. Build a transitive relation so + // current-universe variables equated through other variables still find a non-var partner. + if has_reverse_region_outlives_edge(region_outlives, r1, r2) { + equated_regions_builder.add(r1, r2); + equated_regions_builder.add(r2, r1); + has_equated_regions = true; + } + } + + if !has_equated_regions { + return vec![]; + } + + let equated_regions = equated_regions_builder.freeze(); + let mut candidates = IndexSet::new(); + for (r1, r2) in region_outlives.iter().copied() { + if is_current_universe_region_var(r1) { + candidates.insert(r1); + } + + if is_current_universe_region_var(r2) { + candidates.insert(r2); + } } + + candidates + .into_iter() + .filter_map(|candidate| { + std::iter::once(candidate) + .chain(equated_regions.reachable_from(candidate)) + .find(|r| !is_region_var(*r)) + .map(|partner| (candidate, partner)) + }) + .collect() +} + +fn has_reverse_region_outlives_edge(region_outlives: &[(R, R)], r1: R, r2: R) -> bool { + region_outlives.iter().any(|(outlives, outlived)| outlives == &r2 && outlived == &r1) } fn collect_conjunctive_region_outlives( @@ -634,6 +668,33 @@ impl TypeFolder for EquatedRegionVarReplacer { } } +#[cfg(test)] +mod tests { + use super::compute_equated_region_var_replacements_from; + + #[test] + fn equated_region_var_replacements_follow_transitive_region_var_chains() { + const REVAR_1: u8 = 1; + const REVAR_2: u8 = 2; + const PLACEHOLDER: u8 = 3; + + let region_outlives = [ + (REVAR_1, REVAR_2), + (REVAR_2, REVAR_1), + (REVAR_2, PLACEHOLDER), + (PLACEHOLDER, REVAR_2), + ]; + + let replacements = compute_equated_region_var_replacements_from( + ®ion_outlives, + |r| matches!(r, REVAR_1 | REVAR_2), + |r| matches!(r, REVAR_1 | REVAR_2), + ); + + assert_eq!(replacements, vec![(REVAR_1, PLACEHOLDER), (REVAR_2, PLACEHOLDER)]); + } +} + /// Filter our region constraints to not include constraints between region variables from `u` and /// other regions as those are always satisfied. This requires some care to handle correctly for example: /// `'!a_u1: '?x_u1: '!b_u1` should result in us requiring `'!a_u1: '!b_u1` rather than dropping the two From e6a145e11a4a8e8a895ea0599198cba7aaf51222 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 11 Jul 2026 11:12:20 -0300 Subject: [PATCH 07/17] Move region constraint tests into test module --- .../rustc_type_ir/src/region_constraint.rs | 26 +------------------ .../src/region_constraint/tests.rs | 19 ++++++++++++++ 2 files changed, 20 insertions(+), 25 deletions(-) create mode 100644 compiler/rustc_type_ir/src/region_constraint/tests.rs diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 9ef23466b9e3b..2da2817e1f17c 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -669,31 +669,7 @@ impl TypeFolder for EquatedRegionVarReplacer { } #[cfg(test)] -mod tests { - use super::compute_equated_region_var_replacements_from; - - #[test] - fn equated_region_var_replacements_follow_transitive_region_var_chains() { - const REVAR_1: u8 = 1; - const REVAR_2: u8 = 2; - const PLACEHOLDER: u8 = 3; - - let region_outlives = [ - (REVAR_1, REVAR_2), - (REVAR_2, REVAR_1), - (REVAR_2, PLACEHOLDER), - (PLACEHOLDER, REVAR_2), - ]; - - let replacements = compute_equated_region_var_replacements_from( - ®ion_outlives, - |r| matches!(r, REVAR_1 | REVAR_2), - |r| matches!(r, REVAR_1 | REVAR_2), - ); - - assert_eq!(replacements, vec![(REVAR_1, PLACEHOLDER), (REVAR_2, PLACEHOLDER)]); - } -} +mod tests; /// Filter our region constraints to not include constraints between region variables from `u` and /// other regions as those are always satisfied. This requires some care to handle correctly for example: diff --git a/compiler/rustc_type_ir/src/region_constraint/tests.rs b/compiler/rustc_type_ir/src/region_constraint/tests.rs new file mode 100644 index 0000000000000..b77884683c9f4 --- /dev/null +++ b/compiler/rustc_type_ir/src/region_constraint/tests.rs @@ -0,0 +1,19 @@ +use super::compute_equated_region_var_replacements_from; + +#[test] +fn equated_region_var_replacements_follow_transitive_region_var_chains() { + const REVAR_1: u8 = 1; + const REVAR_2: u8 = 2; + const PLACEHOLDER: u8 = 3; + + let region_outlives = + [(REVAR_1, REVAR_2), (REVAR_2, REVAR_1), (REVAR_2, PLACEHOLDER), (PLACEHOLDER, REVAR_2)]; + + let replacements = compute_equated_region_var_replacements_from( + ®ion_outlives, + |r| matches!(r, REVAR_1 | REVAR_2), + |r| matches!(r, REVAR_1 | REVAR_2), + ); + + assert_eq!(replacements, vec![(REVAR_1, PLACEHOLDER), (REVAR_2, PLACEHOLDER)]); +} From 90911b3b0c9e7b13c16564a173db9091bbbfc57b Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Tue, 21 Jul 2026 20:46:26 -0300 Subject: [PATCH 08/17] Adapt region constraints to upstream APIs --- .../rustc_next_trait_solver/src/canonical/mod.rs | 11 +++++++---- compiler/rustc_type_ir/src/region_constraint.rs | 14 +++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index edec3f03ed378..8e938c6fdeef0 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -14,6 +14,7 @@ use std::iter; use canonicalizer::Canonicalizer; use rustc_index::IndexVec; use rustc_type_ir::inherent::*; +use rustc_type_ir::region_constraint::RegionConstraint as SolverRegionConstraint; use rustc_type_ir::relate::{ self, Relate, RelateResult, TypeRelation, VarianceDiagInfo, relate_args_invariantly, }; @@ -417,10 +418,12 @@ where fn regions(&mut self, a: Region, b: Region) -> RelateResult> { if self.cx().assumptions_on_binders() { if a != b { - self.infcx - .register_solver_region_constraint(ty::RegionConstraint::RegionOutlives(a, b)); - self.infcx - .register_solver_region_constraint(ty::RegionConstraint::RegionOutlives(b, a)); + self.infcx.register_solver_region_constraint( + SolverRegionConstraint::RegionOutlives(a, b), + ); + self.infcx.register_solver_region_constraint( + SolverRegionConstraint::RegionOutlives(b, a), + ); } } else { self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span); diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 2da2817e1f17c..8f37e42264414 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -561,9 +561,9 @@ fn normalize_equated_region_vars, I: Interner fn compute_equated_region_var_replacements, I: Interner>( infcx: &Infcx, - region_outlives: &[(I::Region, I::Region)], + region_outlives: &[(Region, Region)], u: UniverseIndex, -) -> Vec<(I::Region, I::Region)> { +) -> Vec<(Region, Region)> { compute_equated_region_var_replacements_from( region_outlives, |r| is_current_universe_region_var(infcx, r, u), @@ -624,7 +624,7 @@ fn has_reverse_region_outlives_edge(region_outlives: &[(R, R)], r1: R, r2 fn collect_conjunctive_region_outlives( constraint: &RegionConstraint, - out: &mut Vec<(I::Region, I::Region)>, + out: &mut Vec<(Region, Region)>, ) { use RegionConstraint::*; @@ -641,19 +641,19 @@ fn collect_conjunctive_region_outlives( fn is_current_universe_region_var, I: Interner>( infcx: &Infcx, - region: I::Region, + region: Region, u: UniverseIndex, ) -> bool { is_region_var::(region) && max_universe(infcx, region) == u } -fn is_region_var(region: I::Region) -> bool { +fn is_region_var(region: Region) -> bool { matches!(region.kind(), RegionKind::ReVar(_)) } struct EquatedRegionVarReplacer { cx: I, - replacements: Vec<(I::Region, I::Region)>, + replacements: Vec<(Region, Region)>, } impl TypeFolder for EquatedRegionVarReplacer { @@ -661,7 +661,7 @@ impl TypeFolder for EquatedRegionVarReplacer { self.cx } - fn fold_region(&mut self, r: I::Region) -> I::Region { + fn fold_region(&mut self, r: Region) -> Region { // If a region variable has multiple non-var partners, the remaining folded // constraints still relate those partners, so first-match only affects representation. self.replacements.iter().find_map(|(from, to)| (*from == r).then_some(*to)).unwrap_or(r) From dc8a6e4d9545a07bab859d090ef8706ef8026bf0 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Tue, 21 Jul 2026 20:46:42 -0300 Subject: [PATCH 09/17] Clarify region constraint branch terminology --- compiler/rustc_type_ir/src/region_constraint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 8f37e42264414..6b6c5cdb4cfdd 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -486,7 +486,7 @@ pub fn eagerly_handle_placeholders_in_universe Date: Wed, 19 Aug 2026 22:47:52 -0300 Subject: [PATCH 10/17] Match bivariance before region equality --- compiler/rustc_type_ir/src/relate/solver_relating.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 47a2ddfb269ab..573a5523f3750 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -240,11 +240,11 @@ where #[instrument(skip(self), level = "trace")] fn regions(&mut self, a: Region, b: Region) -> RelateResult> { if self.cx().assumptions_on_binders() { - if a == b { - return Ok(a); - } - match self.ambient_variance { + ty::Bivariant => { + unreachable!("Expected bivariance to be handled in relate_with_variance") + } + _ if a == b => return Ok(a), ty::Covariant => self .infcx .register_solver_region_constraint(RegionConstraint::RegionOutlives(a, b)), @@ -257,9 +257,6 @@ where self.infcx .register_solver_region_constraint(RegionConstraint::RegionOutlives(b, a)); } - ty::Bivariant => { - unreachable!("Expected bivariance to be handled in relate_with_variance") - } } } else { match self.ambient_variance { From ba848af1fd42a734e224f65ab6ead12eb2054e90 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Wed, 19 Aug 2026 22:47:57 -0300 Subject: [PATCH 11/17] Annotate principal upcast coerce error --- .../principal-upcast-region-eq-issue-157859.rs | 4 ++-- .../principal-upcast-region-eq-issue-157859.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs index 52b90750e01a9..f3050fe336eb1 100644 --- a/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs +++ b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs @@ -1,10 +1,10 @@ -//@compile-flags: -Zassumptions-on-binders -Znext-solver=globally -//@ dont-require-annotations: ERROR +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally trait Super { fn a(&self) { let a: &dyn Sub = &(); let b: &dyn Super fn(&'a ())> = a; + //~^ ERROR the trait bound `&dyn Sub: CoerceUnsized<&dyn Super fn(&'a ())>>` is not satisfied } } diff --git a/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr index 5ed8cb2dce80a..afc1f56491503 100644 --- a/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr +++ b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `&dyn Sub: CoerceUnsized<&dyn Super fn(&'a ())>>` is not satisfied - --> $DIR/principal-upcast-region-eq-issue-157859.rs:7:49 + --> $DIR/principal-upcast-region-eq-issue-157859.rs:6:49 | LL | let b: &dyn Super fn(&'a ())> = a; | ^ the nightly-only, unstable trait `Unsize fn(&'a ())>>` is not implemented for `dyn Sub` From bcbf75b1a8bde64442afd61283b271f4f69d458e Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Wed, 19 Aug 2026 22:48:23 -0300 Subject: [PATCH 12/17] Store equated region replacements in a map --- .../rustc_type_ir/src/region_constraint.rs | 61 +++++++++---------- .../src/region_constraint/tests.rs | 18 ++++-- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 6b6c5cdb4cfdd..b72e954e95d59 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -45,7 +45,7 @@ impl Default for TransitiveRelationBuilder { } } -use crate::data_structures::IndexMap; +use crate::data_structures::{HashMap, HashSet, IndexMap}; use crate::fold::TypeSuperFoldable; use crate::inherent::*; use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; @@ -563,7 +563,7 @@ fn compute_equated_region_var_replacements, I infcx: &Infcx, region_outlives: &[(Region, Region)], u: UniverseIndex, -) -> Vec<(Region, Region)> { +) -> HashMap, Region> { compute_equated_region_var_replacements_from( region_outlives, |r| is_current_universe_region_var(infcx, r, u), @@ -575,16 +575,18 @@ fn compute_equated_region_var_replacements_from( region_outlives: &[(R, R)], mut is_current_universe_region_var: impl FnMut(R) -> bool, mut is_region_var: impl FnMut(R) -> bool, -) -> Vec<(R, R)> +) -> HashMap where R: Copy + Eq + std::hash::Hash, { + let edges: HashSet<(R, R)> = region_outlives.iter().copied().collect(); + let mut equated_regions_builder = TransitiveRelationBuilder::default(); let mut has_equated_regions = false; for (r1, r2) in region_outlives.iter().copied() { // Paired outlives constraints represent region equality. Build a transitive relation so // current-universe variables equated through other variables still find a non-var partner. - if has_reverse_region_outlives_edge(region_outlives, r1, r2) { + if edges.contains(&(r2, r1)) { equated_regions_builder.add(r1, r2); equated_regions_builder.add(r2, r1); has_equated_regions = true; @@ -592,34 +594,31 @@ where } if !has_equated_regions { - return vec![]; + return HashMap::default(); } let equated_regions = equated_regions_builder.freeze(); - let mut candidates = IndexSet::new(); + let mut seen = HashSet::default(); + let mut replacements = HashMap::default(); for (r1, r2) in region_outlives.iter().copied() { - if is_current_universe_region_var(r1) { - candidates.insert(r1); - } + for candidate in [r1, r2] { + if !seen.insert(candidate) || !is_current_universe_region_var(candidate) { + continue; + } - if is_current_universe_region_var(r2) { - candidates.insert(r2); + // `reachable_from` already includes `candidate` when both equality edges exist. + // Candidates are always revars, so the partner has to come from that closure. + // If a var has several non-var partners, `find` just picks one; the remaining + // folded constraints still relate those partners, so first-match only affects + // representation. + if let Some(partner) = + equated_regions.reachable_from(candidate).into_iter().find(|r| !is_region_var(*r)) + { + replacements.insert(candidate, partner); + } } } - - candidates - .into_iter() - .filter_map(|candidate| { - std::iter::once(candidate) - .chain(equated_regions.reachable_from(candidate)) - .find(|r| !is_region_var(*r)) - .map(|partner| (candidate, partner)) - }) - .collect() -} - -fn has_reverse_region_outlives_edge(region_outlives: &[(R, R)], r1: R, r2: R) -> bool { - region_outlives.iter().any(|(outlives, outlived)| outlives == &r2 && outlived == &r1) + replacements } fn collect_conjunctive_region_outlives( @@ -653,7 +652,7 @@ fn is_region_var(region: Region) -> bool { struct EquatedRegionVarReplacer { cx: I, - replacements: Vec<(Region, Region)>, + replacements: HashMap, Region>, } impl TypeFolder for EquatedRegionVarReplacer { @@ -662,15 +661,10 @@ impl TypeFolder for EquatedRegionVarReplacer { } fn fold_region(&mut self, r: Region) -> Region { - // If a region variable has multiple non-var partners, the remaining folded - // constraints still relate those partners, so first-match only affects representation. - self.replacements.iter().find_map(|(from, to)| (*from == r).then_some(*to)).unwrap_or(r) + self.replacements.get(&r).copied().unwrap_or(r) } } -#[cfg(test)] -mod tests; - /// Filter our region constraints to not include constraints between region variables from `u` and /// other regions as those are always satisfied. This requires some care to handle correctly for example: /// `'!a_u1: '?x_u1: '!b_u1` should result in us requiring `'!a_u1: '!b_u1` rather than dropping the two @@ -1329,3 +1323,6 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> TypeRelation Ok(a) } } + +#[cfg(all(test, feature = "nightly"))] +mod tests; diff --git a/compiler/rustc_type_ir/src/region_constraint/tests.rs b/compiler/rustc_type_ir/src/region_constraint/tests.rs index b77884683c9f4..831d98f590162 100644 --- a/compiler/rustc_type_ir/src/region_constraint/tests.rs +++ b/compiler/rustc_type_ir/src/region_constraint/tests.rs @@ -5,15 +5,25 @@ fn equated_region_var_replacements_follow_transitive_region_var_chains() { const REVAR_1: u8 = 1; const REVAR_2: u8 = 2; const PLACEHOLDER: u8 = 3; + // Equated with REVAR_1, but not a current-universe candidate and not a valid partner. + const OTHER_REVAR: u8 = 4; - let region_outlives = - [(REVAR_1, REVAR_2), (REVAR_2, REVAR_1), (REVAR_2, PLACEHOLDER), (PLACEHOLDER, REVAR_2)]; + let region_outlives = [ + (REVAR_1, REVAR_2), + (REVAR_2, REVAR_1), + (REVAR_2, PLACEHOLDER), + (PLACEHOLDER, REVAR_2), + (REVAR_1, OTHER_REVAR), + (OTHER_REVAR, REVAR_1), + ]; let replacements = compute_equated_region_var_replacements_from( ®ion_outlives, |r| matches!(r, REVAR_1 | REVAR_2), - |r| matches!(r, REVAR_1 | REVAR_2), + |r| matches!(r, REVAR_1 | REVAR_2 | OTHER_REVAR), ); - assert_eq!(replacements, vec![(REVAR_1, PLACEHOLDER), (REVAR_2, PLACEHOLDER)]); + assert_eq!(replacements.len(), 2); + assert_eq!(replacements.get(&REVAR_1), Some(&PLACEHOLDER)); + assert_eq!(replacements.get(&REVAR_2), Some(&PLACEHOLDER)); } From 6d48755ec48bbf86e4c4d960718fc028fc63b02d Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Wed, 19 Aug 2026 22:48:45 -0300 Subject: [PATCH 13/17] Keep ambiguity beside remaining Or candidates Unknown or remaining must not drop the unknown sibling. A later-false candidate would otherwise become NoSolution. --- .../src/infer/outlives/obligations.rs | 25 ++++- .../rustc_type_ir/src/region_constraint.rs | 91 ++++++++++++++++--- .../src/region_constraint/tests.rs | 17 +++- .../assumptions_on_binders/alias_outlives.rs | 5 + .../alias_outlives.stderr | 3 +- ...higher_ranked_alias_outlives_assumption.rs | 3 + 6 files changed, 126 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 2bf73e5da7e34..f5e7d4bc03563 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -279,8 +279,29 @@ impl<'tcx> InferCtxt<'tcx> { b, a, category, ); } - // FIXME(-Zassumptions-on-binders): actually implement OR as an OR - And(nested) | Or(nested) => constraints.extend(nested), + And(nested) => { + debug_assert!(!nested.iter().any(|c| c.is_ambig())); + constraints.extend(nested); + } + // FIXME(-Zassumptions-on-binders): actually implement OR as an OR. + // Mixed `Or` may contain `Ambiguity` beside remaining candidates + // (`evaluate_solver_constraint` keeps that sibling so unknown ∨ + // later-false does not become false). Don't emit the + // unknown-implied-bounds error while a concrete candidate remains. + // `Or([])` is false: we drop it the same way `extend` does on an + // empty slice. A root-false constraint has nothing to register; + // unsatisfied outlives are reported later by borrowck/regionck. + Or(nested) => { + let had_members = !nested.is_empty(); + let concrete: Vec<_> = nested.into_iter().filter(|c| !c.is_ambig()).collect(); + if concrete.is_empty() { + if had_members { + constraints.push(Ambiguity); + } + } else { + constraints.extend(concrete); + } + } AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => unreachable!(), } } diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index b72e954e95d59..c4af7c395216f 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -723,7 +723,65 @@ fn compute_new_region_constraints, I: Interne new_constraints } -/// Evaluate ANDs and ORs to true/false/ambiguous based on whether their arguments are true/false/ambiguous +/// Already-evaluated OR member, used by [`combine_or`]. +#[derive(Clone, Debug, PartialEq, Eq)] +enum EvaluatedOrMember { + True, + False, + Ambiguity, + Other(T), +} + +/// Kleene OR of already-evaluated members. +#[derive(Clone, Debug, PartialEq, Eq)] +enum CombinedOr { + True, + False, + Ambiguity, + /// Still-open candidates. `plus_ambiguity` means an unknown sibling must + /// stay beside them: unknown ∨ remaining must not drop the unknown. + Or { + remaining: Vec, + plus_ambiguity: bool, + }, +} + +/// Combine already-evaluated OR members. +/// +/// `true ∨ x = true`. `false` members are dropped. `unknown ∨ remaining` keeps +/// both: collapsing to `Ambiguity` drops candidates a later universe may still +/// satisfy, and dropping `Ambiguity` makes a later-false remaining collapse +/// unknown ∨ false to false (spurious `NoSolution`). +fn combine_or(members: impl IntoIterator>) -> CombinedOr { + let mut remaining = Vec::new(); + let mut plus_ambiguity = false; + for member in members { + match member { + EvaluatedOrMember::True => return CombinedOr::True, + EvaluatedOrMember::False => {} + EvaluatedOrMember::Ambiguity => plus_ambiguity = true, + EvaluatedOrMember::Other(c) => remaining.push(c), + } + } + + if remaining.is_empty() { + if plus_ambiguity { CombinedOr::Ambiguity } else { CombinedOr::False } + } else { + CombinedOr::Or { remaining, plus_ambiguity } + } +} + +/// Evaluate ANDs and ORs to true/false/ambiguous based on whether their arguments are +/// true/false/ambiguous. +/// +/// `Or(Ambiguity, remaining)` keeps both. Collapsing to `Ambiguity` drops +/// candidates a later universe (or the root) may still satisfy. Dropping the +/// `Ambiguity` sibling is also wrong: if `remaining` later becomes false, +/// unknown ∨ false would become false. +/// +/// `And` is the other way: one ambiguous conjunct makes the whole conjunction +/// unknown, so we still collapse. That is deliberate, not an oversight relative +/// to `Or`. #[instrument(level = "debug", ret)] pub fn evaluate_solver_constraint( constraint: &RegionConstraint, @@ -756,25 +814,32 @@ pub fn evaluate_solver_constraint( ) } Or(or) => { - let mut or_constraints = Vec::new(); let mut ambiguity = None; - for c in or.iter() { + let members = or.iter().map(|c| { let evaluated_constraint = evaluate_solver_constraint(c); - if evaluated_constraint.is_false() { - // do nothing - } else if evaluated_constraint.is_true() { - return RegionConstraint::new_true(); + if evaluated_constraint.is_true() { + EvaluatedOrMember::True + } else if evaluated_constraint.is_false() { + EvaluatedOrMember::False } else if let Ambiguity(span) = evaluated_constraint { ambiguity.get_or_insert(span); + EvaluatedOrMember::Ambiguity } else { - or_constraints.push(evaluated_constraint); + EvaluatedOrMember::Other(evaluated_constraint) + } + }); + + match combine_or(members) { + CombinedOr::True => RegionConstraint::new_true(), + CombinedOr::False => RegionConstraint::new_false(), + CombinedOr::Ambiguity => RegionConstraint::Ambiguity(ambiguity.unwrap()), + CombinedOr::Or { mut remaining, plus_ambiguity } => { + if plus_ambiguity { + remaining.push(RegionConstraint::Ambiguity(ambiguity.unwrap())); + } + RegionConstraint::Or(remaining.into_boxed_slice()) } } - - ambiguity.map_or_else( - || RegionConstraint::Or(or_constraints.into_boxed_slice()), - RegionConstraint::Ambiguity, - ) } } } diff --git a/compiler/rustc_type_ir/src/region_constraint/tests.rs b/compiler/rustc_type_ir/src/region_constraint/tests.rs index 831d98f590162..463de9cad3cb8 100644 --- a/compiler/rustc_type_ir/src/region_constraint/tests.rs +++ b/compiler/rustc_type_ir/src/region_constraint/tests.rs @@ -1,4 +1,6 @@ -use super::compute_equated_region_var_replacements_from; +use super::{ + CombinedOr, EvaluatedOrMember, combine_or, compute_equated_region_var_replacements_from, +}; #[test] fn equated_region_var_replacements_follow_transitive_region_var_chains() { @@ -27,3 +29,16 @@ fn equated_region_var_replacements_follow_transitive_region_var_chains() { assert_eq!(replacements.get(&REVAR_1), Some(&PLACEHOLDER)); assert_eq!(replacements.get(&REVAR_2), Some(&PLACEHOLDER)); } + +/// Mixed `Or(Ambiguity, remaining)` must keep the unknown sibling. A later +/// evaluation where `remaining` becomes false is unknown ∨ false = unknown, +/// not false. +#[test] +fn mixed_or_later_false_candidate_stays_ambiguous() { + let first = combine_or([EvaluatedOrMember::Ambiguity, EvaluatedOrMember::Other("cand")]); + assert_eq!(first, CombinedOr::Or { remaining: vec!["cand"], plus_ambiguity: true }); + + // Second evaluation: the deferred candidate rewrote to false. + let second = combine_or([EvaluatedOrMember::<&str>::Ambiguity, EvaluatedOrMember::False]); + assert_eq!(second, CombinedOr::Ambiguity); +} diff --git a/tests/ui/assumptions_on_binders/alias_outlives.rs b/tests/ui/assumptions_on_binders/alias_outlives.rs index 0c2ed6585cf45..46233daf8dd80 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.rs +++ b/tests/ui/assumptions_on_binders/alias_outlives.rs @@ -3,6 +3,11 @@ // test that a `::Assoc: '!a_u1` constraint is considered to be satisfied // if there's a `T::Assoc: 'static` assumption in the root universe and if not that it is // an error :) +// +// The pass case is also the `Or` evaluation test: rewrite can produce an ambiguous +// candidate next to a real one (`Assoc: 'static` at the root). Collapsing that `Or` +// to `Ambiguity` makes REGIONCK_ENV_PASS fail with E0283. Keep the remaining +// candidate, and keep the `Ambiguity` sibling until that candidate succeeds. #![feature(generic_const_items)] diff --git a/tests/ui/assumptions_on_binders/alias_outlives.stderr b/tests/ui/assumptions_on_binders/alias_outlives.stderr index 1787c1912ae4f..06d54242c9795 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.stderr +++ b/tests/ui/assumptions_on_binders/alias_outlives.stderr @@ -1,8 +1,7 @@ error: higher-ranked lifetime bound could not be satisfied - --> $DIR/alias_outlives.rs:37:45 + --> $DIR/alias_outlives.rs:42:45 | LL | const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() | ^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error - diff --git a/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs b/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs index 2f0f2ca8aab85..4fc0d5ce82daf 100644 --- a/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs +++ b/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs @@ -15,6 +15,9 @@ // } // rewritten to: true (via assumption) // rewritting to `for<'a, 'b> >::Assoc: 'c` would be wrong +// +// The `Or` evaluation fix is not enough for this one. Without +// `normalize_equated_region_vars` it goes ambiguous (E0283). trait Trait<'a, 'b> { type Assoc; From 8344a9126399d8e4fe5c9b2a5400e1575b407572 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Wed, 19 Aug 2026 22:48:54 -0300 Subject: [PATCH 14/17] Clarify normalize and reflexive outlives comments --- compiler/rustc_type_ir/src/region_constraint.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index c4af7c395216f..fd7c19be4ab46 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -485,8 +485,10 @@ pub fn eagerly_handle_placeholders_in_universe { if region_1 == region_2 { - // Reflexive constraints are always satisfied, even if the region is from `u`. + // `'r: 'r` is always true, including for current-universe regions. + // Relating a region to itself, component destructure, and normalize + // rewriting `'?x: '!a` + `'!a: '?x` into `'!a: '!a` can all produce this. return RegionConstraint::new_true(); } let region_1_u = max_universe(infcx, region_1); From 2e09b2c13326921bba3253b52c53c23548cc70de Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Wed, 19 Aug 2026 23:51:34 -0300 Subject: [PATCH 15/17] Restore alias outlives stderr blank line --- tests/ui/assumptions_on_binders/alias_outlives.stderr | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/assumptions_on_binders/alias_outlives.stderr b/tests/ui/assumptions_on_binders/alias_outlives.stderr index 06d54242c9795..3feb067a8c6f3 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.stderr +++ b/tests/ui/assumptions_on_binders/alias_outlives.stderr @@ -5,3 +5,4 @@ LL | const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() | ^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error + From d814ce18e73667c0ea3be00f810d81d876e1224c Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Wed, 19 Aug 2026 23:52:07 -0300 Subject: [PATCH 16/17] Update placeholder assumption diagnostic --- .../placeholder-assumptions-issue-157840.stderr | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr index 5e8e131addd28..3ebb61b92c5ad 100644 --- a/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr +++ b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr @@ -4,10 +4,11 @@ error[E0277]: the trait bound `(): Trait fn(>::Assoc))> LL | (): Trait<>::Assoc>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait fn(>::Assoc))>` is not implemented for `()` | -help: consider extending the `where` clause, but there might be an alternative better way to express this requirement +help: this trait has no implementations, consider adding one + --> $DIR/placeholder-assumptions-issue-157840.rs:3:1 | -LL | (): Trait<>::Assoc>, (): Trait fn(>::Assoc))> - | +++++++++++++++++++++++++++++++++++++++++++++++++ +LL | trait Trait {} + | ^^^^^^^^^^^^^^ error: aborting due to 1 previous error From 62ca92ba3c0474638da22c1815142f0b6a0826b5 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Sat, 22 Aug 2026 10:59:04 -0300 Subject: [PATCH 17/17] Adapt region constraints to current upstream APIs --- .../src/infer/outlives/obligations.rs | 17 +++++++++--- .../src/canonical/mod.rs | 6 +++-- .../rustc_type_ir/src/region_constraint.rs | 11 ++++---- .../src/relate/solver_relating.rs | 26 ++++++++++++------- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index f5e7d4bc03563..7f09d660bdb86 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -292,11 +292,20 @@ impl<'tcx> InferCtxt<'tcx> { // empty slice. A root-false constraint has nothing to register; // unsatisfied outlives are reported later by borrowck/regionck. Or(nested) => { - let had_members = !nested.is_empty(); - let concrete: Vec<_> = nested.into_iter().filter(|c| !c.is_ambig()).collect(); + let mut ambiguity = None; + let concrete: Vec<_> = nested + .into_iter() + .filter_map(|c| match c { + Ambiguity(span) => { + ambiguity.get_or_insert(span); + None + } + c => Some(c), + }) + .collect(); if concrete.is_empty() { - if had_members { - constraints.push(Ambiguity); + if let Some(span) = ambiguity { + constraints.push(Ambiguity(span)); } } else { constraints.extend(concrete); diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 8e938c6fdeef0..f71123b60e66e 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -419,10 +419,12 @@ where if self.cx().assumptions_on_binders() { if a != b { self.infcx.register_solver_region_constraint( - SolverRegionConstraint::RegionOutlives(a, b), + SolverRegionConstraint::RegionOutlives(a, b, ()), + self.span, ); self.infcx.register_solver_region_constraint( - SolverRegionConstraint::RegionOutlives(b, a), + SolverRegionConstraint::RegionOutlives(b, a, ()), + self.span, ); } } else { diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index fd7c19be4ab46..09d6e5110ff7f 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -535,9 +535,10 @@ fn normalize_equated_region_vars, I: Interner use RegionConstraint::*; match constraint { - Ambiguity | RegionOutlives(..) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { - constraint - } + Ambiguity(_) + | RegionOutlives(..) + | PlaceholderTyOutlives(..) + | AliasTyOutlivesViaEnv(..) => constraint, Or(constraints) => Or(constraints .into_iter() .map(|constraint| normalize_equated_region_vars(infcx, constraint, u)) @@ -630,13 +631,13 @@ fn collect_conjunctive_region_outlives( use RegionConstraint::*; match constraint { - RegionOutlives(r1, r2) => out.push((*r1, *r2)), + RegionOutlives(r1, r2, _) => out.push((*r1, *r2)), And(constraints) => { for constraint in constraints.iter() { collect_conjunctive_region_outlives(constraint, out); } } - Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) | Or(..) => {} + Ambiguity(_) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) | Or(..) => {} } } diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 573a5523f3750..6b752ea0a0e65 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -245,17 +245,23 @@ where unreachable!("Expected bivariance to be handled in relate_with_variance") } _ if a == b => return Ok(a), - ty::Covariant => self - .infcx - .register_solver_region_constraint(RegionConstraint::RegionOutlives(a, b)), - ty::Contravariant => self - .infcx - .register_solver_region_constraint(RegionConstraint::RegionOutlives(b, a)), + ty::Covariant => self.infcx.register_solver_region_constraint( + RegionConstraint::RegionOutlives(a, b, ()), + self.span, + ), + ty::Contravariant => self.infcx.register_solver_region_constraint( + RegionConstraint::RegionOutlives(b, a, ()), + self.span, + ), ty::Invariant => { - self.infcx - .register_solver_region_constraint(RegionConstraint::RegionOutlives(a, b)); - self.infcx - .register_solver_region_constraint(RegionConstraint::RegionOutlives(b, a)); + self.infcx.register_solver_region_constraint( + RegionConstraint::RegionOutlives(a, b, ()), + self.span, + ); + self.infcx.register_solver_region_constraint( + RegionConstraint::RegionOutlives(b, a, ()), + self.span, + ); } } } else {