Skip to content

Commit

Permalink
Rollup merge of #132001 - lcnr:stabilize-coherence-again, r=compiler-…
Browse files Browse the repository at this point in the history
…errors

fix coherence error for very large tuples™

see https://rust-lang.zulipchat.com/#narrow/channel/364551-t-types.2Ftrait-system-refactor/topic/diesel.20error for an in-depth explanation of this issue. We once again specialize `NormalizesTo` goals to avoid the impact of erasing their expected term.

fixes #131969

r? `@compiler-errors`
  • Loading branch information
matthiaskrgr authored Oct 21, 2024
2 parents 4aa07a7 + 919b61a commit 780a8c3
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 28 deletions.
24 changes: 17 additions & 7 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_type_ir::fold::TypeFoldable;
use rustc_type_ir::inherent::*;
use rustc_type_ir::relate::solver_relating::RelateExt;
use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner};
use tracing::{instrument, trace};
use tracing::{debug, instrument, trace};

use crate::canonicalizer::{CanonicalizeMode, Canonicalizer};
use crate::delegate::SolverDelegate;
Expand Down Expand Up @@ -165,12 +165,22 @@ where
// HACK: We bail with overflow if the response would have too many non-region
// inference variables. This tends to only happen if we encounter a lot of
// ambiguous alias types which get replaced with fresh inference variables
// during generalization. This prevents a hang in nalgebra.
let num_non_region_vars = canonical.variables.iter().filter(|c| !c.is_region()).count();
if num_non_region_vars > self.cx().recursion_limit() {
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
suggest_increasing_limit: true,
}));
// during generalization. This prevents hangs caused by an exponential blowup,
// see tests/ui/traits/next-solver/coherence-alias-hang.rs.
//
// We don't do so for `NormalizesTo` goals as we erased the expected term and
// bailing with overflow here would prevent us from detecting a type-mismatch,
// causing a coherence error in diesel, see #131969. We still bail with verflow
// when later returning from the parent AliasRelate goal.
if !self.is_normalizes_to_goal {
let num_non_region_vars =
canonical.variables.iter().filter(|c| !c.is_region() && c.is_existential()).count();
if num_non_region_vars > self.cx().recursion_limit() {
debug!(?num_non_region_vars, "too many inference variables -> overflow");
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
suggest_increasing_limit: true,
}));
}
}

Ok(canonical)
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ fn equate_impl_headers<'tcx>(
}

/// The result of [fn impl_intersection_has_impossible_obligation].
#[derive(Debug)]
enum IntersectionHasImpossibleObligations<'tcx> {
Yes,
No {
Expand Down Expand Up @@ -328,6 +329,7 @@ enum IntersectionHasImpossibleObligations<'tcx> {
/// of the two impls above to be empty.
///
/// Importantly, this works even if there isn't a `impl !Error for MyLocalType`.
#[instrument(level = "debug", skip(selcx), ret)]
fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligations: &'a [PredicateObligation<'tcx>],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//@ compile-flags: -Znext-solver
//@ check-pass

// When canonicalizing responses, we bail if there are too many inference variables.
// We previously also counted placeholders, which is incorrect.
#![recursion_limit = "8"]

fn foo<T>() {}

fn bar<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>() {
// The query response will contain 10 placeholders, which previously
// caused us to bail here.
foo::<(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)>();
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//@ check-pass

// When canonicalizing a response in the trait solver, we bail with overflow
// if there are too many non-region inference variables. Doing so in normalizes-to
// goals ends up hiding inference constraints in cases which we want to support,
// see #131969. To prevent this issue we do not check for too many inference
// variables in normalizes-to goals.
#![recursion_limit = "8"]

trait Bound {}
trait Trait {
type Assoc;
}


impl<T0, T1, T2, T3, T4, T5, T6, T7> Trait for (T0, T1, T2, T3, T4, T5, T6, T7)
where
T0: Trait,
T1: Trait,
T2: Trait,
T3: Trait,
T4: Trait,
T5: Trait,
T6: Trait,
T7: Trait,
(
T0::Assoc,
T1::Assoc,
T2::Assoc,
T3::Assoc,
T4::Assoc,
T5::Assoc,
T6::Assoc,
T7::Assoc,
): Clone,
{
type Assoc = (
T0::Assoc,
T1::Assoc,
T2::Assoc,
T3::Assoc,
T4::Assoc,
T5::Assoc,
T6::Assoc,
T7::Assoc,
);
}

trait Overlap {}
impl<T: Trait<Assoc = ()>> Overlap for T {}
impl<T0, T1, T2, T3, T4, T5, T6, T7> Overlap for (T0, T1, T2, T3, T4, T5, T6, T7) {}
fn main() {}
11 changes: 4 additions & 7 deletions tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//@ check-pass
//@ revisions: ai_current ai_next ia_current ia_next ii_current ii_next
//@[ai_next] compile-flags: -Znext-solver
//@[ia_next] compile-flags: -Znext-solver
//@[ii_next] compile-flags: -Znext-solver
//@ revisions: ai ia ii

// Regression test for nalgebra hang <https://github.com/rust-lang/rust/issues/130056>.

Expand All @@ -17,11 +14,11 @@ trait Trait {
type Assoc: ?Sized;
}
impl<T: ?Sized + Trait> Trait for W<T, T> {
#[cfg(any(ai_current, ai_next))]
#[cfg(ai)]
type Assoc = W<T::Assoc, Id<T::Assoc>>;
#[cfg(any(ia_current, ia_next))]
#[cfg(ia)]
type Assoc = W<Id<T::Assoc>, T::Assoc>;
#[cfg(any(ii_current, ii_next))]
#[cfg(ii)]
type Assoc = W<Id<T::Assoc>, Id<T::Assoc>>;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//~ ERROR overflow evaluating the requirement `Self: Trait`
//~^ ERROR overflow evaluating the requirement `Self well-formed`
// This is a non-regression test for issue #115351, where a recursion limit of 0 caused an ICE.
//@ check-pass
//@ compile-flags: -Znext-solver --crate-type=lib

// This is a non-regression test for issue #115351, where a recursion limit of 0 caused an ICE.

#![recursion_limit = "0"]
trait Trait {}
impl Trait for u32 {}

This file was deleted.

0 comments on commit 780a8c3

Please sign in to comment.