diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 626649027da28..7d508a3315217 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -461,6 +461,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 replacements = compute_equated_region_var_replacements(infcx, ®ion_outlives, u); + + if replacements.is_empty() { + constraint + } else { + constraint.fold_with(&mut EquatedRegionVarReplacer { cx: infcx.cx(), replacements }) + } + } + } +} + +fn compute_equated_region_var_replacements, I: Interner>( + infcx: &Infcx, + region_outlives: &[(I::Region, I::Region)], + u: UniverseIndex, +) -> 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( + 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) + } +} + +#[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 @@ -658,6 +808,11 @@ 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); 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)]); +} diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 96e30f06473cf..f55331974d7e2 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}; @@ -254,14 +255,44 @@ where #[instrument(skip(self), level = "trace")] fn regions(&mut self, a: I::Region, b: I::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") + if self.cx().assumptions_on_binders() { + if a == b { + return Ok(a); + } + + 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 { + // 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") + } } } 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`.