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 a0abc918107df..2850444a570b9 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 @@ -18,8 +18,8 @@ use rustc_type_ir::solve::{ }; use rustc_type_ir::{ self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, - OpaqueTypeKey, PredicateKind, Region, TypeFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, TypingMode, eager_resolve_vars, + OpaqueTypeKey, PredicateKind, Region, RegionVid, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, eager_resolve_vars, max_universe, }; use thin_vec::ThinVec; use tracing::{Level, debug, instrument, trace, warn}; @@ -1608,6 +1608,75 @@ where r.retain(|(outlives, _)| !outlives.is_trivial() && unique.insert(*outlives)); } + #[derive(Default)] + struct NonTrivialVars { + vars: HashSet, + } + impl TypeVisitor for NonTrivialVars + where + I: Interner, + { + type Result = (); + fn visit_ty(&mut self, t: I::Ty) { + // If a nested type doesn't have any `ReVar`s, then we won't insert + // anything into `vars` anyway, so skip for better perf. + if !t.has_infer_regions() { + return; + } + t.super_visit_with(self); + } + fn visit_const(&mut self, c: I::Const) { + // The same goes for consts. + if !c.has_infer_regions() { + return; + } + c.super_visit_with(self); + } + fn visit_region(&mut self, r: Region) { + if let ty::ReVar(vid) = r.kind() { + self.vars.insert(vid); + } + } + } + + // If we have a constraint like `'re: '?1`, where '?1 can name 're and '?1 appears + // only on the RHS of region constraints, then this kind of constraint is also trivial, + // since we're able to pick '?1 := 'empty, and 're: 'empty is always true for any 're. + if let ExternalRegionConstraints::Old(r) = &mut external_constraints.region_constraints + && !r.is_empty() + { + let mut vis = NonTrivialVars::default(); + var_values.visit_with(&mut vis); + // We have to visit each component of `external_constraints` individually here + // because we skip the RHS of outlives constraints, and `TypeVisitor` doesn't + // have a method we can easily override in order to do this. + external_constraints.opaque_types.visit_with(&mut vis); + external_constraints.normalization_nested_goals.visit_with(&mut vis); + for (constraint, _) in r.iter() { + match constraint { + ty::RegionConstraint::Outlives(ty::OutlivesClause(sup, _)) => { + sup.visit_with(&mut vis) + } + ty::RegionConstraint::Eq(eq) => eq.visit_with(&mut vis), + } + } + + r.retain(|(outlives, _)| { + if let ty::RegionConstraint::Outlives(ty::OutlivesClause(sup, re)) = *outlives + && let Some(sup_re) = sup.as_region() + && let ty::RegionKind::ReVar(vid) = re.kind() + // This is only safe if we call `eager_resolve_vars` beforehand, + // which we do. + && self.delegate.universe_of_lt(vid).unwrap() + .can_name(max_universe(&**self.delegate, sup_re)) + { + vis.vars.contains(&vid) + } else { + true + } + }); + } + let canonical = canonicalize_response( self.delegate, self.max_input_universe, diff --git a/tests/ui/traits/next-solver/no-dedup-universes.rs b/tests/ui/traits/next-solver/no-dedup-universes.rs new file mode 100644 index 0000000000000..cf54abfcb166c --- /dev/null +++ b/tests/ui/traits/next-solver/no-dedup-universes.rs @@ -0,0 +1,21 @@ +//@ compile-flags: -Znext-solver -Zno-leak-check + +//! Make sure we don't drop trivial-looking region constraints that would otherwise fail +//! leak check. + +trait Trait {} +trait Other<'a, 'b> {} + +struct Foo; +// We need this indirection because something direct like `for<'a> &'a (): 'b` gives us a +// TypeOutlives constraint, whereas we want to be testing how we handle RegionOutlives, and +// only `impl Other for Bar`'s where-clause can give us that. +impl<'b> Trait for Foo where for<'a> Bar: Other<'a, 'b> {} + +struct Bar; +impl<'a, 'b> Other<'a, 'b> for Bar where 'a: 'b {} + +fn f(_: T) {} + +fn main() { f(Foo); } +//~^ ERROR higher-ranked lifetime error diff --git a/tests/ui/traits/next-solver/no-dedup-universes.stderr b/tests/ui/traits/next-solver/no-dedup-universes.stderr new file mode 100644 index 0000000000000..f9b98fe29213b --- /dev/null +++ b/tests/ui/traits/next-solver/no-dedup-universes.stderr @@ -0,0 +1,10 @@ +error: higher-ranked lifetime error + --> $DIR/no-dedup-universes.rs:20:13 + | +LL | fn main() { f(Foo); } + | ^^^^^^ + | + = note: could not prove `Foo: Trait` + +error: aborting due to 1 previous error +