From f0f37c69449a52613e52ba052cb28cac696f869b Mon Sep 17 00:00:00 2001 From: Noa Date: Fri, 3 May 2024 17:25:54 -0500 Subject: [PATCH] Improve diagnostics and add ui tests --- Cargo.lock | 25 ++ Cargo.toml | 1 + crates/bindings-macro/src/lib.rs | 132 +++++--- crates/bindings-macro/src/module.rs | 4 +- crates/bindings/Cargo.toml | 1 + crates/bindings/src/rt.rs | 34 +- crates/bindings/tests/ui.rs | 5 + crates/bindings/tests/ui/reducers.rs | 30 ++ crates/bindings/tests/ui/reducers.stderr | 408 +++++++++++++++++++++++ crates/bindings/tests/ui/tables.rs | 18 + crates/bindings/tests/ui/tables.stderr | 122 +++++++ crates/sats/src/typespace.rs | 2 + 12 files changed, 716 insertions(+), 66 deletions(-) create mode 100644 crates/bindings/tests/ui.rs create mode 100644 crates/bindings/tests/ui/reducers.rs create mode 100644 crates/bindings/tests/ui/reducers.stderr create mode 100644 crates/bindings/tests/ui/tables.rs create mode 100644 crates/bindings/tests/ui/tables.stderr diff --git a/Cargo.lock b/Cargo.lock index 9da393fe0e7..2f7ecde42cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,6 +314,15 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +[[package]] +name = "basic-toml" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bfc506e7a2370ec239e1d072507b2a80c833083699d3c6fa176fbb4de8448c6" +dependencies = [ + "serde", +] + [[package]] name = "benchmarks-module" version = "0.1.0" @@ -4293,6 +4302,7 @@ dependencies = [ "spacetimedb-bindings-sys", "spacetimedb-lib", "spacetimedb-primitives", + "trybuild", ] [[package]] @@ -5585,6 +5595,21 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "trybuild" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419ecd263363827c5730386f418715766f584e2f874d32c23c5b00bd9727e7e" +dependencies = [ + "basic-toml", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", +] + [[package]] name = "tungstenite" version = "0.21.0" diff --git a/Cargo.toml b/Cargo.toml index 448c7eed2fd..d0dc8aafad4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -224,6 +224,7 @@ tracing-core = "0.1.31" tracing-flame = "0.2.0" tracing-log = "0.1.3" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +trybuild = "1" typed-arena = "2.0" url = "2.3.1" urlencoding = "2.1.2" diff --git a/crates/bindings-macro/src/lib.rs b/crates/bindings-macro/src/lib.rs index b3a634561c7..d6e6d995906 100644 --- a/crates/bindings-macro/src/lib.rs +++ b/crates/bindings-macro/src/lib.rs @@ -16,7 +16,6 @@ use module::{derive_deserialize, derive_satstype, derive_serialize}; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, TokenStreamExt}; use spacetimedb_primitives::ColumnAttribute; -use std::collections::HashMap; use std::time::Duration; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; @@ -294,6 +293,24 @@ fn gen_reducer(original_function: ItemFn, reducer_name: &str, extra: ReducerExtr let func_name = &original_function.sig.ident; let vis = &original_function.vis; + for param in &original_function.sig.generics.params { + match param { + syn::GenericParam::Lifetime(_) => {} + syn::GenericParam::Type(_) => { + return Err(syn::Error::new_spanned( + param, + "type parameters are not allowed on reducers", + )) + } + syn::GenericParam::Const(_) => { + return Err(syn::Error::new_spanned( + param, + "const parameters are not allowed on reducers", + )) + } + } + } + // let errmsg = "reducer should have at least 2 arguments: (identity: Identity, timestamp: u64, ...)"; // let ([arg1, arg2], args) = validate_reducer_args(&original_function.sig, errmsg)?; @@ -348,6 +365,9 @@ fn gen_reducer(original_function: ItemFn, reducer_name: &str, extra: ReducerExtr let mut extra_impls = TokenStream::new(); + let lt_params = &original_function.sig.generics; + let lt_where_clause = <_params.where_clause; + if !matches!(extra, ReducerExtra::None) { let arg_names = typed_args .iter() @@ -359,31 +379,12 @@ fn gen_reducer(original_function: ItemFn, reducer_name: &str, extra: ReducerExtr .collect::>(); extra_impls.extend(quote!(impl #func_name { - pub fn schedule(__time: spacetimedb::Timestamp #(, #arg_names: #arg_tys)*) -> spacetimedb::ScheduleToken<#func_name> { - spacetimedb::rt::schedule(__time, (#(#arg_names,)*)) + pub fn schedule #lt_params (__time: spacetimedb::Timestamp #(, #arg_names: #arg_tys)*) -> spacetimedb::ScheduleToken<#func_name> #lt_where_clause { + spacetimedb::rt::schedule(__time, #func_name, (#(#arg_names,)*)) } })); } - let generated_function = quote! { - fn __reducer( - __sender: spacetimedb::sys::Buffer, - __caller_address: spacetimedb::sys::Buffer, - __timestamp: u64, - __args: &[u8] - ) -> spacetimedb::sys::Buffer { - #(spacetimedb::rt::assert_reducer_arg::<#arg_tys>();)* - #(spacetimedb::rt::assert_reducer_ret::<#ret_ty>();)* - spacetimedb::rt::invoke_reducer( - #func_name, - __sender, - __caller_address, - __timestamp, - __args, - ) - } - }; - let generated_describe_function = quote! { #[export_name = #register_describer_symbol] pub extern "C" fn __register_describer() { @@ -397,13 +398,33 @@ fn gen_reducer(original_function: ItemFn, reducer_name: &str, extra: ReducerExtr }; #[allow(non_camel_case_types)] #vis struct #func_name { _never: ::core::convert::Infallible } + const _: () = { + fn _assert_args #lt_params () #lt_where_clause { + #(let _ = <#arg_tys as spacetimedb::rt::ReducerArg>::_ITEM;)* + #(let _ = <#ret_ty as spacetimedb::rt::ReducerResult>::into_result;)* + } + }; + impl #func_name { + fn invoke( + __sender: spacetimedb::sys::Buffer, + __caller_address: spacetimedb::sys::Buffer, + __timestamp: u64, + __args: &[u8] + ) -> spacetimedb::sys::Buffer { + spacetimedb::rt::invoke_reducer( + #func_name, + __sender, + __caller_address, + __timestamp, + __args, + ) + } + } + #[automatically_derived] impl spacetimedb::rt::ReducerInfo for #func_name { const NAME: &'static str = #reducer_name; const ARG_NAMES: &'static [Option<&'static str>] = &[#(#opt_arg_names),*]; - const INVOKE: spacetimedb::rt::ReducerFn = { - #generated_function - __reducer - }; + const INVOKE: spacetimedb::rt::ReducerFn = #func_name::invoke; } #extra_impls #original_function @@ -492,6 +513,24 @@ fn spacetimedb_tabletype_impl(item: syn::DeriveInput) -> syn::Result {} + syn::GenericParam::Type(_) => { + return Err(syn::Error::new_spanned( + param, + "type parameters are not allowed on tables", + )) + } + syn::GenericParam::Const(_) => { + return Err(syn::Error::new_spanned( + param, + "const parameters are not allowed on tables", + )) + } + } + } + let mut columns = Vec::::new(); let get_table_id_func = quote! { @@ -737,39 +776,24 @@ fn spacetimedb_tabletype_impl(item: syn::DeriveInput) -> syn::Result>() - .into_iter() - .map(|(ty, span)| quote_spanned!(span=> impl #trait_ident for #ty {})); - - quote_spanned! {item.span()=> - trait #trait_ident: spacetimedb::SpacetimeType {} - #(#field_impls)* - } - }; - // Output all macro data let emission = quote! { const _: () = { #describe_table_func }; - const _: () = { - #assert_fields_are_spacetimetypes - }; - impl #original_struct_ident { + // Attempt to improve the compile error when a table field doesn't satisfy + // the supertraits of `TableType`. We make it so the field span indicates + // which fields are offenders, and error reporting stops if the field doesn't + // implement `SpacetimeType` (which happens to be the derive macro one is + // supposed to use). That is, the user doesn't see errors about `Serialize`, + // `Deserialize` not being satisfied, which they wouldn't know what to do + // about. + const ___ASSERT_SPACETIMETYPE: () = { + #(spacetimedb::rt::assert_spacetimetype::<#field_types>();)* + }; + #db_insert #(#unique_filter_funcs)* #(#unique_update_funcs)* @@ -811,7 +835,9 @@ fn spacetimedb_index( let output = quote! { #original_struct - const _: () = spacetimedb::rt::assert_table::<#original_struct_name>(); + const _: () = { + let _ = <#original_struct_name as spacetimedb::TableType>::TABLE_NAME; + }; }; if std::env::var("PROC_MACRO_DEBUG").is_ok() { diff --git a/crates/bindings-macro/src/module.rs b/crates/bindings-macro/src/module.rs index a86d1fe9215..d745d94660f 100644 --- a/crates/bindings-macro/src/module.rs +++ b/crates/bindings-macro/src/module.rs @@ -29,7 +29,6 @@ pub(crate) struct SatsField<'a> { pub name: Option, pub ty: &'a syn::Type, pub original_attrs: &'a [syn::Attribute], - pub span: Span, } pub(crate) struct SatsVariant<'a> { @@ -53,7 +52,6 @@ pub(crate) fn sats_type_from_derive( name: field.ident.as_ref().map(syn::Ident::to_string), ty: &field.ty, original_attrs: &field.attrs, - span: field.span(), }); SatsTypeData::Product(fields.collect()) } @@ -198,7 +196,7 @@ pub(crate) fn derive_deserialize(ty: &SatsType<'_>) -> TokenStream { let spacetimedb_lib = &ty.krate; let (impl_generics, ty_generics, where_clause) = ty.generics.split_for_impl(); - let mut de_generics = ty.generics.clone(); + let mut de_generics = ty.generics.clone(); let de_lifetime = syn::Lifetime::new("'de", Span::call_site()); for lp in de_generics.lifetimes_mut() { lp.bounds.push(de_lifetime.clone()); diff --git a/crates/bindings/Cargo.toml b/crates/bindings/Cargo.toml index 36e45be8527..f89e0ce2e03 100644 --- a/crates/bindings/Cargo.toml +++ b/crates/bindings/Cargo.toml @@ -29,3 +29,4 @@ scoped-tls.workspace = true [dev-dependencies] rand.workspace = true bytes.workspace = true +trybuild.workspace = true diff --git a/crates/bindings/src/rt.rs b/crates/bindings/src/rt.rs index 13474b16434..ca1d1733a8e 100644 --- a/crates/bindings/src/rt.rs +++ b/crates/bindings/src/rt.rs @@ -106,6 +106,14 @@ fn cvt_result(res: Result<(), Box>) -> Buffer { /// A trait for types representing the *execution logic* of a reducer. /// /// The type parameter `T` is used for determining whether there is a context argument. +#[diagnostic::on_unimplemented( + message = "invalid reducer signature", + label = "this reducer signature is not valid; parameters and return type must implement `SpacetimeType`", + note = "", + note = "reducer signatures must match the following pattern:", + note = " Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>]", + note = "" +)] pub trait Reducer<'de, A: Args<'de>, T> { fn invoke(&self, ctx: ReducerContext, args: A) -> Result<(), Box>; } @@ -179,15 +187,17 @@ impl ReducerResult for Result<(), E> { } /// A trait of types that can be an argument of a reducer. -pub trait ReducerArg<'de> {} -impl<'de, T: Deserialize<'de>> ReducerArg<'de> for T {} -impl ReducerArg<'_> for ReducerContext {} -/// Assert that `T: ReducerArg`. -pub fn assert_reducer_arg<'de, T: ReducerArg<'de>>() {} -/// Assert that `T: ReducerResult`. -pub fn assert_reducer_ret() {} -/// Assert that `T: TableType`. -pub const fn assert_table() {} +#[diagnostic::on_unimplemented(message = "the reducer argument `{Self}` does not implement `SpacetimeType`")] +pub trait ReducerArg { + // a little hack used in the macro to make error messages nicer. it generates ::_ITEM + #[doc(hidden)] + const _ITEM: () = (); +} +impl ReducerArg for T {} +impl ReducerArg for ReducerContext {} + +// the macro generates ::make_type:: +pub const fn assert_spacetimetype() {} /// Used in the last type parameter of `Reducer` to indicate that the /// context argument *should* be passed to the reducer logic. @@ -339,7 +349,11 @@ pub fn schedule_in(duration: Duration) -> Timestamp { /// Schedule reducer `R` to be executed async at `time`stamp with arguments `args`. /// /// Returns a token for the schedule that can be used to cancel the schedule. -pub fn schedule<'de, R: ReducerInfo>(time: Timestamp, args: impl ScheduleArgs<'de>) -> ScheduleToken { +pub fn schedule<'de, R: ReducerInfo, A: Args<'de>, T>( + time: Timestamp, + _: impl Reducer<'de, A, T>, + args: impl ScheduleArgs<'de, Args = A>, +) -> ScheduleToken { // bsatn serialize the arguments into a vector. let arg_bytes = bsatn::to_vec(&SerDeArgs(args.into_args())).unwrap(); diff --git a/crates/bindings/tests/ui.rs b/crates/bindings/tests/ui.rs new file mode 100644 index 00000000000..870c2f95ec1 --- /dev/null +++ b/crates/bindings/tests/ui.rs @@ -0,0 +1,5 @@ +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/crates/bindings/tests/ui/reducers.rs b/crates/bindings/tests/ui/reducers.rs new file mode 100644 index 00000000000..0ecedc800a8 --- /dev/null +++ b/crates/bindings/tests/ui/reducers.rs @@ -0,0 +1,30 @@ +use spacetimedb::*; + +struct Test; + +#[spacetimedb(reducer)] +fn bad_type(_a: Test) {} + +#[spacetimedb(reducer)] +fn bad_return_type() -> Test { + Test +} + +#[spacetimedb(reducer)] +fn bad_type_ctx(_ctx: ReducerContext, _a: Test) {} + +#[spacetimedb(reducer)] +fn bad_return_type_ctx(_ctx: ReducerContext) -> Test { + Test +} + +#[spacetimedb(reducer)] +fn lifetime<'a>(_a: &'a str) {} + +#[spacetimedb(reducer)] +fn type_param() {} + +#[spacetimedb(reducer)] +fn const_param() {} + +fn main() {} diff --git a/crates/bindings/tests/ui/reducers.stderr b/crates/bindings/tests/ui/reducers.stderr new file mode 100644 index 00000000000..8d150335121 --- /dev/null +++ b/crates/bindings/tests/ui/reducers.stderr @@ -0,0 +1,408 @@ +error: type parameters are not allowed on reducers + --> tests/ui/reducers.rs:25:15 + | +25 | fn type_param() {} + | ^ + +error: const parameters are not allowed on reducers + --> tests/ui/reducers.rs:28:16 + | +28 | fn const_param() {} + | ^^^^^^^^^^^ + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:6:4 + | +5 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +6 | fn bad_type(_a: Test) {} + | ^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn(Test) {bad_type}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `register_reducer` + --> src/rt.rs + | + | pub fn register_reducer<'a, A: Args<'a>, T, I: ReducerInfo>(_: impl Reducer<'a, A, T>) { + | ^^^^^^^^^^^^^^^^^ required by this bound in `register_reducer` + +error[E0277]: the reducer argument `Test` does not implement `SpacetimeType` + --> tests/ui/reducers.rs:6:17 + | +6 | fn bad_type(_a: Test) {} + | ^^^^ the trait `SpacetimeType` is not implemented for `Test`, which is required by `Test: ReducerArg` + | + = help: the trait `ReducerArg` is implemented for `spacetimedb::ReducerContext` + = note: required for `Test` to implement `ReducerArg` + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:6:4 + | +5 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +6 | fn bad_type(_a: Test) {} + | ^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn(Test) {bad_type}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `invoke_reducer` + --> src/rt.rs + | + | pub fn invoke_reducer<'a, A: Args<'a>, T>( + | -------------- required by a bound in this function + | reducer: impl Reducer<'a, A, T>, + | ^^^^^^^^^^^^^^^^^ required by this bound in `invoke_reducer` + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:6:4 + | +5 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +6 | fn bad_type(_a: Test) {} + | ^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn(Test) {bad_type}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `spacetimedb::rt::schedule` + --> src/rt.rs + | + | pub fn schedule<'de, R: ReducerInfo, A: Args<'de>, T>( + | -------- required by a bound in this function + | time: Timestamp, + | _: impl Reducer<'de, A, T>, + | ^^^^^^^^^^^^^^^^^^ required by this bound in `schedule` + +error[E0277]: the trait bound `Test: SpacetimeType` is not satisfied + --> tests/ui/reducers.rs:5:1 + | +5 | #[spacetimedb(reducer)] + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `SpacetimeType` is not implemented for `Test`, which is required by `(Test,): ScheduleArgs<'_>` + | + = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition + = help: the following other types implement trait `SpacetimeType`: + bool + i8 + i16 + i32 + i64 + i128 + u8 + u16 + and $N others + = note: required for `(Test,)` to implement `spacetimedb::rt::Args<'_>` + = note: required for `(Test,)` to implement `ScheduleArgs<'_>` +note: required by a bound in `spacetimedb::rt::schedule` + --> src/rt.rs + | + | pub fn schedule<'de, R: ReducerInfo, A: Args<'de>, T>( + | -------- required by a bound in this function +... + | args: impl ScheduleArgs<'de, Args = A>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `schedule` + = note: this error originates in the attribute macro `spacetimedb` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Test: Deserialize<'_>` is not satisfied + --> tests/ui/reducers.rs:5:1 + | +5 | #[spacetimedb(reducer)] + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `Test`, which is required by `(Test,): ScheduleArgs<'_>` + | + = help: the following other types implement trait `Deserialize<'de>`: + bool + i8 + i16 + i32 + i64 + i128 + u8 + u16 + and $N others + = note: required for `(Test,)` to implement `spacetimedb::rt::Args<'_>` + = note: required for `(Test,)` to implement `ScheduleArgs<'_>` +note: required by a bound in `spacetimedb::rt::schedule` + --> src/rt.rs + | + | pub fn schedule<'de, R: ReducerInfo, A: Args<'de>, T>( + | -------- required by a bound in this function +... + | args: impl ScheduleArgs<'de, Args = A>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `schedule` + = note: this error originates in the attribute macro `spacetimedb` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Test: Serialize` is not satisfied + --> tests/ui/reducers.rs:5:1 + | +5 | #[spacetimedb(reducer)] + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `Test`, which is required by `(Test,): ScheduleArgs<'_>` + | + = help: the following other types implement trait `Serialize`: + bool + i8 + i16 + i32 + i64 + i128 + u8 + u16 + and $N others + = note: required for `(Test,)` to implement `spacetimedb::rt::Args<'_>` + = note: required for `(Test,)` to implement `ScheduleArgs<'_>` +note: required by a bound in `spacetimedb::rt::schedule` + --> src/rt.rs + | + | pub fn schedule<'de, R: ReducerInfo, A: Args<'de>, T>( + | -------- required by a bound in this function +... + | args: impl ScheduleArgs<'de, Args = A>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `schedule` + = note: this error originates in the attribute macro `spacetimedb` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:9:4 + | +8 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +9 | fn bad_return_type() -> Test { + | ^^^^^^^^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn() -> Test {bad_return_type}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `register_reducer` + --> src/rt.rs + | + | pub fn register_reducer<'a, A: Args<'a>, T, I: ReducerInfo>(_: impl Reducer<'a, A, T>) { + | ^^^^^^^^^^^^^^^^^ required by this bound in `register_reducer` + +error[E0277]: the trait bound `Test: ReducerResult` is not satisfied + --> tests/ui/reducers.rs:9:25 + | +9 | fn bad_return_type() -> Test { + | ^^^^ the trait `ReducerResult` is not implemented for `Test` + | + = help: the following other types implement trait `ReducerResult`: + Result<(), E> + () + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:9:4 + | +8 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +9 | fn bad_return_type() -> Test { + | ^^^^^^^^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn() -> Test {bad_return_type}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `invoke_reducer` + --> src/rt.rs + | + | pub fn invoke_reducer<'a, A: Args<'a>, T>( + | -------------- required by a bound in this function + | reducer: impl Reducer<'a, A, T>, + | ^^^^^^^^^^^^^^^^^ required by this bound in `invoke_reducer` + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:9:4 + | +8 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +9 | fn bad_return_type() -> Test { + | ^^^^^^^^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn() -> Test {bad_return_type}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `spacetimedb::rt::schedule` + --> src/rt.rs + | + | pub fn schedule<'de, R: ReducerInfo, A: Args<'de>, T>( + | -------- required by a bound in this function + | time: Timestamp, + | _: impl Reducer<'de, A, T>, + | ^^^^^^^^^^^^^^^^^^ required by this bound in `schedule` + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:14:4 + | +13 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +14 | fn bad_type_ctx(_ctx: ReducerContext, _a: Test) {} + | ^^^^^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn(spacetimedb::ReducerContext, Test) {bad_type_ctx}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `register_reducer` + --> src/rt.rs + | + | pub fn register_reducer<'a, A: Args<'a>, T, I: ReducerInfo>(_: impl Reducer<'a, A, T>) { + | ^^^^^^^^^^^^^^^^^ required by this bound in `register_reducer` + +error[E0277]: the reducer argument `Test` does not implement `SpacetimeType` + --> tests/ui/reducers.rs:14:43 + | +14 | fn bad_type_ctx(_ctx: ReducerContext, _a: Test) {} + | ^^^^ the trait `SpacetimeType` is not implemented for `Test`, which is required by `Test: ReducerArg` + | + = help: the trait `ReducerArg` is implemented for `spacetimedb::ReducerContext` + = note: required for `Test` to implement `ReducerArg` + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:14:4 + | +13 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +14 | fn bad_type_ctx(_ctx: ReducerContext, _a: Test) {} + | ^^^^^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn(spacetimedb::ReducerContext, Test) {bad_type_ctx}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `invoke_reducer` + --> src/rt.rs + | + | pub fn invoke_reducer<'a, A: Args<'a>, T>( + | -------------- required by a bound in this function + | reducer: impl Reducer<'a, A, T>, + | ^^^^^^^^^^^^^^^^^ required by this bound in `invoke_reducer` + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:14:4 + | +13 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +14 | fn bad_type_ctx(_ctx: ReducerContext, _a: Test) {} + | ^^^^^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn(spacetimedb::ReducerContext, Test) {bad_type_ctx}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `spacetimedb::rt::schedule` + --> src/rt.rs + | + | pub fn schedule<'de, R: ReducerInfo, A: Args<'de>, T>( + | -------- required by a bound in this function + | time: Timestamp, + | _: impl Reducer<'de, A, T>, + | ^^^^^^^^^^^^^^^^^^ required by this bound in `schedule` + +error[E0277]: the trait bound `(spacetimedb::ReducerContext, Test): ScheduleArgs<'_>` is not satisfied + --> tests/ui/reducers.rs:13:1 + | +13 | #[spacetimedb(reducer)] + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `ScheduleArgs<'_>` is not implemented for `(spacetimedb::ReducerContext, Test)` + | + = help: the following other types implement trait `ScheduleArgs<'de>`: + (spacetimedb::ReducerContext,) + (spacetimedb::ReducerContext, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF) + (spacetimedb::ReducerContext, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF) + (spacetimedb::ReducerContext, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF) + (spacetimedb::ReducerContext, X, Y, Z, AA, AB, AC, AD, AE, AF) + (spacetimedb::ReducerContext, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF) + (spacetimedb::ReducerContext, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF) + (spacetimedb::ReducerContext, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF) + and $N others +note: required by a bound in `spacetimedb::rt::schedule` + --> src/rt.rs + | + | pub fn schedule<'de, R: ReducerInfo, A: Args<'de>, T>( + | -------- required by a bound in this function +... + | args: impl ScheduleArgs<'de, Args = A>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `schedule` + = note: this error originates in the attribute macro `spacetimedb` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:17:4 + | +16 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +17 | fn bad_return_type_ctx(_ctx: ReducerContext) -> Test { + | ^^^^^^^^^^^^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn(spacetimedb::ReducerContext) -> Test {bad_return_type_ctx}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `register_reducer` + --> src/rt.rs + | + | pub fn register_reducer<'a, A: Args<'a>, T, I: ReducerInfo>(_: impl Reducer<'a, A, T>) { + | ^^^^^^^^^^^^^^^^^ required by this bound in `register_reducer` + +error[E0277]: the trait bound `Test: ReducerResult` is not satisfied + --> tests/ui/reducers.rs:17:49 + | +17 | fn bad_return_type_ctx(_ctx: ReducerContext) -> Test { + | ^^^^ the trait `ReducerResult` is not implemented for `Test` + | + = help: the following other types implement trait `ReducerResult`: + Result<(), E> + () + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:17:4 + | +16 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +17 | fn bad_return_type_ctx(_ctx: ReducerContext) -> Test { + | ^^^^^^^^^^^^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn(spacetimedb::ReducerContext) -> Test {bad_return_type_ctx}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `invoke_reducer` + --> src/rt.rs + | + | pub fn invoke_reducer<'a, A: Args<'a>, T>( + | -------------- required by a bound in this function + | reducer: impl Reducer<'a, A, T>, + | ^^^^^^^^^^^^^^^^^ required by this bound in `invoke_reducer` + +error[E0277]: invalid reducer signature + --> tests/ui/reducers.rs:17:4 + | +16 | #[spacetimedb(reducer)] + | ----------------------- required by a bound introduced by this call +17 | fn bad_return_type_ctx(_ctx: ReducerContext) -> Test { + | ^^^^^^^^^^^^^^^^^^^ this reducer signature is not valid; parameters and return type must implement `SpacetimeType` + | + = help: the trait `Reducer<'_, _, _>` is not implemented for fn item `fn(spacetimedb::ReducerContext) -> Test {bad_return_type_ctx}` + = note: + = note: reducer signatures must match the following pattern: + = note: Fn([ReducerContext,] [T where T: SpacetimeType, ...]) [-> Result<(), impl Display>] + = note: +note: required by a bound in `spacetimedb::rt::schedule` + --> src/rt.rs + | + | pub fn schedule<'de, R: ReducerInfo, A: Args<'de>, T>( + | -------- required by a bound in this function + | time: Timestamp, + | _: impl Reducer<'de, A, T>, + | ^^^^^^^^^^^^^^^^^^ required by this bound in `schedule` diff --git a/crates/bindings/tests/ui/tables.rs b/crates/bindings/tests/ui/tables.rs new file mode 100644 index 00000000000..5552c2e6094 --- /dev/null +++ b/crates/bindings/tests/ui/tables.rs @@ -0,0 +1,18 @@ +use spacetimedb::*; + +struct Test; + +#[spacetimedb(table)] +struct Table { + x: Test, +} + +#[spacetimedb(table)] +struct TypeParam { + t: T, +} + +#[spacetimedb(table)] +struct ConstParam {} + +fn main() {} diff --git a/crates/bindings/tests/ui/tables.stderr b/crates/bindings/tests/ui/tables.stderr new file mode 100644 index 00000000000..650f58ea105 --- /dev/null +++ b/crates/bindings/tests/ui/tables.stderr @@ -0,0 +1,122 @@ +error: type parameters are not allowed on tables + --> tests/ui/tables.rs:11:18 + | +11 | struct TypeParam { + | ^ + +error: const parameters are not allowed on tables + --> tests/ui/tables.rs:16:19 + | +16 | struct ConstParam {} + | ^^^^^^^^^^^ + +error[E0277]: the trait bound `Test: SpacetimeType` is not satisfied + --> tests/ui/tables.rs:7:8 + | +7 | x: Test, + | ^^^^ the trait `SpacetimeType` is not implemented for `Test` + | + = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition + = help: the following other types implement trait `SpacetimeType`: + bool + i8 + i16 + i32 + i64 + i128 + u8 + u16 + and $N others +note: required by a bound in `assert_spacetimetype` + --> src/rt.rs + | + | pub const fn assert_spacetimetype() {} + | ^^^^^^^^^^^^^ required by this bound in `assert_spacetimetype` + +error[E0277]: the trait bound `Test: SpacetimeType` is not satisfied + --> tests/ui/tables.rs:7:8 + | +7 | x: Test, + | ^^^^ the trait `SpacetimeType` is not implemented for `Test` + | + = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition + = help: the following other types implement trait `SpacetimeType`: + bool + i8 + i16 + i32 + i64 + i128 + u8 + u16 + and $N others + +error[E0277]: the trait bound `Test: Deserialize<'de>` is not satisfied + --> tests/ui/tables.rs:7:8 + | +5 | #[spacetimedb(table)] + | --------------------- required by a bound introduced by this call +6 | struct Table { +7 | x: Test, + | ^^^^ the trait `Deserialize<'de>` is not implemented for `Test` + | + = help: the following other types implement trait `Deserialize<'de>`: + bool + i8 + i16 + i32 + i64 + i128 + u8 + u16 + and $N others +note: required by a bound in `spacetimedb::spacetimedb_lib::de::SeqProductAccess::next_element` + --> $WORKSPACE/crates/sats/src/de.rs + | + | fn next_element>(&mut self) -> Result, Self::Error> { + | ^^^^^^^^^^^^^^^^ required by this bound in `SeqProductAccess::next_element` + +error[E0277]: the trait bound `Test: Deserialize<'_>` is not satisfied + --> tests/ui/tables.rs:5:1 + | +5 | #[spacetimedb(table)] + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `Test` + | + = help: the following other types implement trait `Deserialize<'de>`: + bool + i8 + i16 + i32 + i64 + i128 + u8 + u16 + and $N others +note: required by a bound in `get_field_value` + --> $WORKSPACE/crates/sats/src/de.rs + | + | fn get_field_value>(&mut self) -> Result { + | ^^^^^^^^^^^^^^^^ required by this bound in `NamedProductAccess::get_field_value` + = note: this error originates in the derive macro `spacetimedb::TableType` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Test: Serialize` is not satisfied + --> tests/ui/tables.rs:7:8 + | +7 | x: Test, + | ^^^^ the trait `Serialize` is not implemented for `Test` + | + = help: the following other types implement trait `Serialize`: + bool + i8 + i16 + i32 + i64 + i128 + u8 + u16 + and $N others +note: required by a bound in `spacetimedb::spacetimedb_lib::ser::SerializeNamedProduct::serialize_element` + --> $WORKSPACE/crates/sats/src/ser.rs + | + | fn serialize_element(&mut self, name: Option<&str>, elem: &T) -> Result<(), Self::Error>; + | ^^^^^^^^^ required by this bound in `SerializeNamedProduct::serialize_element` diff --git a/crates/sats/src/typespace.rs b/crates/sats/src/typespace.rs index 42fb5122716..5ec2ead6845 100644 --- a/crates/sats/src/typespace.rs +++ b/crates/sats/src/typespace.rs @@ -180,6 +180,8 @@ impl Typespace { /// A trait for types that can be represented as an `AlgebraicType` /// provided a typing context `typespace`. +// TODO: we might want to have a note about what to do if you're trying to use a type from another crate in your table. +#[diagnostic::on_unimplemented(note = "if you own the type, try adding `#[derive(SpacetimeType)]` to its definition")] pub trait SpacetimeType { /// Returns an `AlgebraicType` representing the type for `Self` in SATS /// and in the typing context in `typespace`.