diff --git a/.gitignore b/.gitignore index 32ec3fe4b79..0d525ab5819 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ Cargo.lock .cargo/config.toml .cargo/config + diff --git a/tests-build/tests/fail/macros_type_mismatch.stderr b/tests-build/tests/fail/macros_type_mismatch.stderr index c20dae30268..075ca6434ba 100644 --- a/tests-build/tests/fail/macros_type_mismatch.stderr +++ b/tests-build/tests/fail/macros_type_mismatch.stderr @@ -1,3 +1,14 @@ +error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:3:1: 3:15}` to be a future that resolves to `()`, but it resolves to `Result<(), _>` + --> tests/fail/macros_type_mismatch.rs:3:1 + | +3 | #[tokio::main] + | ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>` + | + = note: expected unit type `()` + found enum `Result<(), _>` + = note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:3:1: 3:15}` to `&dyn Future` + = note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0308]: mismatched types --> tests/fail/macros_type_mismatch.rs:5:5 | @@ -15,6 +26,17 @@ help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panic 5 | Ok(()).expect("REASON") | +++++++++++++++++ +error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:8:1: 8:15}` to be a future that resolves to `()`, but it resolves to `Result<(), _>` + --> tests/fail/macros_type_mismatch.rs:8:1 + | +8 | #[tokio::main] + | ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>` + | + = note: expected unit type `()` + found enum `Result<(), _>` + = note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:8:1: 8:15}` to `&dyn Future` + = note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0308]: mismatched types --> tests/fail/macros_type_mismatch.rs:10:5 | @@ -32,6 +54,17 @@ help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panic 10 | return Ok(());.expect("REASON") | +++++++++++++++++ +error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:13:1: 13:15}` to be a future that resolves to `Result<(), ()>`, but it resolves to `()` + --> tests/fail/macros_type_mismatch.rs:13:1 + | +13 | #[tokio::main] + | ^^^^^^^^^^^^^^ expected `Result<(), ()>`, found `()` + | + = note: expected enum `Result<(), ()>` + found unit type `()` + = note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:13:1: 13:15}` to `&dyn Future>` + = note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0308]: mismatched types --> tests/fail/macros_type_mismatch.rs:23:5 | @@ -58,6 +91,17 @@ error[E0277]: the `?` operator can only be used in an async block that returns ` 40 | None?; | ^ cannot use the `?` operator in an async block that returns `()` +error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:38:1: 38:15}` to be a future that resolves to `Option<()>`, but it resolves to `()` + --> tests/fail/macros_type_mismatch.rs:38:1 + | +38 | #[tokio::main] + | ^^^^^^^^^^^^^^ expected `Option<()>`, found `()` + | + = note: expected enum `Option<()>` + found unit type `()` + = note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:38:1: 38:15}` to `&dyn Future>` + = note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0308]: mismatched types --> tests/fail/macros_type_mismatch.rs:40:5 | @@ -86,6 +130,17 @@ error[E0277]: the `?` operator can only be used in an async block that returns ` 57 | Ok(())?; | ^ cannot use the `?` operator in an async block that returns `()` +error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:55:1: 55:15}` to be a future that resolves to `Result<(), ()>`, but it resolves to `()` + --> tests/fail/macros_type_mismatch.rs:55:1 + | +55 | #[tokio::main] + | ^^^^^^^^^^^^^^ expected `Result<(), ()>`, found `()` + | + = note: expected enum `Result<(), ()>` + found unit type `()` + = note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:55:1: 55:15}` to `&dyn Future>` + = note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0308]: mismatched types --> tests/fail/macros_type_mismatch.rs:57:5 | @@ -102,6 +157,15 @@ help: try adding an expression at the end of the block 58 + Ok(()) | +error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:63:1: 63:15}` to be a future that resolves to `()`, but it resolves to `{integer}` + --> tests/fail/macros_type_mismatch.rs:63:1 + | +63 | #[tokio::main] + | ^^^^^^^^^^^^^^ expected `()`, found integer + | + = note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:63:1: 63:15}` to `&dyn Future` + = note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0308]: mismatched types --> tests/fail/macros_type_mismatch.rs:66:5 | diff --git a/tests-build/tests/pass/impl_trait.rs b/tests-build/tests/pass/impl_trait.rs index e0b7b958371..5dfe6747f21 100644 --- a/tests-build/tests/pass/impl_trait.rs +++ b/tests-build/tests/pass/impl_trait.rs @@ -10,8 +10,14 @@ async fn impl_trait() -> impl Iterator { [()].into_iter() } +#[tokio::main] +async fn impl_trait2() -> Result<(), impl core::fmt::Debug> { + Err(()) +} + fn main() { if impl_trait().count() == 10 { never(); } + let _ = impl_trait2(); } diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index 09c6c48a445..7328325aa51 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -293,6 +293,35 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result bool { + match ty { + syn::Type::ImplTrait(_) => true, + syn::Type::Array(t) => contains_impl_trait(&t.elem), + syn::Type::Ptr(t) => contains_impl_trait(&t.elem), + syn::Type::Reference(t) => contains_impl_trait(&t.elem), + syn::Type::Slice(t) => contains_impl_trait(&t.elem), + syn::Type::Tuple(t) => t.elems.iter().any(contains_impl_trait), + syn::Type::Paren(t) => contains_impl_trait(&t.elem), + syn::Type::Group(t) => contains_impl_trait(&t.elem), + syn::Type::Path(t) => match t.path.segments.last() { + Some(segment) => match &segment.arguments { + syn::PathArguments::AngleBracketed(args) => args.args.iter().any(|arg| match arg { + syn::GenericArgument::Type(t) => contains_impl_trait(t), + syn::GenericArgument::AssocType(t) => contains_impl_trait(&t.ty), + _ => false, + }), + syn::PathArguments::Parenthesized(args) => { + args.inputs.iter().any(contains_impl_trait) + || matches!(&args.output, syn::ReturnType::Type(_, t) if contains_impl_trait(t)) + } + syn::PathArguments::None => false, + }, + None => false, + }, + _ => false, + } +} + fn build_config( input: &ItemFn, args: AttributeArgs, @@ -494,22 +523,42 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt // // We don't do this for the main function as it should only be used once so // there will be no benefit. + let output_type = match &input.sig.output { + // For functions with no return value syn doesn't print anything, + // but that doesn't work as `Output` for our boxed `Future`, so + // default to `()` (the same type as the function output). + syn::ReturnType::Default => quote! { () }, + syn::ReturnType::Type(_, ret_type) => quote! { #ret_type }, + }; + let body = if is_test { - let output_type = match &input.sig.output { - // For functions with no return value syn doesn't print anything, - // but that doesn't work as `Output` for our boxed `Future`, so - // default to `()` (the same type as the function output). - syn::ReturnType::Default => quote! { () }, - syn::ReturnType::Type(_, ret_type) => quote! { #ret_type }, - }; quote! { let body = async #body; #crate_path::pin!(body); let body: ::core::pin::Pin<&mut dyn ::core::future::Future> = body; } } else { + // force typecheck without runtime overhead + let check_block = match &input.sig.output { + syn::ReturnType::Type(_, t) + if matches!(**t, syn::Type::Never(_)) || contains_impl_trait(t) => + { + quote! {} + } + _ => quote! { + if false { + let _: &dyn ::core::future::Future = &body; + } + }, + }; + quote! { let body = async #body; + // Compile-time assertion that the future's output matches the return type. + let body = { + #check_block + body + }; } };