diff --git a/.gitattributes b/.gitattributes index bd24553281a47..650dc002a59d5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ .config/ast-grep/rule-tests/__snapshots__/** linguist-generated=true +crates/turbo-tasks-macros-tests/tests/**/*.stderr linguist-generated=true crates/turbopack-tests/tests/snapshot/**/output/** linguist-generated=true crates/turborepo-lockfiles/fixtures/*.lock text eol=lf diff --git a/Cargo.lock b/Cargo.lock index dff4502909952..751f368b6e991 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10027,6 +10027,20 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "trybuild" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1e5645f2ee8025c2f1d75e1138f2dd034d74e6ba54620f3c569ba2a2a1ea06" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml 0.8.14", +] + [[package]] name = "ttf-parser" version = "0.15.2" @@ -10261,6 +10275,7 @@ name = "turbo-tasks-macros" version = "0.1.0" dependencies = [ "anyhow", + "either", "proc-macro-error", "proc-macro2", "quote", @@ -10285,6 +10300,7 @@ dependencies = [ "anyhow", "lazy_static", "tokio", + "trybuild", "turbo-tasks", "turbo-tasks-build", "turbo-tasks-memory", diff --git a/crates/turbo-tasks-macros-tests/Cargo.toml b/crates/turbo-tasks-macros-tests/Cargo.toml index f68df867b1846..5080fc64bf1b6 100644 --- a/crates/turbo-tasks-macros-tests/Cargo.toml +++ b/crates/turbo-tasks-macros-tests/Cargo.toml @@ -13,6 +13,7 @@ turbo-tasks-memory = { workspace = true } turbo-tasks-testing = { workspace = true } # TODO: turbo-tasks-testing uses a macro that requires us to depend on lazy-static. lazy_static = { workspace = true } +trybuild = { version = "1.0.97" } [build-dependencies] turbo-tasks-build = { workspace = true } diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_only_vc.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_only_vc.rs new file mode 100644 index 0000000000000..f091233b4f018 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_only_vc.rs @@ -0,0 +1,10 @@ +#![allow(dead_code)] + +use turbo_tasks::{ResolvedValue, Vc}; + +#[derive(ResolvedValue)] +struct ContainsOnlyVc { + a: Vc, +} + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_only_vc.stderr b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_only_vc.stderr new file mode 100644 index 0000000000000..32b9d09452a35 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_only_vc.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `Vc: ResolvedValue` is not satisfied + --> tests/derive_resolved_value/fail_contains_only_vc.rs:7:8 + | +7 | a: Vc, + | ^^^^^^^ the trait `ResolvedValue` is not implemented for `Vc` + | + = help: the following other types implement trait `ResolvedValue`: + &T + &mut T + () + (A, Z, Y, X, W, V, U, T) + (B, A, Z, Y, X, W, V, U, T) + (C, B, A, Z, Y, X, W, V, U, T) + (D, C, B, A, Z, Y, X, W, V, U, T) + (E, D, C, B, A, Z, Y, X, W, V, U, T) + and $N others +note: required by a bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + --> tests/derive_resolved_value/fail_contains_only_vc.rs:5:10 + | +5 | #[derive(ResolvedValue)] + | ^^^^^^^^^^^^^ required by this bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + = note: this error originates in the derive macro `ResolvedValue` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_resolved_vc_and_vc.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_resolved_vc_and_vc.rs new file mode 100644 index 0000000000000..46f2db572d90d --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_resolved_vc_and_vc.rs @@ -0,0 +1,11 @@ +#![allow(dead_code)] + +use turbo_tasks::{ResolvedValue, ResolvedVc, Vc}; + +#[derive(ResolvedValue)] +struct ContainsResolvedVcAndVc { + a: ResolvedVc, + b: Vc, +} + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_resolved_vc_and_vc.stderr b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_resolved_vc_and_vc.stderr new file mode 100644 index 0000000000000..3c6098cdc5fed --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_resolved_vc_and_vc.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `Vc: ResolvedValue` is not satisfied + --> tests/derive_resolved_value/fail_contains_resolved_vc_and_vc.rs:8:8 + | +8 | b: Vc, + | ^^^^^^^ the trait `ResolvedValue` is not implemented for `Vc` + | + = help: the following other types implement trait `ResolvedValue`: + &T + &mut T + () + (A, Z, Y, X, W, V, U, T) + (B, A, Z, Y, X, W, V, U, T) + (C, B, A, Z, Y, X, W, V, U, T) + (D, C, B, A, Z, Y, X, W, V, U, T) + (E, D, C, B, A, Z, Y, X, W, V, U, T) + and $N others +note: required by a bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + --> tests/derive_resolved_value/fail_contains_resolved_vc_and_vc.rs:5:10 + | +5 | #[derive(ResolvedValue)] + | ^^^^^^^^^^^^^ required by this bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + = note: this error originates in the derive macro `ResolvedValue` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_vc_inside_generic.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_vc_inside_generic.rs new file mode 100644 index 0000000000000..52e7e833e1ea9 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_vc_inside_generic.rs @@ -0,0 +1,10 @@ +#![allow(dead_code)] + +use turbo_tasks::{ResolvedValue, Vc}; + +#[derive(ResolvedValue)] +struct ContainsVcInsideGeneric { + a: Option; 4]>>, +} + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_vc_inside_generic.stderr b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_vc_inside_generic.stderr new file mode 100644 index 0000000000000..dc91f11ada319 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_contains_vc_inside_generic.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `Vc: ResolvedValue` is not satisfied + --> tests/derive_resolved_value/fail_contains_vc_inside_generic.rs:7:8 + | +7 | a: Option; 4]>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ResolvedValue` is not implemented for `Vc`, which is required by `Option; 4]>>: ResolvedValue` + | + = help: the following other types implement trait `ResolvedValue`: + &T + &mut T + () + (A, Z, Y, X, W, V, U, T) + (B, A, Z, Y, X, W, V, U, T) + (C, B, A, Z, Y, X, W, V, U, T) + (D, C, B, A, Z, Y, X, W, V, U, T) + (E, D, C, B, A, Z, Y, X, W, V, U, T) + and $N others + = note: required for `[Vc; 4]` to implement `ResolvedValue` + = note: 2 redundant requirements hidden + = note: required for `Option; 4]>>` to implement `ResolvedValue` +note: required by a bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + --> tests/derive_resolved_value/fail_contains_vc_inside_generic.rs:5:10 + | +5 | #[derive(ResolvedValue)] + | ^^^^^^^^^^^^^ required by this bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + = note: this error originates in the derive macro `ResolvedValue` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_enum.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_enum.rs new file mode 100644 index 0000000000000..931043a001111 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_enum.rs @@ -0,0 +1,14 @@ +#![allow(dead_code)] + +use turbo_tasks::ResolvedValue; + +struct UnresolvedValue; + +#[derive(ResolvedValue)] +enum ContainsUnresolvedValue { + Unit, + Unnamed(UnresolvedValue), + Named { a: UnresolvedValue }, +} + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_enum.stderr b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_enum.stderr new file mode 100644 index 0000000000000..31871c211e49f --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_enum.stderr @@ -0,0 +1,45 @@ +error[E0277]: the trait bound `UnresolvedValue: ResolvedValue` is not satisfied + --> tests/derive_resolved_value/fail_simple_enum.rs:10:13 + | +10 | Unnamed(UnresolvedValue), + | ^^^^^^^^^^^^^^^ the trait `ResolvedValue` is not implemented for `UnresolvedValue` + | + = help: the following other types implement trait `ResolvedValue`: + &T + &mut T + () + (A, Z, Y, X, W, V, U, T) + (B, A, Z, Y, X, W, V, U, T) + (C, B, A, Z, Y, X, W, V, U, T) + (D, C, B, A, Z, Y, X, W, V, U, T) + (E, D, C, B, A, Z, Y, X, W, V, U, T) + and $N others +note: required by a bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + --> tests/derive_resolved_value/fail_simple_enum.rs:7:10 + | +7 | #[derive(ResolvedValue)] + | ^^^^^^^^^^^^^ required by this bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + = note: this error originates in the derive macro `ResolvedValue` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `UnresolvedValue: ResolvedValue` is not satisfied + --> tests/derive_resolved_value/fail_simple_enum.rs:11:16 + | +11 | Named { a: UnresolvedValue }, + | ^^^^^^^^^^^^^^^ the trait `ResolvedValue` is not implemented for `UnresolvedValue` + | + = help: the following other types implement trait `ResolvedValue`: + &T + &mut T + () + (A, Z, Y, X, W, V, U, T) + (B, A, Z, Y, X, W, V, U, T) + (C, B, A, Z, Y, X, W, V, U, T) + (D, C, B, A, Z, Y, X, W, V, U, T) + (E, D, C, B, A, Z, Y, X, W, V, U, T) + and $N others +note: required by a bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + --> tests/derive_resolved_value/fail_simple_enum.rs:7:10 + | +7 | #[derive(ResolvedValue)] + | ^^^^^^^^^^^^^ required by this bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + = note: this error originates in the derive macro `ResolvedValue` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_named_struct.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_named_struct.rs new file mode 100644 index 0000000000000..bd138baba00b2 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_named_struct.rs @@ -0,0 +1,12 @@ +#![allow(dead_code)] + +use turbo_tasks::ResolvedValue; + +struct UnresolvedValue; + +#[derive(ResolvedValue)] +struct ContainsUnresolvedValueNamed { + a: UnresolvedValue, +} + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_named_struct.stderr b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_named_struct.stderr new file mode 100644 index 0000000000000..57e0b2cae6ce2 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_named_struct.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `UnresolvedValue: ResolvedValue` is not satisfied + --> tests/derive_resolved_value/fail_simple_named_struct.rs:9:8 + | +9 | a: UnresolvedValue, + | ^^^^^^^^^^^^^^^ the trait `ResolvedValue` is not implemented for `UnresolvedValue` + | + = help: the following other types implement trait `ResolvedValue`: + &T + &mut T + () + (A, Z, Y, X, W, V, U, T) + (B, A, Z, Y, X, W, V, U, T) + (C, B, A, Z, Y, X, W, V, U, T) + (D, C, B, A, Z, Y, X, W, V, U, T) + (E, D, C, B, A, Z, Y, X, W, V, U, T) + and $N others +note: required by a bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + --> tests/derive_resolved_value/fail_simple_named_struct.rs:7:10 + | +7 | #[derive(ResolvedValue)] + | ^^^^^^^^^^^^^ required by this bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + = note: this error originates in the derive macro `ResolvedValue` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_unnamed_struct.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_unnamed_struct.rs new file mode 100644 index 0000000000000..5549af87f1f86 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_unnamed_struct.rs @@ -0,0 +1,10 @@ +#![allow(dead_code)] + +use turbo_tasks::ResolvedValue; + +struct UnresolvedValue; + +#[derive(ResolvedValue)] +struct ContainsUnresolvedValueUnnamed(UnresolvedValue); + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_unnamed_struct.stderr b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_unnamed_struct.stderr new file mode 100644 index 0000000000000..6052e7151c56e --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_simple_unnamed_struct.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `UnresolvedValue: ResolvedValue` is not satisfied + --> tests/derive_resolved_value/fail_simple_unnamed_struct.rs:8:39 + | +8 | struct ContainsUnresolvedValueUnnamed(UnresolvedValue); + | ^^^^^^^^^^^^^^^ the trait `ResolvedValue` is not implemented for `UnresolvedValue` + | + = help: the following other types implement trait `ResolvedValue`: + &T + &mut T + () + (A, Z, Y, X, W, V, U, T) + (B, A, Z, Y, X, W, V, U, T) + (C, B, A, Z, Y, X, W, V, U, T) + (D, C, B, A, Z, Y, X, W, V, U, T) + (E, D, C, B, A, Z, Y, X, W, V, U, T) + and $N others +note: required by a bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + --> tests/derive_resolved_value/fail_simple_unnamed_struct.rs:7:10 + | +7 | #[derive(ResolvedValue)] + | ^^^^^^^^^^^^^ required by this bound in `DeriveResolvedValueAssertion::assert_impl_resolved_value` + = note: this error originates in the derive macro `ResolvedValue` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_underconstrained_generic.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_underconstrained_generic.rs new file mode 100644 index 0000000000000..1ccb4149ff1e8 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_underconstrained_generic.rs @@ -0,0 +1,10 @@ +#![allow(dead_code)] + +use turbo_tasks::ResolvedValue; + +#[derive(ResolvedValue)] +struct ContainsUnderconstrainedGeneric { + value: T, +} + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_underconstrained_generic.stderr b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_underconstrained_generic.stderr new file mode 100644 index 0000000000000..985703ae8f3fc --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/fail_underconstrained_generic.stderr @@ -0,0 +1,16 @@ +error[E0277]: the trait bound `T: ResolvedValue` is not satisfied + --> tests/derive_resolved_value/fail_underconstrained_generic.rs:7:12 + | +7 | value: T, + | ^ the trait `ResolvedValue` is not implemented for `T` + | +note: required by a bound in `DeriveResolvedValueAssertion::::assert_impl_resolved_value` + --> tests/derive_resolved_value/fail_underconstrained_generic.rs:5:10 + | +5 | #[derive(ResolvedValue)] + | ^^^^^^^^^^^^^ required by this bound in `DeriveResolvedValueAssertion::::assert_impl_resolved_value` + = note: this error originates in the derive macro `ResolvedValue` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider restricting type parameter `T` + | +6 | struct ContainsUnderconstrainedGeneric { + | ++++++++++++++++++++++++++++ diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_contains_resolved_vc.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_contains_resolved_vc.rs new file mode 100644 index 0000000000000..5c80368183097 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_contains_resolved_vc.rs @@ -0,0 +1,27 @@ +#![allow(dead_code)] + +use turbo_tasks::{ResolvedValue, ResolvedVc}; + +#[derive(ResolvedValue)] +struct ContainsResolvedVcNamedStruct { + a: ResolvedVc, +} + +#[derive(ResolvedValue)] +struct ContainsResolvedVcUnnamedStruct(ResolvedVc); + +#[derive(ResolvedValue)] +enum ContainsResolvedVcEnum { + Unit, + Unnamed(ResolvedVc), + Named { a: ResolvedVc }, +} + +#[derive(ResolvedValue)] +struct ContainsResolvedAlongWithOtherValues { + a: i32, + b: ResolvedVc, + c: (), +} + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_lifetimes.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_lifetimes.rs new file mode 100644 index 0000000000000..58bad80f3cc2b --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_lifetimes.rs @@ -0,0 +1,13 @@ +use turbo_tasks::ResolvedValue; + +#[derive(ResolvedValue)] +struct ContainsBorrowedData<'a> { + borrowed: &'a Option<&'a [&'a str]>, +} + +fn main() { + let a = ContainsBorrowedData { + borrowed: &Some(["value"].as_slice()), + }; + let _ = a.borrowed; +} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_linked_list.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_linked_list.rs new file mode 100644 index 0000000000000..f57c0e06225cb --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_linked_list.rs @@ -0,0 +1,31 @@ +use turbo_tasks::ResolvedValue; + +#[derive(ResolvedValue)] +// use an inline type constraint here +struct LinkedList { + // LinkedListNode is also a ResolvedValue + head: Option>>, +} + +#[derive(ResolvedValue)] +struct LinkedListNode +where + T: ResolvedValue, // use a where type constraint here +{ + current: T, + // A self-recursive type + next: Option>>, +} + +fn main() { + let ll = LinkedList { + head: Some(Box::new(LinkedListNode { + current: 1, + next: Some(Box::new(LinkedListNode { + current: 2, + next: None, + })), + })), + }; + let _last = ll.head.unwrap().next.unwrap().current; +} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_phantom_data.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_phantom_data.rs new file mode 100644 index 0000000000000..b6e540a5efb59 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_phantom_data.rs @@ -0,0 +1,16 @@ +#![allow(dead_code)] + +use std::marker::PhantomData; + +use turbo_tasks::{ResolvedValue, Vc}; + +struct Unresolved; + +#[derive(ResolvedValue)] +struct PhantomDataCanContainAnything( + PhantomData>, + PhantomData, + PhantomData>, +); + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_simple_enums.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_simple_enums.rs new file mode 100644 index 0000000000000..46c1ce3d9d75e --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_simple_enums.rs @@ -0,0 +1,19 @@ +#![allow(dead_code)] + +use turbo_tasks::{ResolvedValue, ResolvedVc}; + +#[derive(ResolvedValue)] +enum EnumI32 { + Unit, + Unnamed(i32), + Named { a: i32 }, +} + +#[derive(ResolvedValue)] +enum EnumResolvedVc { + Unit, + Unnamed(ResolvedVc), + Named { a: ResolvedVc }, +} + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_simple_structs.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_simple_structs.rs new file mode 100644 index 0000000000000..1d7420d65a072 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value/pass_simple_structs.rs @@ -0,0 +1,23 @@ +#![allow(dead_code)] + +use std::marker::PhantomData; + +use turbo_tasks::ResolvedValue; + +#[derive(ResolvedValue)] +struct UnitStruct; + +#[derive(ResolvedValue)] +struct ContainsSimpleValuesNamed { + a: i32, + b: String, + c: (), + d: (u8, u8, (u8, u8, u8)), + e: [u8; 8], + f: PhantomData, +} + +#[derive(ResolvedValue)] +struct ContainsSimpleValuesUnnamed(i32, String, ()); + +fn main() {} diff --git a/crates/turbo-tasks-macros-tests/tests/trybuild.rs b/crates/turbo-tasks-macros-tests/tests/trybuild.rs new file mode 100644 index 0000000000000..749369094b88e --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/trybuild.rs @@ -0,0 +1,6 @@ +#[test] +fn derive_resolved_value() { + let t = trybuild::TestCases::new(); + t.pass("tests/derive_resolved_value/pass_*.rs"); + t.compile_fail("tests/derive_resolved_value/fail_*.rs"); +} diff --git a/crates/turbo-tasks-macros/Cargo.toml b/crates/turbo-tasks-macros/Cargo.toml index 9d6509310a716..0200335e03ce2 100644 --- a/crates/turbo-tasks-macros/Cargo.toml +++ b/crates/turbo-tasks-macros/Cargo.toml @@ -14,6 +14,7 @@ workspace = true [dependencies] anyhow = { workspace = true } +either = { workspace = true } proc-macro-error = "1.0.4" proc-macro2 = { workspace = true } quote = { workspace = true } diff --git a/crates/turbo-tasks-macros/src/derive/mod.rs b/crates/turbo-tasks-macros/src/derive/mod.rs index 953fc350c8e02..d48323309096e 100644 --- a/crates/turbo-tasks-macros/src/derive/mod.rs +++ b/crates/turbo-tasks-macros/src/derive/mod.rs @@ -1,10 +1,12 @@ mod deterministic_hash_macro; +mod resolved_value_macro; mod task_input_macro; mod trace_raw_vcs_macro; mod value_debug_format_macro; mod value_debug_macro; pub use deterministic_hash_macro::derive_deterministic_hash; +pub use resolved_value_macro::derive_resolved_value; use syn::{spanned::Spanned, Attribute, Meta, MetaList, NestedMeta}; pub use task_input_macro::derive_task_input; pub use trace_raw_vcs_macro::derive_trace_raw_vcs; diff --git a/crates/turbo-tasks-macros/src/derive/resolved_value_macro.rs b/crates/turbo-tasks-macros/src/derive/resolved_value_macro.rs new file mode 100644 index 0000000000000..86ce9dca298fa --- /dev/null +++ b/crates/turbo-tasks-macros/src/derive/resolved_value_macro.rs @@ -0,0 +1,57 @@ +use either::Either; +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, quote_spanned}; +use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Generics}; + +pub fn derive_resolved_value(input: TokenStream) -> TokenStream { + let derive_input = parse_macro_input!(input as DeriveInput); + let ident = &derive_input.ident; + + let assertions = assert_fields_impl_resolved_value(&derive_input.generics, &derive_input.data); + + let (impl_generics, ty_generics, where_clause) = derive_input.generics.split_for_impl(); + quote! { + unsafe impl #impl_generics ::turbo_tasks::ResolvedValue + for #ident #ty_generics #where_clause {} + #assertions + } + .into() +} + +fn iter_data_fields(data: &Data) -> impl Iterator { + match data { + Data::Struct(ds) => Either::Left(ds.fields.iter()), + Data::Enum(de) => Either::Right(Either::Left(de.variants.iter().flat_map(|v| &v.fields))), + Data::Union(du) => Either::Right(Either::Right(du.fields.named.iter())), + } +} + +fn assert_fields_impl_resolved_value(generics: &Generics, data: &Data) -> TokenStream2 { + // this technique is based on the trick used by + // `static_assertions::assert_impl_all`, but extended to support generics. + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let field_types: Vec<_> = iter_data_fields(data).map(|field| &field.ty).collect(); + let assertion_calls = field_types.iter().map(|ty| { + quote_spanned! { + // attribute type assertion errors to the line where the field is defined + ty.span() => + // this call is only valid if ty is a ResolvedValue + Self::assert_impl_resolved_value::<#ty>(); + } + }); + quote! { + const _: fn() = || { + // create this struct just to hold onto our generics... + // we reproduce the field types here to ensure any generics get used + struct DeriveResolvedValueAssertion #impl_generics (#(#field_types),*) #where_clause; + + impl #impl_generics DeriveResolvedValueAssertion #ty_generics #where_clause { + fn assert_impl_resolved_value() {} + fn field_types() { + #(#assertion_calls)* + } + } + }; + } +} diff --git a/crates/turbo-tasks-macros/src/lib.rs b/crates/turbo-tasks-macros/src/lib.rs index f9977e5afd1c4..c1ced476b8e28 100644 --- a/crates/turbo-tasks-macros/src/lib.rs +++ b/crates/turbo-tasks-macros/src/lib.rs @@ -22,6 +22,11 @@ pub fn derive_trace_raw_vcs_attr(input: TokenStream) -> TokenStream { derive::derive_trace_raw_vcs(input) } +#[proc_macro_derive(ResolvedValue, attributes(turbo_tasks))] +pub fn derive_resolved_value_attr(input: TokenStream) -> TokenStream { + derive::derive_resolved_value(input) +} + #[proc_macro_derive(ValueDebug, attributes(turbo_tasks))] pub fn derive_value_debug_attr(input: TokenStream) -> TokenStream { derive::derive_value_debug(input) diff --git a/crates/turbo-tasks/src/vc/resolved.rs b/crates/turbo-tasks/src/vc/resolved.rs index ec29ec4e0bec4..7a80758d48640 100644 --- a/crates/turbo-tasks/src/vc/resolved.rs +++ b/crates/turbo-tasks/src/vc/resolved.rs @@ -128,3 +128,5 @@ unsafe impl ResolvedValue for PhantomData {} unsafe impl ResolvedValue for &T {} unsafe impl ResolvedValue for &mut T {} + +pub use turbo_tasks_macros::ResolvedValue;