Skip to content

Commit

Permalink
Revert Item type specification
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Jul 27, 2019
1 parent 9f04a09 commit 66b3ef9
Show file tree
Hide file tree
Showing 20 changed files with 206 additions and 153 deletions.
8 changes: 4 additions & 4 deletions futures-async-macro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Add this to your `Cargo.toml`:

```toml
[dependencies]
futures-preview = { version = "0.3.0-alpha.16", features = ["async-stream", "nightly"] }
futures-preview = { version = "=0.3.0-alpha.16", features = ["async-stream", "nightly"] }
```

### \#\[for_await\]
Expand Down Expand Up @@ -41,15 +41,15 @@ use futures::prelude::*;
use futures::async_stream;

// Returns a stream of i32
#[async_stream]
fn foo(stream: impl Stream<Item = String>) -> i32 {
#[async_stream(item = i32)]
fn foo(stream: impl Stream<Item = String>) {
#[for_await]
for x in stream {
yield x.parse().unwrap();
}
}
```

`#[async_stream]` have an item type specified via `-> some::Path` and the values output from the stream must be yielded via the `yield` expression.
`#[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.

[futures-await]: https://github.com/alexcrichton/futures-await
5 changes: 5 additions & 0 deletions futures-async-macro/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ macro_rules! error {
syn::Error::new(proc_macro2::Span::call_site(), $msg).to_compile_error(),
)
};
($span:expr, $msg:expr) => {
return proc_macro::TokenStream::from(
syn::Error::new_spanned($span, $msg).to_compile_error(),
)
};
}

// TODO: Should we give another name?
Expand Down
49 changes: 31 additions & 18 deletions futures-async-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree as TokenTree2};
use quote::{quote, ToTokens};
use syn::{
fold::{self, Fold},
parse::{Parse, ParseStream},
token, ArgCaptured, Error, Expr, ExprCall, ExprField, ExprForLoop, ExprMacro, ExprYield, FnArg,
FnDecl, Ident, Item, ItemFn, Member, Pat, PatIdent, ReturnType, TypeTuple,
FnDecl, Ident, Item, ItemFn, Member, Pat, PatIdent, ReturnType, Token, Type, TypeTuple,
};

#[macro_use]
Expand All @@ -33,27 +34,25 @@ pub fn for_await(args: TokenStream, input: TokenStream) -> TokenStream {
/// Creates streams via generators.
#[proc_macro_attribute]
pub fn async_stream(args: TokenStream, input: TokenStream) -> TokenStream {
assert_!(args.is_empty(), args_is_not_empty!("async_stream"));

let item: ItemFn = syn::parse_macro_input!(input);
expand_async_stream_fn(item)
let arg: Arg = syn::parse_macro_input!(args);
let function: ItemFn = syn::parse_macro_input!(input);
expand_async_stream_fn(function, &arg.0)
}

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

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

// Give the invocation of the `from_generator` function the same span as the output
// Give the invocation of the `from_generator` function the same span as the `item_ty`
// as currently errors related to it being a result are targeted here. Not
// sure if more errors will highlight this function call...
let output_span = first_last(&output);
let output_span = first_last(item_ty);
let gen_function = quote! { ::futures::async_stream::from_generator };
let gen_function = respan(gen_function, output_span);
let body_inner = quote! {
Expand All @@ -170,14 +169,14 @@ fn expand_async_stream_fn(item: ItemFn) -> TokenStream {
// Raw `impl` breaks syntax highlighting in some editors.
let impl_token = token::Impl::default();
let return_ty = quote! {
#impl_token ::futures::stream::Stream<Item = #output> + #(#lifetimes +)*
#impl_token ::futures::stream::Stream<Item = #item_ty> + #(#lifetimes +)*
};
let return_ty = respan(return_ty, output_span);
TokenStream::from(quote! {
#(#attrs)*
#vis #unsafety #abi #constness
#fn_token #ident #generics (#(#inputs_no_patterns),*)
#rarrow_token #return_ty
-> #return_ty
#where_clause
#body
})
Expand Down Expand Up @@ -211,6 +210,20 @@ pub fn async_stream_block(input: TokenStream) -> TokenStream {
tokens.into()
}

struct Arg(Type);

mod kw {
syn::custom_keyword!(item);
}

impl Parse for Arg {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let _: kw::item = input.parse()?;
let _: Token![=] = input.parse()?;
input.parse().map(Self)
}
}

/// The scope in which `#[for_await]`, `.await` was called.
///
/// The type of generator depends on which scope is called.
Expand Down
8 changes: 4 additions & 4 deletions futures/testcrate/ui/bad-input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@

use futures::*;

#[async_stream]
fn foo() -> i32 {
#[async_stream(item = i32)]
fn foo() {
#[for_await(bar)]
for i in stream::iter(vec![1, 2]) {
yield i;
}
}

#[async_stream(baz)]
fn bar() -> i32 {
#[async_stream(baz, item = i32)]
fn bar() {
#[for_await]
for i in stream::iter(vec![1, 2]) {
yield i;
Expand Down
8 changes: 4 additions & 4 deletions futures/testcrate/ui/bad-input.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ error: attribute must be of the form `#[for_await]`
8 | #[for_await(bar)]
| ^^^^^^^^^^^^^^^^^

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

error: aborting due to 2 previous errors

24 changes: 24 additions & 0 deletions futures/testcrate/ui/bad-item-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![feature(async_await, generators)]

use futures::*;

#[async_stream(item = Option<i32>)]
fn foobar() {
let val = Some(42);
if val.is_none() {
yield None;
return;
}
let val = val.unwrap();
yield val;
}

#[async_stream(item = (i32, i32))]
fn tuple() {
if false {
yield 3;
}
yield (1, 2)
}

fn main() {}
59 changes: 59 additions & 0 deletions futures/testcrate/ui/bad-item-type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
error[E0308]: mismatched types
--> $DIR/bad-item-type.rs:13:11
|
13 | yield val;
| ^^^
| |
| expected enum `std::option::Option`, found integer
| help: try using a variant of the expected type: `Some(val)`
|
= note: expected type `std::option::Option<_>`
found type `{integer}`

error[E0698]: type inside generator must be known in this context
--> $DIR/bad-item-type.rs:5:1
|
5 | #[async_stream(item = Option<i32>)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `{integer}`
|
note: the type is part of the generator because of this `yield`
--> $DIR/bad-item-type.rs:5:1
|
5 | #[async_stream(item = Option<i32>)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/bad-item-type.rs:21:11
|
21 | yield (1, 2)
| ^^^^^^ expected integer, found tuple
|
= note: expected type `{integer}`
found type `({integer}, {integer})`

error[E0271]: type mismatch resolving `<impl futures_core::stream::Stream as futures_core::stream::Stream>::Item == (i32, i32)`
--> $DIR/bad-item-type.rs:16:23
|
16 | #[async_stream(item = (i32, i32))]
| ^^^^^^^^^^ expected integer, found tuple
|
= note: expected type `{integer}`
found type `(i32, i32)`
= note: the return type of a function must have a statically known size

error[E0698]: type inside generator must be known in this context
--> $DIR/bad-item-type.rs:16:1
|
16 | #[async_stream(item = (i32, i32))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `{integer}`
|
note: the type is part of the generator because of this `yield`
--> $DIR/bad-item-type.rs:16:1
|
16 | #[async_stream(item = (i32, i32))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 5 previous errors

Some errors have detailed explanations: E0271, E0308, E0698.
For more information about an error, try `rustc --explain E0271`.
21 changes: 4 additions & 17 deletions futures/testcrate/ui/bad-return-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,10 @@

use futures::*;

#[async_stream]
fn foobar() -> Option<i32> {
let val = Some(42);
if val.is_none() {
yield None;
return;
}
let val = val.unwrap();
yield val;
}
#[async_stream(item = Option<i32>)]
fn foo() -> i32 {} // ERROR

#[async_stream]
fn tuple() -> (i32, i32) {
if false {
yield 3;
}
yield (1, 2)
}
#[async_stream(item = (i32, i32))]
fn tuple() -> () {} // OK

fn main() {}
61 changes: 5 additions & 56 deletions futures/testcrate/ui/bad-return-type.stderr
Original file line number Diff line number Diff line change
@@ -1,59 +1,8 @@
error[E0308]: mismatched types
--> $DIR/bad-return-type.rs:13:11
|
13 | yield val;
| ^^^
| |
| expected enum `std::option::Option`, found integer
| help: try using a variant of the expected type: `Some(val)`
|
= note: expected type `std::option::Option<_>`
found type `{integer}`

error[E0698]: type inside generator must be known in this context
--> $DIR/bad-return-type.rs:5:1
|
5 | #[async_stream]
| ^^^^^^^^^^^^^^^ cannot infer type for `{integer}`
|
note: the type is part of the generator because of this `yield`
--> $DIR/bad-return-type.rs:5:1
error: async stream functions must return the unit type
--> $DIR/bad-return-type.rs:6:13
|
5 | #[async_stream]
| ^^^^^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/bad-return-type.rs:21:11
|
21 | yield (1, 2)
| ^^^^^^ expected integer, found tuple
|
= note: expected type `{integer}`
found type `({integer}, {integer})`

error[E0271]: type mismatch resolving `<impl futures_core::stream::Stream as futures_core::stream::Stream>::Item == (i32, i32)`
--> $DIR/bad-return-type.rs:17:15
|
17 | fn tuple() -> (i32, i32) {
| ^^^^^^^^^^ expected integer, found tuple
|
= note: expected type `{integer}`
found type `(i32, i32)`
= note: the return type of a function must have a statically known size

error[E0698]: type inside generator must be known in this context
--> $DIR/bad-return-type.rs:16:1
|
16 | #[async_stream]
| ^^^^^^^^^^^^^^^ cannot infer type for `{integer}`
|
note: the type is part of the generator because of this `yield`
--> $DIR/bad-return-type.rs:16:1
|
16 | #[async_stream]
| ^^^^^^^^^^^^^^^
6 | fn foo() -> i32 {} // ERROR
| ^^^

error: aborting due to 5 previous errors
error: aborting due to previous error

Some errors have detailed explanations: E0271, E0308, E0698.
For more information about an error, try `rustc --explain E0271`.
2 changes: 1 addition & 1 deletion futures/testcrate/ui/forget-semicolon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use futures::*;

#[async_stream]
#[async_stream(item = ())]
fn foo() {
yield;
Some(())
Expand Down
8 changes: 8 additions & 0 deletions futures/testcrate/ui/missing-item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![feature(async_await, generators)]

use futures::*;

#[async_stream]
fn foo(a: String) {}

fn main() {}
8 changes: 8 additions & 0 deletions futures/testcrate/ui/missing-item.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: unexpected end of input, expected `item`
--> $DIR/missing-item.rs:5:1
|
5 | #[async_stream]
| ^^^^^^^^^^^^^^^

error: aborting due to previous error

4 changes: 2 additions & 2 deletions futures/testcrate/ui/nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use futures::*;

#[async_stream]
fn _stream1() -> i32 {
#[async_stream(item = i32)]
fn _stream1() {
let _ = async {
#[for_await]
for i in stream::iter(vec![1, 2]) {
Expand Down
Loading

0 comments on commit 66b3ef9

Please sign in to comment.