Rollup merge of #130094 - workingjubilee:concurrency-is-real, r=lcnr

Inform the solver if evaluation is concurrent

Parallel compilation of a program can cause unexpected event sequencing. Inform the solver when this is true so it can skip invalid asserts.
This commit is contained in:
Jubilee 2024-09-09 19:20:37 -07:00 committed by GitHub
commit 2859c24e64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 72 additions and 4 deletions

View File

@ -181,6 +181,10 @@ fn with_global_cache<R>(
} }
} }
fn evaluation_is_concurrent(&self) -> bool {
self.sess.threads() > 1
}
fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T { fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {
self.expand_abstract_consts(t) self.expand_abstract_consts(t)
} }

View File

@ -137,6 +137,8 @@ fn with_global_cache<R>(
f: impl FnOnce(&mut search_graph::GlobalCache<Self>) -> R, f: impl FnOnce(&mut search_graph::GlobalCache<Self>) -> R,
) -> R; ) -> R;
fn evaluation_is_concurrent(&self) -> bool;
fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T; fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;
type GenericsOf: GenericsOf<Self>; type GenericsOf: GenericsOf<Self>;
@ -404,4 +406,7 @@ fn with_global_cache<R>(
) -> R { ) -> R {
I::with_global_cache(self, mode, f) I::with_global_cache(self, mode, f)
} }
fn evaluation_is_concurrent(&self) -> bool {
self.evaluation_is_concurrent()
}
} }

View File

@ -44,22 +44,28 @@ pub(super) fn insert(
cx: X, cx: X,
input: X::Input, input: X::Input,
result: X::Result, origin_result: X::Result,
dep_node: X::DepNodeIndex, dep_node: X::DepNodeIndex,
additional_depth: usize, additional_depth: usize,
encountered_overflow: bool, encountered_overflow: bool,
nested_goals: NestedGoals<X>, 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(); let entry = self.map.entry(input).or_default();
if encountered_overflow { if encountered_overflow {
let with_overflow = WithOverflow { nested_goals, result }; let with_overflow = WithOverflow { nested_goals, result };
let prev = entry.with_overflow.insert(additional_depth, with_overflow); 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 { } else {
let prev = entry.success.replace(Success { additional_depth, nested_goals, result }); 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);
}
} }
} }

View File

@ -53,6 +53,8 @@ fn with_global_cache<R>(
mode: SolverMode, mode: SolverMode,
f: impl FnOnce(&mut GlobalCache<Self>) -> R, f: impl FnOnce(&mut GlobalCache<Self>) -> R,
) -> R; ) -> R;
fn evaluation_is_concurrent(&self) -> bool;
} }
pub trait Delegate { pub trait Delegate {

View File

@ -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() {}

View File

@ -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`.