Skip to content
Draft
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
68 changes: 67 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::ops::ControlFlow;

#[cfg(feature = "nightly")]
use rustc_macros::StableHash;
use rustc_type_ir::data_structures::HashSet;
use rustc_type_ir::data_structures::{HashMap, HashSet};
use rustc_type_ir::inherent::*;
use rustc_type_ir::region_constraint::RegionConstraint;
use rustc_type_ir::relate::Relate;
Expand Down Expand Up @@ -1712,6 +1712,72 @@ where

filter_irrelevant_region_constraints(self.delegate, var_values, &mut external_constraints);

// skip duplicate type outlives constraints with the same region requirements
// leave constraints we can't compare unchanged
if let ExternalRegionConstraints::Old(constraints) =
&mut external_constraints.region_constraints
&& constraints.len() >= 10_000
{
let before = constraints.len();

let mut component_cache: HashMap<I::Ty, Option<Vec<usize>>> = Default::default();

// use numeric IDs so region sets can be sorted and compared
let mut region_ids: HashMap<Region<I>, usize> = Default::default();

let mut signatures = HashSet::default();
let mut kept = Vec::with_capacity(before);

for (constraint, vis) in std::mem::take(constraints) {
if let ty::RegionConstraint::Outlives(ty::OutlivesClause(lhs, rhs)) = constraint
&& let ty::GenericArgKind::Type(t) = lhs.kind()
&& matches!(vis, crate::solve::VisibleForLeakCheck::Unreachable)
&& matches!(rhs.kind(), ty::RePlaceholder(_))
&& !t.has_infer()
&& !t.has_escaping_bound_vars()
{
let signature = component_cache.entry(t).or_insert_with(|| {
let mut components = Default::default();
rustc_type_ir::outlives::push_outlives_components(
self.cx(),
t,
&mut components,
);

let mut ids = Vec::new();

for component in components {
match component {
rustc_type_ir::outlives::Component::Region(reg) => {
let next_id = region_ids.len();
let id = *region_ids.entry(reg).or_insert(next_id);
ids.push(id);
}
_ => return None,
}
}

ids.sort_unstable();
ids.dedup();

(!ids.is_empty()).then_some(ids)
});

if let Some(signature) = signature {
// these constraints are ignored by leak checking
// only one needs to be kept
if !signatures.insert((rhs, signature.clone())) {
continue;
}
}
}

kept.push((constraint, vis));
}

*constraints = kept;
}

let mut canonical = canonicalize_response(
self.delegate,
self.max_input_universe,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//@ revisions: valid short_x short_y short_both
//@[valid] check-pass
//@[short_x] check-fail
//@[short_y] check-fail
//@[short_both] check-fail
//@ compile-flags: -Znext-solver=globally -Zpolonius=off
//@ dont-check-compiler-stderr

// different types can have the same lifetime requirements

trait NeedsRegions<'a> {}

impl<'a, 'x, 'y> NeedsRegions<'a> for (&'x str, &'y str)
where
(&'x str, u8): 'a,
(&'x str, u16): 'a,
(&'y str, u8): 'a,
(&'y str, u16): 'a,
(&'x str, &'y str, u8): 'a,
(&'x str, &'y str, u16): 'a,
{}

trait Check {}

impl<'x, 'y> Check for (&'x str, &'y str)
where
for<'a> (&'x str, &'y str): NeedsRegions<'a>,
{}

fn require<T: Check>() {}

#[cfg(valid)]
fn check() {
require::<(&'static str, &'static str)>();
}

#[cfg(short_x)]
fn check<'short>(_: &'short str) {
require::<(&'short str, &'static str)>();
//[short_x]~^ ERROR lifetime may not live long enough
}

#[cfg(short_y)]
fn check<'short>(_: &'short str) {
require::<(&'static str, &'short str)>();
//[short_y]~^ ERROR lifetime may not live long enough
}

#[cfg(short_both)]
fn check<'short>(_: &'short str) {
require::<(&'short str, &'short str)>();
//[short_both]~^ ERROR lifetime may not live long enough
}

fn main() {}
Loading