Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Cargo.lock

.cargo/config.toml
.cargo/config

64 changes: 64 additions & 0 deletions tests-build/tests/fail/macros_type_mismatch.stderr
Original file line number Diff line number Diff line change
@@ -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<Output = ()>`
= 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
|
Expand All @@ -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<Output = ()>`
= 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
|
Expand All @@ -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<Output = Result<(), ()>>`
= 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
|
Expand All @@ -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<Output = Option<()>>`
= 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
|
Expand Down Expand Up @@ -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<Output = Result<(), ()>>`
= 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
|
Expand All @@ -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<Output = ()>`
= 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
|
Expand Down
6 changes: 6 additions & 0 deletions tests-build/tests/pass/impl_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ async fn impl_trait() -> impl Iterator<Item = impl core::fmt::Debug> {
[()].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();
}
63 changes: 56 additions & 7 deletions tokio-macros/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,35 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
}
}

fn contains_impl_trait(ty: &syn::Type) -> 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,
Expand Down Expand Up @@ -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<Output = #output_type>> = 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<Output = #output_type> = &body;
}
},
};

quote! {
let body = async #body;
// Compile-time assertion that the future's output matches the return type.
let body = {
#check_block
body
};
}
};

Expand Down