Skip to content

Commit

Permalink
compiler: Inform the solver of concurrency
Browse files Browse the repository at this point in the history
Parallel compilation of a program can cause unexpected event sequencing.
Inform the solver when this is true so it can skip invalid asserts, then
assert replaced solutions are equal if Some
  • Loading branch information
workingjubilee committed Sep 9, 2024
1 parent 12b26c1 commit 52fc385
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 4 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
}
}

fn evaluation_is_concurrent(&self) -> bool {
self.sess.threads() > 1
}

fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {
self.expand_abstract_consts(t)
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ pub trait Interner:
f: impl FnOnce(&mut search_graph::GlobalCache<Self>) -> R,
) -> R;

fn evaluation_is_concurrent(&self) -> bool;

fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;

type GenericsOf: GenericsOf<Self>;
Expand Down Expand Up @@ -404,4 +406,7 @@ impl<I: Interner> search_graph::Cx for I {
) -> R {
I::with_global_cache(self, mode, f)
}
fn evaluation_is_concurrent(&self) -> bool {
self.evaluation_is_concurrent()
}
}
14 changes: 10 additions & 4 deletions compiler/rustc_type_ir/src/search_graph/global_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,28 @@ impl<X: Cx> GlobalCache<X> {
cx: X,
input: X::Input,

result: X::Result,
origin_result: X::Result,
dep_node: X::DepNodeIndex,

additional_depth: usize,
encountered_overflow: bool,
nested_goals: NestedGoals<X>,
) {
let result = cx.mk_tracked(result, dep_node);
let result = cx.mk_tracked(origin_result, dep_node);
let entry = self.map.entry(input).or_default();
if encountered_overflow {
let with_overflow = WithOverflow { nested_goals, result };
let prev = entry.with_overflow.insert(additional_depth, with_overflow);
assert!(prev.is_none());
if let Some(prev) = &prev {
assert!(cx.evaluation_is_concurrent());
assert_eq!(cx.get_tracked(&prev.result), origin_result);
}
} else {
let prev = entry.success.replace(Success { additional_depth, nested_goals, result });
assert!(prev.is_none());
if let Some(prev) = &prev {
assert!(cx.evaluation_is_concurrent());
assert_eq!(cx.get_tracked(&prev.result), origin_result);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_type_ir/src/search_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub trait Cx: Copy {
mode: SolverMode,
f: impl FnOnce(&mut GlobalCache<Self>) -> R,
) -> R;

fn evaluation_is_concurrent(&self) -> bool;
}

pub trait Delegate {
Expand Down
27 changes: 27 additions & 0 deletions tests/ui/traits/next-solver/global-cache-and-parallel-frontend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//@ compile-flags: -Zthreads=16

// original issue: https://github.com/rust-lang/rust/issues/129112
// Previously, the "next" solver asserted that each successful solution is only obtained once.
// This test exhibits a repro that, with next-solver + -Zthreads, triggered that old assert.
// In the presence of multithreaded solving, it's possible to concurrently evaluate things twice,
// which leads to replacing already-solved solutions in the global solution cache!
// We assume this is fine if we check to make sure they are solved the same way each time.

// This test only nondeterministically fails but that's okay, as it will be rerun by CI many times,
// so it should almost always fail before anything is merged. As other thread tests already exist,
// we already face this difficulty, probably. If we need to fix this by reducing the error margin,
// we should improve compiletest.

#[derive(Clone, Eq)] //~ ERROR [E0277]
pub struct Struct<T>(T);

impl<T: Clone, U> PartialEq<U> for Struct<T>
where
U: Into<Struct<T>> + Clone
{
fn eq(&self, _other: &U) -> bool {
todo!()
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0277]: the trait bound `T: Clone` is not satisfied
--> $DIR/global-cache-and-parallel-frontend.rs:15:17
|
LL | #[derive(Clone, Eq)]
| ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct<T>: PartialEq`
|
note: required for `Struct<T>` to implement `PartialEq`
--> $DIR/global-cache-and-parallel-frontend.rs:18:19
|
LL | impl<T: Clone, U> PartialEq<U> for Struct<T>
| ----- ^^^^^^^^^^^^ ^^^^^^^^^
| |
| unsatisfied trait bound introduced here
note: required by a bound in `Eq`
--> $SRC_DIR/core/src/cmp.rs:LL:COL
= note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider restricting type parameter `T`
|
LL | pub struct Struct<T: std::clone::Clone>(T);
| +++++++++++++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.

0 comments on commit 52fc385

Please sign in to comment.