Skip to content

Commit ff65bec

Browse files
committed
Revert Item type specification
1 parent 0fcc1b8 commit ff65bec

20 files changed

+206
-153
lines changed

futures-async-macro/README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Add this to your `Cargo.toml`:
77

88
```toml
99
[dependencies]
10-
futures-preview = { version = "0.3.0-alpha.16", features = ["async-stream", "nightly"] }
10+
futures-preview = { version = "=0.3.0-alpha.16", features = ["async-stream", "nightly"] }
1111
```
1212

1313
### \#\[for_await\]
@@ -41,15 +41,15 @@ use futures::prelude::*;
4141
use futures::async_stream;
4242

4343
// Returns a stream of i32
44-
#[async_stream]
45-
fn foo(stream: impl Stream<Item = String>) -> i32 {
44+
#[async_stream(item = i32)]
45+
fn foo(stream: impl Stream<Item = String>) {
4646
#[for_await]
4747
for x in stream {
4848
yield x.parse().unwrap();
4949
}
5050
}
5151
```
5252

53-
`#[async_stream]` have an item type specified via `-> some::Path` and the values output from the stream must be yielded via the `yield` expression.
53+
`#[async_stream]` must have an item type specified via `item = some::Path` and the values output from the stream must be yielded via the `yield` expression.
5454

5555
[futures-await]: https://github.com/alexcrichton/futures-await

futures-async-macro/src/error.rs

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ macro_rules! error {
55
syn::Error::new(proc_macro2::Span::call_site(), $msg).to_compile_error(),
66
)
77
};
8+
($span:expr, $msg:expr) => {
9+
return proc_macro::TokenStream::from(
10+
syn::Error::new_spanned($span, $msg).to_compile_error(),
11+
)
12+
};
813
}
914

1015
// TODO: Should we give another name?

futures-async-macro/src/lib.rs

+31-18
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree as TokenTree2};
1111
use quote::{quote, ToTokens};
1212
use syn::{
1313
fold::{self, Fold},
14+
parse::{Parse, ParseStream},
1415
token, ArgCaptured, Error, Expr, ExprCall, ExprField, ExprForLoop, ExprMacro, ExprYield, FnArg,
15-
FnDecl, Ident, Item, ItemFn, Member, Pat, PatIdent, ReturnType, TypeTuple,
16+
FnDecl, Ident, Item, ItemFn, Member, Pat, PatIdent, ReturnType, Token, Type, TypeTuple,
1617
};
1718

