Skip to content

Commit 52fc385

Browse files
compiler: Inform the solver of concurrency
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
1 parent 12b26c1 commit 52fc385

File tree

6 files changed

+72
-4
lines changed

6 files changed

+72
-4
lines changed

compiler/rustc_middle/src/ty/context.rs

+4
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
181181
}
182182
}
183183

184+
fn evaluation_is_concurrent(&self) -> bool {
185+
self.sess.threads() > 1
186+
}
187+
184188
fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {
185189
self.expand_abstract_consts(t)
186190
}

compiler/rustc_type_ir/src/interner.rs

+5
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ pub trait Interner:
137137
f: impl FnOnce(&mut search_graph::GlobalCache<Self>) -> R,
138138
) -> R;
139139

140+
fn evaluation_is_concurrent(&self) -> bool;
141+
140142
fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;
141143

142144
type GenericsOf: GenericsOf<Self>;
@@ -404,4 +406,7 @@ impl<I: Interner> search_graph::Cx for I {
404406
) -> R {
405407
I::with_global_cache(self, mode, f)
406408
}
409+
fn evaluation_is_concurrent(&self) -> bool {
410+
self.evaluation_is_concurrent()
411+
}
407412
}

compiler/rustc_type_ir/src/search_graph/global_cache.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,28 @@ impl<X: Cx> GlobalCache<X> {
4444
cx: X,
4545
input: X::Input,
4646

47-
result: X::Result,
47+
origin_result: X::Result,
4848
dep_node: X::DepNodeIndex,
4949

5050
additional_depth: usize,
5151
encountered_overflow: bool,
5252
nested_goals: NestedGoals<X>,
5353
) {
54-
let result = cx.mk_tracked(result, dep_node);
54+
let result = cx.mk_tracked(origin_result, dep_node);
5555
let entry = self.map.entry(input).or_default();
5656
if encountered_overflow {
5757
let with_overflow = WithOverflow { nested_goals, result };
5858
let prev = entry.with_overflow.insert(additional_depth, with_overflow);
59-
assert!(prev.is_none());
59+
if let Some(prev) = &prev {
60+
assert!(cx.evaluation_is_concurrent());
61+
assert_eq!(cx.get_tracked(&prev.result), origin_result);
62+
}
6063
} else {
6164
let prev = entry.success.replace(Success { additional_depth, nested_goals, result });
62-
assert!(prev.is_none());
65+
if let Some(prev) = &prev {
66+
assert!(cx.evaluation_is_concurrent());
67+
assert_eq!(cx.get_tracked(&prev.result), origin_result);
68+
}
6369
}
6470
}
6571

compiler/rustc_type_ir/src/search_graph/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ pub trait Cx: Copy {
5353
mode: SolverMode,
5454
f: impl FnOnce(&mut GlobalCache<Self>) -> R,
5555
) -> R;
56+
57+
fn evaluation_is_concurrent(&self) -> bool;
5658
}
5759

5860
pub trait Delegate {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//@ compile-flags: -Zthreads=16
2+
3+
// original issue: https://github.com/rust-lang/rust/issues/129112
4+
// Previously, the "next" solver asserted that each successful solution is only obtained once.
5+
// This test exhibits a repro that, with next-solver + -Zthreads, triggered that old assert.
6+
// In the presence of multithreaded solving, it's possible to concurrently evaluate things twice,
7+
// which leads to replacing already-solved solutions in the global solution cache!
8+
// We assume this is fine if we check to make sure they are solved the same way each time.
9+
10+
// This test only nondeterministically fails but that's okay, as it will be rerun by CI many times,
11+
// so it should almost always fail before anything is merged. As other thread tests already exist,
12+
// we already face this difficulty, probably. If we need to fix this by reducing the error margin,
13+
// we should improve compiletest.
14+
15+
#[derive(Clone, Eq)] //~ ERROR [E0277]
16+
pub struct Struct<T>(T);
17+
18+
impl<T: Clone, U> PartialEq<U> for Struct<T>
19+
where
20+
U: Into<Struct<T>> + Clone
21+
{
22+
fn eq(&self, _other: &U) -> bool {
23+
todo!()
24+
}
25+
}
26+
27+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0277]: the trait bound `T: Clone` is not satisfied
2+
--> $DIR/global-cache-and-parallel-frontend.rs:15:17
3+
|
4+
LL | #[derive(Clone, Eq)]
5+
| ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct<T>: PartialEq`
6+
|
7+
note: required for `Struct<T>` to implement `PartialEq`
8+
--> $DIR/global-cache-and-parallel-frontend.rs:18:19
9+
|
10+
LL | impl<T: Clone, U> PartialEq<U> for Struct<T>
11+
| ----- ^^^^^^^^^^^^ ^^^^^^^^^
12+
| |
13+
| unsatisfied trait bound introduced here
14+
note: required by a bound in `Eq`
15+
--> $SRC_DIR/core/src/cmp.rs:LL:COL
16+
= note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
17+
help: consider restricting type parameter `T`
18+
|
19+
LL | pub struct Struct<T: std::clone::Clone>(T);
20+
| +++++++++++++++++++
21+
22+
error: aborting due to 1 previous error
23+
24+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)