390: Add some doc on `specify` r=nikomatsakis a=XFFXFF

Add some documentation to explain why `specify` only works if the key is a tracked struct created in the current query, as per [this](https://salsa.zulipchat.com/#narrow/stream/146365-good-first-issue/topic/questions.20about.20.60specify.60/near/295416521) zulip thread.  
I also added some tests to test that "specify" shouldn't work in some cases.

Co-authored-by: XFFXFF <1247714429@qq.com>
This commit is contained in:
bors[bot] 2022-08-30 10:17:28 +00:00 committed by GitHub
commit 1cabfdb077
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 175 additions and 2 deletions

View file

@ -30,12 +30,24 @@ where
let (active_query_key, current_deps) = match runtime.active_query() {
Some(v) => v,
None => panic!("can only use `set` with an active query"),
None => panic!("can only use `specify` with an active query"),
};
// `specify` only works if the key is a tracked struct created in the current query.
//
// The reason is this. We want to ensure that the same result is reached regardless of
// the "path" that the user takes through the execution graph.
// If you permit values to be specified from other queries, you can have a situation like this:
// * Q0 creates the tracked struct T0
// * Q1 specifies the value for F(T0)
// * Q2 invokes F(T0)
// * Q3 invokes Q1 and then Q2
// * Q4 invokes Q2 and then Q1
//
// Now, if We invoke Q3 first, We get one result for Q2, but if We invoke Q4 first, We get a different value. That's no good.
let database_key_index = key.database_key_index(db);
if !runtime.is_output_of_active_query(database_key_index) {
panic!("can only use `set` on entities created during current query");
panic!("can only use `specfiy` on entities created during current query");
}
// Subtle: we treat the "input" to a set query as if it were

View file

@ -12,3 +12,4 @@ parking_lot = "0.12.1"
test-log = "0.2.11"
env_logger = "*"
trybuild = "1.0"
rustversion = "1.0"

View file

@ -0,0 +1,35 @@
//! Test that `specify` does not work if the key is a `salsa::input`
//! compilation fails
#![allow(warnings)]
#[salsa::jar(db = Db)]
struct Jar(MyInput, MyTracked, tracked_fn);
trait Db: salsa::DbWithJar<Jar> {}
#[salsa::input(jar = Jar)]
struct MyInput {
field: u32,
}
#[salsa::tracked(jar = Jar)]
struct MyTracked {
field: u32,
}
#[salsa::tracked(jar = Jar, specify)]
fn tracked_fn(db: &dyn Db, input: MyInput) -> MyTracked {
MyTracked::new(db, input.field(db) * 2)
}
#[salsa::db(Jar)]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
impl salsa::Database for Database {}
impl Db for Database {}
fn main() {}

View file

@ -0,0 +1,14 @@
error[E0277]: the trait bound `MyInput: TrackedStructInDb<dyn Db>` is not satisfied
--> tests/compile-fail/specify-does-not-work-if-the-key-is-a-salsa-input.rs:21:28
|
20 | #[salsa::tracked(jar = Jar, specify)]
| ------------------------------------- required by a bound introduced by this call
21 | fn tracked_fn(db: &dyn Db, input: MyInput) -> MyTracked {
| ^^^^^ the trait `TrackedStructInDb<dyn Db>` is not implemented for `MyInput`
|
= help: the trait `TrackedStructInDb<DB>` is implemented for `MyTracked`
note: required by a bound in `function::specify::<impl FunctionIngredient<C>>::specify_and_record`
--> $WORKSPACE/components/salsa-2022/src/function/specify.rs
|
| C::Key: TrackedStructInDb<DynDb<'db, C>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `function::specify::<impl FunctionIngredient<C>>::specify_and_record`

View file

@ -0,0 +1,35 @@
//! Test that `specify` does not work if the key is a `salsa::interned`
//! compilation fails
#![allow(warnings)]
#[salsa::jar(db = Db)]
struct Jar(MyInterned, MyTracked, tracked_fn);
trait Db: salsa::DbWithJar<Jar> {}
#[salsa::interned(jar = Jar)]
struct MyInterned {
field: u32,
}
#[salsa::tracked(jar = Jar)]
struct MyTracked {
field: u32,
}
#[salsa::tracked(jar = Jar, specify)]
fn tracked_fn(db: &dyn Db, input: MyInterned) -> MyTracked {
MyTracked::new(db, input.field(db) * 2)
}
#[salsa::db(Jar)]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
impl salsa::Database for Database {}
impl Db for Database {}
fn main() {}

View file

@ -0,0 +1,14 @@
error[E0277]: the trait bound `MyInterned: TrackedStructInDb<dyn Db>` is not satisfied
--> tests/compile-fail/specify-does-not-work-if-the-key-is-a-salsa-interned.rs:21:28
|
20 | #[salsa::tracked(jar = Jar, specify)]
| ------------------------------------- required by a bound introduced by this call
21 | fn tracked_fn(db: &dyn Db, input: MyInterned) -> MyTracked {
| ^^^^^ the trait `TrackedStructInDb<dyn Db>` is not implemented for `MyInterned`
|
= help: the trait `TrackedStructInDb<DB>` is implemented for `MyTracked`
note: required by a bound in `function::specify::<impl FunctionIngredient<C>>::specify_and_record`
--> $WORKSPACE/components/salsa-2022/src/function/specify.rs
|
| C::Key: TrackedStructInDb<DynDb<'db, C>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `function::specify::<impl FunctionIngredient<C>>::specify_and_record`

View file

@ -1,3 +1,4 @@
#[rustversion::stable]
#[test]
fn compile_fail() {
let t = trybuild::TestCases::new();

View file

@ -0,0 +1,61 @@
//! Test that `specify` only works if the key is a tracked struct created in the current query.
//! compilation succeeds but execution panics
#![allow(warnings)]
#[salsa::jar(db = Db)]
struct Jar(
MyInput,
MyTracked,
tracked_fn,
tracked_fn_extra,
tracked_struct_created_in_another_query,
);
trait Db: salsa::DbWithJar<Jar> {}
#[salsa::input(jar = Jar)]
struct MyInput {
field: u32,
}
#[salsa::tracked(jar = Jar)]
struct MyTracked {
field: u32,
}
#[salsa::tracked(jar = Jar)]
fn tracked_struct_created_in_another_query(db: &dyn Db, input: MyInput) -> MyTracked {
MyTracked::new(db, input.field(db) * 2)
}
#[salsa::tracked(jar = Jar)]
fn tracked_fn(db: &dyn Db, input: MyInput) -> MyTracked {
let t = tracked_struct_created_in_another_query(db, input);
if input.field(db) != 0 {
tracked_fn_extra::specify(db, t, 2222);
}
t
}
#[salsa::tracked(jar = Jar, specify)]
fn tracked_fn_extra(_db: &dyn Db, _input: MyTracked) -> u32 {
0
}
#[salsa::db(Jar)]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
impl salsa::Database for Database {}
impl Db for Database {}
#[test]
#[should_panic]
fn execute_when_specified() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
let tracked = tracked_fn(&db, input);
}