1819
#[macro_use]
@@ -33,27 +34,25 @@ pub fn for_await(args: TokenStream, input: TokenStream) -> TokenStream {
3334
/// Creates streams via generators.
3435
#[proc_macro_attribute]
3536
pub fn async_stream(args: TokenStream, input: TokenStream) -> TokenStream {
36-
assert_!(args.is_empty(), args_is_not_empty!("async_stream"));
37-
38-
let item: ItemFn = syn::parse_macro_input!(input);
39-
expand_async_stream_fn(item)
37+
let arg: Arg = syn::parse_macro_input!(args);
38+
let function: ItemFn = syn::parse_macro_input!(input);
39+
expand_async_stream_fn(function, &arg.0)
4040
}
4141

42-
fn expand_async_stream_fn(item: ItemFn) -> TokenStream {
42+
fn expand_async_stream_fn(function: ItemFn, item_ty: &Type) -> TokenStream {
4343
// Parse our item, expecting a function. This function may be an actual
4444
// top-level function or it could be a method (typically dictated by the
4545
// arguments). We then extract everything we'd like to use.
46-
let ItemFn { ident, vis, constness, unsafety, abi, block, decl, attrs, .. } = item;
46+
let ItemFn { ident, vis, constness, unsafety, abi, block, decl, attrs, .. } = function;
4747
let FnDecl { inputs, output, variadic, mut generics, fn_token, .. } = *decl;
4848
let where_clause = &generics.where_clause;
4949
assert_!(variadic.is_none(), "variadic functions cannot be async");
50-
let (output, rarrow_token) = match output {
51-
ReturnType::Type(rarrow_token, t) => (*t, rarrow_token),
52-
ReturnType::Default => (
53-
TypeTuple { elems: Default::default(), paren_token: Default::default() }.into(),
54-
Default::default(),
55-
),
56-
};
50+
if let ReturnType::Type(_, t) = output {
51+
match &*t {
52+
Type::Tuple(TypeTuple { elems, .. }) if elems.is_empty() => {}
53+
_ => error!(t, "async stream functions must return the unit type"),
54+
}
55+
}
5756

5857
// We've got to get a bit creative with our handling of arguments. For a
5958
// number of reasons we translate this:
@@ -150,10 +149,10 @@ fn expand_async_stream_fn(item: ItemFn) -> TokenStream {
150149
gen_body_inner.to_tokens(tokens);
151150
});
152151

153-
// Give the invocation of the `from_generator` function the same span as the output
152+
// Give the invocation of the `from_generator` function the same span as the `item_ty`
154153
// as currently errors related to it being a result are targeted here. Not
155154
// sure if more errors will highlight this function call...
156-
let output_span = first_last(&output);
155+
let output_span = first_last(item_ty);
157156
let gen_function = quote! { ::futures::async_stream::from_generator };
158157
let gen_function = respan(gen_function, output_span);
159158
let body_inner = quote! {
@@ -170,14 +169,14 @@ fn expand_async_stream_fn(item: ItemFn) -> TokenStream {
170169
// Raw `impl` breaks syntax highlighting in some editors.
171170
let impl_token = token::Impl::default();
172171
let return_ty = quote! {
173-
#impl_token ::futures::stream::Stream<Item = #output> + #(#lifetimes +)*
172+
#impl_token ::futures::stream::Stream<Item = #item_ty> + #(#lifetimes +)*
174173
};
175174
let return_ty = respan(return_ty, output_span);
176175
TokenStream::from(quote! {
177176
#(#attrs)*
178177
#vis #unsafety #abi #constness
179178
#fn_token #ident #generics (#(#inputs_no_patterns),*)
180-
#rarrow_token #return_ty
179+
-> #return_ty
181180
#where_clause
182181
#body
183182
})
@@ -211,6 +210,20 @@ pub fn async_stream_block(input: TokenStream) -> TokenStream {
211210
tokens.into()
212211
}
213212

213+
struct Arg(Type);
214+
215+
mod kw {
216+
syn::custom_keyword!(item);
217+
}
218+
219+
impl Parse for Arg {
220+
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
221+
let _: kw::item = input.parse()?;
222+
let _: Token![=] = input.parse()?;
223+
input.parse().map(Self)
224+
}
225+
}
226+
214227
/// The scope in which `#[for_await]`, `.await` was called.
215228
///
216229
/// The type of generator depends on which scope is called.

futures/testcrate/ui/bad-input.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33

44
use futures::*;
55

6-
#[async_stream]
7-
fn foo() -> i32 {
6+
#[async_stream(item = i32)]
7+
fn foo() {
88
#[for_await(bar)]
99
for i in stream::iter(vec![1, 2]) {
1010
yield i;
1111
}
1212
}
1313

14-
#[async_stream(baz)]
15-
fn bar() -> i32 {
14+
#[async_stream(baz, item = i32)]
15+
fn bar() {
1616
#[for_await]
1717
for i in stream::iter(vec![1, 2]) {
1818
yield i;

futures/testcrate/ui/bad-input.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ error: attribute must be of the form `#[for_await]`
44
8 | #[for_await(bar)]
55
| ^^^^^^^^^^^^^^^^^
66

7-
error: attribute must be of the form `#[async_stream]`
8-
--> $DIR/bad-input.rs:14:1
7+
error: expected `item`
8+
--> $DIR/bad-input.rs:14:16
99
|
10-
14 | #[async_stream(baz)]
11-
| ^^^^^^^^^^^^^^^^^^^^
10+
14 | #[async_stream(baz, item = i32)]
11+
| ^^^
1212

1313
error: aborting due to 2 previous errors
1414

futures/testcrate/ui/bad-item-type.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#![feature(async_await, generators)]
2+
3+
use futures::*;
4+
5+
#[async_stream(item = Option<i32>)]
6+
fn foobar() {
7+
let val = Some(42);
8+
if val.is_none() {
9+
yield None;
10+
return;
11+
}
12+
let val = val.unwrap();
13+
yield val;
14+
}
15+
16+
#[async_stream(item = (i32, i32))]
17+
fn tuple() {
18+
if false {
19+
yield 3;
20+
}
21+
yield (1, 2)
22+
}
23+
24+
fn main() {}
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/bad-item-type.rs:13:11
3+
|
4+
13 | yield val;
5+
| ^^^
6+
| |
7+
| expected enum `std::option::Option`, found integer
8+
| help: try using a variant of the expected type: `Some(val)`
9+
|
10+
= note: expected type `std::option::Option<_>`
11+
found type `{integer}`
12+
13+
error[E0698]: type inside generator must be known in this context
14+
--> $DIR/bad-item-type.rs:5:1
15+
|
16+
5 | #[async_stream(item = Option<i32>)]
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `{integer}`
18+
|
19+
note: the type is part of the generator because of this `yield`
20+
--> $DIR/bad-item-type.rs:5:1
21+
|
22+
5 | #[async_stream(item = Option<i32>)]
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
25+
error[E0308]: mismatched types
26+
--> $DIR/bad-item-type.rs:21:11
27+
|
28+
21 | yield (1, 2)
29+
| ^^^^^^ expected integer, found tuple
30+
|
31+
= note: expected type `{integer}`
32+
found type `({integer}, {integer})`
33+
34+
error[E0271]: type mismatch resolving `<impl futures_core::stream::Stream as futures_core::stream::Stream>::Item == (i32, i32)`
35+
--> $DIR/bad-item-type.rs:16:23
36+
|
37+
16 | #[async_stream(item = (i32, i32))]
38+
| ^^^^^^^^^^ expected integer, found tuple
39+
|
40+
= note: expected type `{integer}`
41+
found type `(i32, i32)`
42+
= note: the return type of a function must have a statically known size
43+
44+
error[E0698]: type inside generator must be known in this context
45+
--> $DIR/bad-item-type.rs:16:1
46+
|
47+
16 | #[async_stream(item = (i32, i32))]
48+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `{integer}`
49+
|
50+
note: the type is part of the generator because of this `yield`
51+
--> $DIR/bad-item-type.rs:16:1
52+
|
53+
16 | #[async_stream(item = (i32, i32))]
54+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
55+
56+
error: aborting due to 5 previous errors
57+
58+
Some errors have detailed explanations: E0271, E0308, E0698.
59+
For more information about an error, try `rustc --explain E0271`.

futures/testcrate/ui/bad-return-type.rs

+4-17
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,10 @@
22

33
use futures::*;
44

5-
#[async_stream]
6-
fn foobar() -> Option<i32> {
7-
let val = Some(42);
8-
if val.is_none() {
9-
yield None;
10-
return;
11-
}
12-
let val = val.unwrap();
13-
yield val;
14-
}
5+
#[async_stream(item = Option<i32>)]
6+
fn foo() -> i32 {} // ERROR
157

16-
#[async_stream]
17-
fn tuple() -> (i32, i32) {
18-
if false {
19-
yield 3;
20-
}
21-
yield (1, 2)
22-
}
8+
#[async_stream(item = (i32, i32))]
9+
fn tuple() -> () {} // OK
2310

2411
fn main() {}
+5-56
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,8 @@
1-
error[E0308]: mismatched types
2-
--> $DIR/bad-return-type.rs:13:11
3-
|
4-
13 | yield val;
5-
| ^^^
6-
| |
7-
| expected enum `std::option::Option`, found integer
8-
| help: try using a variant of the expected type: `Some(val)`
9-
|
10-
= note: expected type `std::option::Option<_>`
11-
found type `{integer}`
12-
13-
error[E0698]: type inside generator must be known in this context
14-
--> $DIR/bad-return-type.rs:5:1
15-
|
16-
5 | #[async_stream]
17-
| ^^^^^^^^^^^^^^^ cannot infer type for `{integer}`
18-
|
19-
note: the type is part of the generator because of this `yield`
20-
--> $DIR/bad-return-type.rs:5:1
1+
error: async stream functions must return the unit type
2+
--> $DIR/bad-return-type.rs:6:13
213
|
22-
5 | #[async_stream]
23-
| ^^^^^^^^^^^^^^^
24-
25-
error[E0308]: mismatched types
26-
--> $DIR/bad-return-type.rs:21:11
27-
|
28-
21 | yield (1, 2)
29-
| ^^^^^^ expected integer, found tuple
30-
|
31-
= note: expected type `{integer}`
32-
found type `({integer}, {integer})`
33-
34-
error[E0271]: type mismatch resolving `<impl futures_core::stream::Stream as futures_core::stream::Stream>::Item == (i32, i32)`
35-
--> $DIR/bad-return-type.rs:17:15
36-
|
37-
17 | fn tuple() -> (i32, i32) {
38-
| ^^^^^^^^^^ expected integer, found tuple
39-
|
40-
= note: expected type `{integer}`
41-
found type `(i32, i32)`
42-
= note: the return type of a function must have a statically known size
43-
44-
error[E0698]: type inside generator must be known in this context
45-
--> $DIR/bad-return-type.rs:16:1
46-
|
47-
16 | #[async_stream]
48-
| ^^^^^^^^^^^^^^^ cannot infer type for `{integer}`
49-
|
50-
note: the type is part of the generator because of this `yield`
51-
--> $DIR/bad-return-type.rs:16:1
52-
|
53-
16 | #[async_stream]
54-
| ^^^^^^^^^^^^^^^
4+
6 | fn foo() -> i32 {} // ERROR
5+
| ^^^
556

56-
error: aborting due to 5 previous errors
7+
error: aborting due to previous error
578

58-
Some errors have detailed explanations: E0271, E0308, E0698.
59-
For more information about an error, try `rustc --explain E0271`.

futures/testcrate/ui/forget-semicolon.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use futures::*;
44

5-
#[async_stream]
5+
#[async_stream(item = ())]
66
fn foo() {
77
yield;
88
Some(())

futures/testcrate/ui/missing-item.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#![feature(async_await, generators)]
2+
3+
use futures::*;
4+
5+
#[async_stream]
6+
fn foo(a: String) {}
7+
8+
fn main() {}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: unexpected end of input, expected `item`
2+
--> $DIR/missing-item.rs:5:1
3+
|
4+
5 | #[async_stream]
5+
| ^^^^^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+

futures/testcrate/ui/nested.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
use futures::*;
44

5-
#[async_stream]
6-
fn _stream1() -> i32 {
5+
#[async_stream(item = i32)]
6+
fn _stream1() {
77
let _ = async {
88
#[for_await]
99
for i in stream::iter(vec![1, 2]) {

0 commit comments

Comments
 (0)