From 4d4dea1d227068be6cdaf52bea6dc588cf62bbad Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 24 Apr 2019 20:50:13 +0900 Subject: [PATCH 01/15] Add async_stream to create streams via generators --- Cargo.toml | 1 + futures-async-macro/Cargo.toml | 24 ++ futures-async-macro/LICENSE-APACHE | 1 + futures-async-macro/LICENSE-MIT | 1 + futures-async-macro/README.md | 51 +++ futures-async-macro/src/elision.rs | 101 +++++ futures-async-macro/src/error.rs | 52 +++ futures-async-macro/src/lib.rs | 404 ++++++++++++++++++ futures-select-macro/Cargo.toml | 2 +- futures-util/Cargo.toml | 1 + futures-util/src/async_stream/await_item.rs | 46 ++ futures-util/src/async_stream/mod.rs | 83 ++++ futures-util/src/async_stream/stream_yield.rs | 24 ++ futures-util/src/lib.rs | 12 + futures/Cargo.toml | 2 + futures/src/lib.rs | 12 + 16 files changed, 816 insertions(+), 1 deletion(-) create mode 100644 futures-async-macro/Cargo.toml create mode 120000 futures-async-macro/LICENSE-APACHE create mode 120000 futures-async-macro/LICENSE-MIT create mode 100644 futures-async-macro/README.md create mode 100644 futures-async-macro/src/elision.rs create mode 100644 futures-async-macro/src/error.rs create mode 100644 futures-async-macro/src/lib.rs create mode 100644 futures-util/src/async_stream/await_item.rs create mode 100644 futures-util/src/async_stream/mod.rs create mode 100644 futures-util/src/async_stream/stream_yield.rs diff --git a/Cargo.toml b/Cargo.toml index 430205d8ca..47e4f81a2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "futures", + "futures-async-macro", "futures-core", "futures-channel", "futures-executor", diff --git a/futures-async-macro/Cargo.toml b/futures-async-macro/Cargo.toml new file mode 100644 index 0000000000..aa75e5d05b --- /dev/null +++ b/futures-async-macro/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "futures-async-macro-preview" +edition = "2018" +version = "0.3.0-alpha.17" +authors = ["Alex Crichton "] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang-nursery/futures-rs" +homepage = "https://rust-lang-nursery.github.io/futures-rs" +documentation = "https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.14/futures_async_macro" +description = """ +Definition of the `#[async_stream]` macro for the `futures-rs` crate as well as a few other assorted macros. +""" + +[lib] +name = "futures_async_macro" +proc-macro = true + +[features] +std = [] + +[dependencies] +proc-macro2 = "0.4" +quote = "0.6" +syn = { version = "0.15.31", features = ["full", "fold"] } diff --git a/futures-async-macro/LICENSE-APACHE b/futures-async-macro/LICENSE-APACHE new file mode 120000 index 0000000000..965b606f33 --- /dev/null +++ b/futures-async-macro/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/futures-async-macro/LICENSE-MIT b/futures-async-macro/LICENSE-MIT new file mode 120000 index 0000000000..76219eb72e --- /dev/null +++ b/futures-async-macro/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/futures-async-macro/README.md b/futures-async-macro/README.md new file mode 100644 index 0000000000..598bec6141 --- /dev/null +++ b/futures-async-macro/README.md @@ -0,0 +1,51 @@ + +**Note that these are experimental APIs.** + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-preview = { version = "0.3.0-alpha.14", features = ["async-stream", "nightly"] } +``` + +### \#\[for_await\] + +Processes streams using a for loop. + +This is a reimplement of [futures-await]'s `#[async]` for loops for futures 0.3 and is an experimental implementation of [the idea listed as the next step of async/await](https://github.com/rust-lang/rfcs/blob/master/text/2394-async_await.md#for-await-and-processing-streams). + +```rust +use futures::for_await; +use futures::prelude::*; + +#[for_await] +for value in stream::iter(1..=5) { + println!("{}", value); +} +``` + +`value` has the `Item` type of the stream passed in. Note that async for loops can only be used inside of `async` functions or `#[async_stream]` functions. + +### \#\[async_stream\] + +This is a reimplement of [futures-await]'s `#[async_stream]` for futures 0.3 and is an experimental implementation of [the idea listed as the next step of async/await](https://github.com/rust-lang/rfcs/blob/master/text/2394-async_await.md#generators-and-streams). + +```rust +use futures::prelude::*; +use futures::{async_stream, stream_yield}; + +// Returns a stream of i32 +#[async_stream] +fn foo(stream: impl Stream) -> i32 { + #[for_await] + for x in stream { + 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 `stream_yield!` macro. + +[futures-await]: https://github.com/alexcrichton/futures-await diff --git a/futures-async-macro/src/elision.rs b/futures-async-macro/src/elision.rs new file mode 100644 index 0000000000..4d4d6c13e6 --- /dev/null +++ b/futures-async-macro/src/elision.rs @@ -0,0 +1,101 @@ +use proc_macro2::Span; +use syn::fold::Fold; +use syn::punctuated::Punctuated; +use syn::token::Comma; +use syn::{ArgSelfRef, FnArg, GenericParam, Lifetime, LifetimeDef, TypeReference}; + +pub(super) fn unelide_lifetimes( + generics: &mut Punctuated, + args: Vec, +) -> Vec { + let mut folder = UnelideLifetimes::new(generics); + args.into_iter().map(|arg| folder.fold_fn_arg(arg)).collect() +} + +struct UnelideLifetimes<'a> { + generics: &'a mut Punctuated, + lifetime_index: usize, + lifetime_name: String, + count: u32, +} + +impl<'a> UnelideLifetimes<'a> { + fn new(generics: &'a mut Punctuated) -> UnelideLifetimes<'a> { + let lifetime_index = lifetime_index(generics); + let lifetime_name = lifetime_name(generics); + UnelideLifetimes { generics, lifetime_index, lifetime_name, count: 0 } + } + + // Constitute a new lifetime + fn new_lifetime(&mut self) -> Lifetime { + let lifetime_name = format!("{}{}", self.lifetime_name, self.count); + let lifetime = Lifetime::new(&lifetime_name, Span::call_site()); + + let idx = self.lifetime_index + self.count as usize; + self.generics.insert(idx, GenericParam::Lifetime(LifetimeDef::new(lifetime.clone()))); + self.count += 1; + + lifetime + } + + // Take an Option and guarantee its an unelided lifetime + fn expand_lifetime(&mut self, lifetime: Option) -> Lifetime { + match lifetime { + Some(l) => self.fold_lifetime(l), + None => self.new_lifetime(), + } + } +} + +impl Fold for UnelideLifetimes<'_> { + // Handling self arguments + fn fold_arg_self_ref(&mut self, arg: ArgSelfRef) -> ArgSelfRef { + let ArgSelfRef { and_token, lifetime, mutability, self_token } = arg; + let lifetime = Some(self.expand_lifetime(lifetime)); + ArgSelfRef { and_token, lifetime, mutability, self_token } + } + + // If the lifetime is `'_`, replace it with a new unelided lifetime + fn fold_lifetime(&mut self, lifetime: Lifetime) -> Lifetime { + if lifetime.ident == "_" { + self.new_lifetime() + } else { + lifetime + } + } + + // If the reference's lifetime is elided, replace it with a new unelided lifetime + fn fold_type_reference(&mut self, ty_ref: TypeReference) -> TypeReference { + let TypeReference { and_token, lifetime, mutability, elem } = ty_ref; + let lifetime = Some(self.expand_lifetime(lifetime)); + let elem = Box::new(self.fold_type(*elem)); + TypeReference { and_token, lifetime, mutability, elem } + } +} + +fn lifetime_index(generics: &Punctuated) -> usize { + generics + .iter() + .take_while(|param| if let GenericParam::Lifetime(_) = param { true } else { false }) + .count() +} + +// Determine the prefix for all lifetime names. Ensure it doesn't +// overlap with any existing lifetime names. +fn lifetime_name(generics: &Punctuated) -> String { + let mut lifetime_name = String::from("'_async"); + let existing_lifetimes: Vec = generics + .iter() + .filter_map(|param| { + if let GenericParam::Lifetime(LifetimeDef { lifetime, .. }) = param { + Some(lifetime.to_string()) + } else { + None + } + }) + .collect(); + while existing_lifetimes.iter().any(|name| name.starts_with(&lifetime_name)) { + lifetime_name.push('_'); + } + lifetime_name +} diff --git a/futures-async-macro/src/error.rs b/futures-async-macro/src/error.rs new file mode 100644 index 0000000000..19534dd116 --- /dev/null +++ b/futures-async-macro/src/error.rs @@ -0,0 +1,52 @@ +// Make error messages a little more readable than `panic!`. +macro_rules! error { + ($msg:expr) => { + return proc_macro::TokenStream::from( + syn::Error::new(proc_macro2::Span::call_site(), $msg).to_compile_error(), + ) + }; +} + +// TODO: Should we give another name? +// `assert!` that call `error!` instead of `panic!`. +macro_rules! assert_ { + ($e:expr, $msg:expr) => { + if !$e { + error!($msg) + } + }; +} + +pub(super) fn expr_compile_error(e: &syn::Error) -> syn::Expr { + syn::parse2(e.to_compile_error()).unwrap() +} + +// Long error messages and error messages that are called multiple times + +macro_rules! args_is_not_empty { + ($name:expr) => { + concat!("attribute must be of the form `#[", $name, "]`") + }; +} + +macro_rules! outside_of_async_error { + ($tokens:expr, $name:expr) => { + $crate::error::expr_compile_error(&syn::Error::new_spanned( + $tokens, + concat!( + $name, + " cannot be allowed outside of \ + async closures, blocks, functions, async_stream blocks, and functions." + ), + )) + }; +} + +macro_rules! outside_of_async_stream_error { + ($tokens:expr, $name:expr) => { + $crate::error::expr_compile_error(&syn::Error::new_spanned( + $tokens, + concat!($name, " cannot be allowed outside of async_stream blocks, and functions."), + )) + }; +} diff --git a/futures-async-macro/src/lib.rs b/futures-async-macro/src/lib.rs new file mode 100644 index 0000000000..88249f5bbe --- /dev/null +++ b/futures-async-macro/src/lib.rs @@ -0,0 +1,404 @@ +//! Procedural macro for the `#[async_stream]` attribute. + +#![recursion_limit = "128"] +#![warn(rust_2018_idioms)] + +extern crate proc_macro; + +use proc_macro::{Delimiter, Group, TokenStream, TokenTree}; +use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree as TokenTree2}; +use quote::{quote, ToTokens}; +use syn::{ + fold::{self, Fold}, + token, ArgCaptured, Error, Expr, ExprForLoop, ExprMacro, FnArg, FnDecl, Ident, Item, ItemFn, + Pat, PatIdent, ReturnType, TypeTuple, +}; + +#[macro_use] +mod error; + +mod elision; + +#[proc_macro_attribute] +pub fn for_await(args: TokenStream, input: TokenStream) -> TokenStream { + assert_!(args.is_empty(), args_is_not_empty!("for_await")); + + let mut expr: ExprForLoop = syn::parse_macro_input!(input); + expr.attrs.push(syn::parse_quote!(#[for_await])); + Expand(Future).fold_expr(Expr::ForLoop(expr)).into_token_stream().into() +} + +#[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) +} + +fn expand_async_stream_fn(item: ItemFn) -> 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 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(), + ), + }; + + // We've got to get a bit creative with our handling of arguments. For a + // number of reasons we translate this: + // + // fn foo(ref a: u32) -> u32 { + // // ... + // } + // + // into roughly: + // + // fn foo(__arg_0: u32) -> impl Stream { + // from_generator(static move || { + // let ref a = __arg_0; + // + // // ... + // }) + // } + // + // The intention here is to ensure that all local function variables get + // moved into the generator we're creating, and they're also all then bound + // appropriately according to their patterns and whatnot. + // + // We notably skip everything related to `self` which typically doesn't have + // many patterns with it and just gets captured naturally. + let mut inputs_no_patterns = Vec::new(); + let mut patterns = Vec::new(); + let mut temp_bindings = Vec::new(); + for (i, input) in inputs.into_iter().enumerate() { + // `self: Box` will get captured naturally + let mut is_input_no_pattern = false; + if let FnArg::Captured(ArgCaptured { pat: Pat::Ident(pat), .. }) = &input { + if pat.ident == "self" { + is_input_no_pattern = true; + } + } + if is_input_no_pattern { + inputs_no_patterns.push(input); + continue; + } + + if let FnArg::Captured(ArgCaptured { + pat: pat @ Pat::Ident(PatIdent { by_ref: Some(_), .. }), + ty, + colon_token, + }) = input + { + // `ref a: B` (or some similar pattern) + patterns.push(pat); + let ident = Ident::new(&format!("__arg_{}", i), Span::call_site()); + temp_bindings.push(ident.clone()); + let pat = PatIdent { by_ref: None, mutability: None, ident, subpat: None }.into(); + inputs_no_patterns.push(ArgCaptured { pat, ty, colon_token }.into()); + } else { + // Other arguments get captured naturally + inputs_no_patterns.push(input); + } + } + + // This is the point where we handle + // + // #[for_await] + // for x in y { + // } + // + // Basically just take all those expression and expand them. + // + // Also, in some items, it needs to adjust the type to be generated depending on whether it is + // called in the scope of async or the scope of async-stream, it is processed here. + let block = Expand(Stream).fold_block(*block); + + let block_inner = quote! { + #( let #patterns = #temp_bindings; )* + #block + }; + let mut result = TokenStream2::new(); + block.brace_token.surround(&mut result, |tokens| { + block_inner.to_tokens(tokens); + }); + token::Semi([block.brace_token.span]).to_tokens(&mut result); + + let gen_body_inner = quote! { + let (): () = #result + + // Ensure that this closure is a generator, even if it doesn't + // have any `yield` statements. + #[allow(unreachable_code)] + { + return; + loop { yield ::futures::core_reexport::task::Poll::Pending } + } + }; + let mut gen_body = TokenStream2::new(); + block.brace_token.surround(&mut gen_body, |tokens| { + gen_body_inner.to_tokens(tokens); + }); + + // Give the invocation of the `from_generator` function the same span as the output + // 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 gen_function = quote! { ::futures::async_stream::from_generator }; + let gen_function = respan(gen_function, output_span); + let body_inner = quote! { + #gen_function (static move || -> () #gen_body) + }; + let mut body = TokenStream2::new(); + block.brace_token.surround(&mut body, |tokens| { + body_inner.to_tokens(tokens); + }); + + let inputs_no_patterns = elision::unelide_lifetimes(&mut generics.params, inputs_no_patterns); + let lifetimes: Vec<_> = generics.lifetimes().map(|l| &l.lifetime).collect(); + + // Raw `impl` breaks syntax highlighting in some editors. + let impl_token = token::Impl::default(); + let return_ty = quote! { + #impl_token ::futures::stream::Stream + #(#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 + #where_clause + #body + }) +} + +#[proc_macro] +pub fn async_stream_block(input: TokenStream) -> TokenStream { + let input = TokenStream::from(TokenTree::Group(Group::new(Delimiter::Brace, input))); + let expr = syn::parse_macro_input!(input); + let expr = Expand(Stream).fold_expr(expr); + + let mut tokens = quote! { ::futures::async_stream::from_generator }; + + // Use some manual token construction here instead of `quote!` to ensure + // that we get the `call_site` span instead of the default span. + let span = Span::call_site(); + token::Paren(span).surround(&mut tokens, |tokens| { + token::Static(span).to_tokens(tokens); + token::Move(span).to_tokens(tokens); + token::OrOr([span, span]).to_tokens(tokens); + token::Brace(span).surround(tokens, |tokens| { + (quote! { + if false { yield ::futures::core_reexport::task::Poll::Pending } + }) + .to_tokens(tokens); + expr.to_tokens(tokens); + }); + }); + + tokens.into() +} + +/// The scope in which `#[for_await]`, `await!` and `await_item!` was called. +/// +/// The type of generator depends on which scope is called. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Scope { + /// `async fn`, `async {}`, `async ||` + Future, + /// `#[async_stream]`, `async_stream_block!` + Stream, + /// `static move ||`, `||` + /// + /// It cannot call `#[for_await]`, `await!` and `await_item!` in this scope. + Closure, +} + +use Scope::{Closure, Future, Stream}; + +struct Expand(Scope); + +impl Expand { + /// Expands `#[for_await] for pat in expr { .. }`. + fn expand_for_await(&self, mut expr: ExprForLoop) -> Expr { + if !(expr.attrs.len() == 1 && expr.attrs[0].path.is_ident("for_await")) { + return Expr::ForLoop(expr); + } else if !expr.attrs[0].tts.is_empty() { + return error::expr_compile_error(&Error::new_spanned( + expr.attrs.pop(), + args_is_not_empty!("for_await"), + )); + } + + // It needs to adjust the type yielded by the macro because generators used internally by + // async fn yield `()` type, but generators used internally by `async_stream` yield + // `Poll` type. + let yield_ = match self.0 { + Future => TokenStream2::new(), + Stream => quote! { ::futures::core_reexport::task::Poll::Pending }, + Closure => return outside_of_async_error!(expr, "#[for_await]"), + }; + let ExprForLoop { label, pat, expr, body, .. } = expr; + + // Basically just expand to a `poll` loop + syn::parse_quote! {{ + let mut __pinned = #expr; + #label + loop { + let #pat = { + match ::futures::async_stream::poll_next_with_tls_context(unsafe { + ::futures::core_reexport::pin::Pin::new_unchecked(&mut __pinned) + }) + { + ::futures::core_reexport::task::Poll::Ready(e) => { + match e { + ::futures::core_reexport::option::Option::Some(e) => e, + ::futures::core_reexport::option::Option::None => break, + } + } + ::futures::core_reexport::task::Poll::Pending => { + yield #yield_; + continue + } + } + }; + + #body + } + }} + } + + /* TODO: If this is enabled, it can be used for `yield` instead of `stream_yield!`. + Raw `yield` is simpler, but it may be preferable to distinguish it from `yield` called + in other scopes. + /// Expands `yield expr` in `async_stream` scope. + fn expand_yield(&self, expr: ExprYield) -> ExprYield { + if self.0 != Stream { + return expr; + } + + let ExprYield { attrs, yield_token, expr } = expr; + let expr = expr.map_or_else(|| quote!(()), ToTokens::into_token_stream); + let expr = syn::parse_quote! { + ::futures::core_reexport::task::Poll::Ready(#expr) + }; + ExprYield { attrs, yield_token, expr: Some(Box::new(expr)) } + } + */ + + /// Expands a macro. + fn expand_macro(&mut self, mut expr: ExprMacro) -> Expr { + if self.0 == Stream && expr.mac.path.is_ident("await") { + return self.expand_await_macros(expr, "poll_with_tls_context"); + } else if expr.mac.path.is_ident("await_item") { + match self.0 { + Stream => return self.expand_await_macros(expr, "poll_next_with_tls_context"), + Closure => return outside_of_async_error!(expr, "await_item!"), + Future => {} + } + } else if expr.mac.path.is_ident("async_stream_block") { + // FIXME: When added Parse impl for ExprCall, replace `if let ..` + `unreachable!()` + // with `let` + `.unwrap()` + if let Ok(Expr::Call(mut e)) = syn::parse(async_stream_block(expr.mac.tts.into())) { + e.attrs.append(&mut expr.attrs); + return Expr::Call(e); + } else { + unreachable!() + } + } else if self.0 != Stream && expr.mac.path.is_ident("stream_yield") { + // TODO: Should we remove real `stream_yield!` macro and replace `stream_yield!` call in + // here? -- or should we use `yield` instead of ``? + return outside_of_async_stream_error!(expr, "stream_yield!"); + } + + Expr::Macro(expr) + } + + /// Expands `await!(expr)` or `await_item!(expr)` in `async_stream` scope. + /// + /// It needs to adjust the type yielded by the macro because generators used internally by + /// async fn yield `()` type, but generators used internally by `async_stream` yield + /// `Poll` type. + fn expand_await_macros(&mut self, expr: ExprMacro, poll_fn: &str) -> Expr { + assert_eq!(self.0, Stream); + + let expr = expr.mac.tts; + let poll_fn = Ident::new(poll_fn, Span::call_site()); + + // Because macro input (`#expr`) is untrusted, use `syn::parse2` + `expr_compile_error` + // instead of `syn::parse_quote!` to generate better error messages (`syn::parse_quote!` + // panics if fail to parse). + syn::parse2(quote! {{ + let mut __pinned = #expr; + loop { + if let ::futures::core_reexport::task::Poll::Ready(x) = + ::futures::async_stream::#poll_fn(unsafe { + ::futures::core_reexport::pin::Pin::new_unchecked(&mut __pinned) + }) + { + break x; + } + + yield ::futures::core_reexport::task::Poll::Pending + } + }}) + .unwrap_or_else(|e| error::expr_compile_error(&e)) + } +} + +impl Fold for Expand { + fn fold_expr(&mut self, expr: Expr) -> Expr { + // Backup current scope and adjust the scope. This must be done before folding expr. + let tmp = self.0; + match &expr { + Expr::Async(_) => self.0 = Future, + Expr::Closure(expr) => self.0 = if expr.asyncness.is_some() { Future } else { Closure }, + Expr::Macro(expr) if expr.mac.path.is_ident("async_stream_block") => self.0 = Stream, + _ => {} + } + + let expr = match fold::fold_expr(self, expr) { + Expr::ForLoop(expr) => self.expand_for_await(expr), + // Expr::Yield(expr) => Expr::Yield(self.expand_yield(expr)), + Expr::Macro(expr) => self.expand_macro(expr), + expr => expr, + }; + + // Restore the backup. + self.0 = tmp; + expr + } + + // Don't recurse into items + fn fold_item(&mut self, item: Item) -> Item { + item + } +} + +fn first_last(tokens: &T) -> (Span, Span) { + let mut spans = TokenStream2::new(); + tokens.to_tokens(&mut spans); + let good_tokens = spans.into_iter().collect::>(); + let first_span = good_tokens.first().map_or_else(Span::call_site, TokenTree2::span); + let last_span = good_tokens.last().map_or_else(|| first_span, TokenTree2::span); + (first_span, last_span) +} + +fn respan(input: TokenStream2, (first_span, last_span): (Span, Span)) -> TokenStream2 { + let mut new_tokens = input.into_iter().collect::>(); + if let Some(token) = new_tokens.first_mut() { + token.set_span(first_span); + } + for token in new_tokens.iter_mut().skip(1) { + token.set_span(last_span); + } + new_tokens.into_iter().collect() +} diff --git a/futures-select-macro/Cargo.toml b/futures-select-macro/Cargo.toml index fd81124a28..039f4f9dc0 100644 --- a/futures-select-macro/Cargo.toml +++ b/futures-select-macro/Cargo.toml @@ -22,4 +22,4 @@ std = [] proc-macro2 = "0.4" proc-macro-hack = "0.5.3" quote = "0.6" -syn = { version = "0.15.25", features = ["full"] } +syn = { version = "0.15.31", features = ["full"] } diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index a28bc5c718..c56ff632a8 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -28,6 +28,7 @@ sink = ["futures-sink-preview"] io = ["std", "futures-io-preview", "memchr"] channel = ["std", "futures-channel-preview"] select-macro = ["async-await", "futures-select-macro-preview", "proc-macro-hack", "proc-macro-nested", "rand"] +async-stream = ["std"] [dependencies] futures-core-preview = { path = "../futures-core", version = "=0.3.0-alpha.17", default-features = false } diff --git a/futures-util/src/async_stream/await_item.rs b/futures-util/src/async_stream/await_item.rs new file mode 100644 index 0000000000..6f56c3b705 --- /dev/null +++ b/futures-util/src/async_stream/await_item.rs @@ -0,0 +1,46 @@ +/// Await an item from a stream inside an `async` function. +/// +/// You should pass an object implementing [`Stream`] to this macro, it will +/// implicitly `yield` while that stream returns [`Poll::Pending`] and evaluate +/// to a [`Result`] containing the next item or error from that stream. +/// +/// If you want to iterate over all items in a `Stream` you should instead see +/// the documentation on `#[async] for` in the main `#[async]` documentation. +/// +/// # Examples +/// +/// ```no-run +/// #![feature(futures_api, async_await, generators)] +/// use futures::prelude::*; +/// use futures::executor::block_on; +/// use futures::await_item; +/// +/// async fn eventually_ten() -> u32 { +/// let mut stream = stream::repeat(5); +/// if let Some(first) = await_item!(&mut stream) { +/// if let Some(second) = await_item!(&mut stream) { +/// return first + second; +/// } +/// } +/// 0 +/// } +/// +/// assert_eq!(10, block_on(eventually_ten())); +/// ``` +#[macro_export] +macro_rules! await_item { + ($e:expr) => {{ + let mut pinned = $e; + loop { + if let $crate::core_reexport::task::Poll::Ready(x) = + $crate::async_stream::poll_next_with_tls_context(unsafe { + $crate::core_reexport::pin::Pin::new_unchecked(&mut pinned) + }) + { + break x; + } + + yield + } + }} +} diff --git a/futures-util/src/async_stream/mod.rs b/futures-util/src/async_stream/mod.rs new file mode 100644 index 0000000000..6c599e75f5 --- /dev/null +++ b/futures-util/src/async_stream/mod.rs @@ -0,0 +1,83 @@ +//! Await +//! +//! This module contains a number of functions and combinators for working +//! with `async`/`await` code. + +#[macro_use] +mod await_item; +pub use self::await_item::*; + +#[macro_use] +mod stream_yield; +pub use self::stream_yield::*; + +use futures_core::future::Future; +use futures_core::stream::Stream; +use std::future::{self, get_task_context, set_task_context}; +use std::marker::{PhantomData, PhantomPinned}; +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +/// Wrap a generator in a stream. +/// +/// This function returns a `GenStream` underneath, but hides it in `impl Trait` to give +/// better error messages (`impl Stream` rather than `GenStream<[closure.....]>`). +pub fn from_generator(x: T) -> impl Stream +where + T: Generator, Return = ()>, +{ + GenStream { gen: x, done: false, _phantom: PhantomData, _pin: PhantomPinned } +} + +/// A wrapper around generators used to implement `Stream` for `async`/`await` code. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +struct GenStream, Return = ()>> { + gen: T, + // If resuming after Complete, std generators panic. This is natural when using the generators, + // but in the streams, we may want to call `poll_next` after `poll_next` returns `None` + // because it is possible to call `next` after `next` returns `None` in many iterators. + done: bool, + _phantom: PhantomData, + // We rely on the fact that async/await streams are immovable in order to create + // self-referential borrows in the underlying generator. + _pin: PhantomPinned, +} + +impl, Return = ()>> Stream for GenStream { + type Item = U; + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.done { + return Poll::Ready(None); + } + // Safe because we're !Unpin + !Drop mapping to a ?Unpin value + let Self { gen, done, .. } = unsafe { Pin::get_unchecked_mut(self) }; + let gen = unsafe { Pin::new_unchecked(gen) }; + set_task_context(cx, || match gen.resume() { + GeneratorState::Yielded(x) => x.map(Some), + GeneratorState::Complete(()) => { + *done = true; + Poll::Ready(None) + } + }) + } +} + +/// Polls a stream in the current thread-local task waker. +pub fn poll_next_with_tls_context(s: Pin<&mut S>) -> Poll> +where + S: Stream, +{ + get_task_context(|cx| S::poll_next(s, cx)) +} + +// The `await!` called in `async_stream` needs to be adjust to yield `Poll`, +// but for this purpose, we don't want the user to use `#![feature(gen_future)]`. +/// Polls a future in the current thread-local task waker. +#[inline] +pub fn poll_with_tls_context(f: Pin<&mut F>) -> Poll +where + F: Future +{ + future::poll_with_tls_context(f) +} diff --git a/futures-util/src/async_stream/stream_yield.rs b/futures-util/src/async_stream/stream_yield.rs new file mode 100644 index 0000000000..9cabccf0e6 --- /dev/null +++ b/futures-util/src/async_stream/stream_yield.rs @@ -0,0 +1,24 @@ +/// Yield an item from an `#[async_stream]` function. +/// +/// # Examples +/// +/// ```no-run +/// #![feature(futures_api, generators)] +/// use futures::prelude::*; +/// use futures::executor::block_on; +/// use futures::{async_stream, stream_yield}; +/// +/// #[async_stream] +/// fn one_five() -> u32 { +/// stream_yield!(1); +/// stream_yield!(5); +/// } +/// +/// assert_eq!(vec![1, 5], block_on(one_five().collect::>())); +/// ``` +#[macro_export] +macro_rules! stream_yield { + ($e:expr) => {{ + yield $crate::core_reexport::task::Poll::Ready($e) + }} +} diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 82d6a44763..766efad0e2 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -3,6 +3,7 @@ #![cfg_attr(feature = "async-await", feature(async_await))] #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] +#![cfg_attr(feature = "async-stream", feature(gen_future, generator_trait, generators))] #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] @@ -20,6 +21,9 @@ compile_error!("The `cfg-target-has-atomic` feature requires the `nightly` featu #[cfg(all(feature = "async-await", not(feature = "nightly")))] compile_error!("The `async-await` feature requires the `nightly` feature as an explicit opt-in to unstable features"); +#[cfg(all(feature = "async-stream", not(feature = "nightly")))] +compile_error!("The `async-stream` feature requires the `nightly` feature as an explicit opt-in to unstable features"); + #[cfg(feature = "alloc")] extern crate alloc; @@ -38,6 +42,14 @@ pub mod async_await; #[doc(hidden)] pub use self::async_await::*; +#[cfg(feature = "async-stream")] +#[macro_use] +#[doc(hidden)] +pub mod async_stream; +#[cfg(feature = "async-stream")] +#[doc(hidden)] +pub use self::async_stream::*; + #[cfg(feature = "select-macro")] #[doc(hidden)] pub mod rand_reexport { // used by select! diff --git a/futures/Cargo.toml b/futures/Cargo.toml index b598bfb6fc..683373305e 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -28,6 +28,7 @@ futures-executor-preview = { path = "../futures-executor", version = "=0.3.0-alp futures-io-preview = { path = "../futures-io", version = "=0.3.0-alpha.17", default-features = false } futures-sink-preview = { path = "../futures-sink", version = "=0.3.0-alpha.17", default-features = false } futures-util-preview = { path = "../futures-util", version = "=0.3.0-alpha.17", default-features = false, features = ["sink"] } +futures-async-macro-preview = { path = "../futures-async-macro", version = "=0.3.0-alpha.17", optional = true } [dev-dependencies] pin-utils = "0.1.0-alpha.4" @@ -44,6 +45,7 @@ async-await = ["futures-util-preview/async-await", "futures-util-preview/select- compat = ["std", "futures-util-preview/compat"] io-compat = ["compat", "futures-util-preview/io-compat"] cfg-target-has-atomic = ["futures-core-preview/cfg-target-has-atomic", "futures-channel-preview/cfg-target-has-atomic", "futures-util-preview/cfg-target-has-atomic"] +async-stream = ["std", "futures-util-preview/async-stream", "futures-async-macro-preview"] [package.metadata.docs.rs] all-features = true diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 675b2656bd..9cc899019e 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -40,6 +40,9 @@ compile_error!("The `async-await` feature requires the `nightly` feature as an e #[cfg(all(feature = "cfg-target-has-atomic", not(feature = "nightly")))] compile_error!("The `cfg-target-has-atomic` feature requires the `nightly` feature as an explicit opt-in to unstable features"); +#[cfg(all(feature = "async-stream", not(feature = "nightly")))] +compile_error!("The `async-stream` feature requires the `nightly` feature as an explicit opt-in to unstable features"); + #[doc(hidden)] pub use futures_core::core_reexport; #[doc(hidden)] pub use futures_core::future::Future; @@ -72,6 +75,15 @@ pub use futures_util::{ // Async-await join, try_join, pending, poll, }; +// Async stream +#[cfg(feature = "async-stream")] +pub use futures_util::{await_item, stream_yield}; +#[cfg(feature = "async-stream")] +#[doc(hidden)] +pub use futures_util::async_stream; +#[cfg(feature = "async-stream")] +#[doc(hidden)] // https://github.com/rust-lang/rust/issues/58696 +pub use futures_async_macro::*; #[cfg_attr( feature = "cfg-target-has-atomic", From 4503e44ff787c51483f1e1dad5361bcf97cc82c5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 24 Apr 2019 20:50:39 +0900 Subject: [PATCH 02/15] Add tests --- .travis.yml | 7 + futures/testcrate/Cargo.toml | 9 +- futures/testcrate/lib.rs | 1 + futures/testcrate/tests/ui.rs | 12 +- futures/testcrate/ui/bad-input-1.rs | 13 + futures/testcrate/ui/bad-input-1.stderr | 8 + futures/testcrate/ui/bad-input-2.rs | 22 ++ futures/testcrate/ui/bad-input-2.stderr | 14 ++ futures/testcrate/ui/bad-return-type.rs | 27 +-- futures/testcrate/ui/bad-return-type.stderr | 202 ++++++++++++---- futures/testcrate/ui/forget-ok.rs | 11 - futures/testcrate/ui/forget-ok.stderr | 25 -- futures/testcrate/ui/forget-semicolon.rs | 11 + futures/testcrate/ui/forget-semicolon.stderr | 14 ++ futures/testcrate/ui/missing-item.rs | 9 - futures/testcrate/ui/missing-item.stderr | 10 - .../testcrate/ui/move-captured-variable.rs | 8 +- .../ui/move-captured-variable.stderr | 16 +- futures/testcrate/ui/nested.rs | 15 ++ futures/testcrate/ui/nested.stderr | 20 ++ futures/testcrate/ui/not-a-result.rs | 23 -- futures/testcrate/ui/not-a-result.stderr | 61 ----- futures/testcrate/ui/type_error.rs | 10 +- futures/testcrate/ui/type_error.stderr | 4 +- futures/testcrate/ui/unresolved-type.rs | 13 +- futures/testcrate/ui/unresolved-type.stderr | 44 ++-- futures/testcrate/ui/update-all-references.sh | 2 +- futures/testcrate/ui/update-references.sh | 6 +- futures/tests/async_stream/elisions.rs | 66 ++++++ .../async_await => tests/async_stream}/mod.rs | 1 + futures/tests/async_stream/nested.rs | 13 + futures/tests/async_stream/pinned.rs | 45 ++++ futures/tests/async_stream/smoke.rs | 147 ++++++++++++ futures/tests/async_stream_tests.rs | 5 + .../tests_disabled/async_await/elisions.rs | 49 ---- futures/tests_disabled/async_await/pinned.rs | 116 --------- futures/tests_disabled/async_await/smoke.rs | 223 ------------------ futures/tests_disabled/async_await_tests.rs | 4 - 38 files changed, 619 insertions(+), 667 deletions(-) create mode 100644 futures/testcrate/ui/bad-input-1.rs create mode 100644 futures/testcrate/ui/bad-input-1.stderr create mode 100644 futures/testcrate/ui/bad-input-2.rs create mode 100644 futures/testcrate/ui/bad-input-2.stderr delete mode 100644 futures/testcrate/ui/forget-ok.rs delete mode 100644 futures/testcrate/ui/forget-ok.stderr create mode 100644 futures/testcrate/ui/forget-semicolon.rs create mode 100644 futures/testcrate/ui/forget-semicolon.stderr delete mode 100644 futures/testcrate/ui/missing-item.rs delete mode 100644 futures/testcrate/ui/missing-item.stderr create mode 100644 futures/testcrate/ui/nested.rs create mode 100644 futures/testcrate/ui/nested.stderr delete mode 100644 futures/testcrate/ui/not-a-result.rs delete mode 100644 futures/testcrate/ui/not-a-result.stderr create mode 100644 futures/tests/async_stream/elisions.rs rename futures/{tests_disabled/async_await => tests/async_stream}/mod.rs (75%) create mode 100644 futures/tests/async_stream/nested.rs create mode 100644 futures/tests/async_stream/pinned.rs create mode 100644 futures/tests/async_stream/smoke.rs create mode 100644 futures/tests/async_stream_tests.rs delete mode 100644 futures/tests_disabled/async_await/elisions.rs delete mode 100644 futures/tests_disabled/async_await/pinned.rs delete mode 100644 futures/tests_disabled/async_await/smoke.rs delete mode 100644 futures/tests_disabled/async_await_tests.rs diff --git a/.travis.yml b/.travis.yml index 2441997963..469b1daa93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -97,6 +97,13 @@ matrix: - cargo build --manifest-path futures-channel/Cargo.toml --no-default-features --features alloc - cargo build --manifest-path futures-util/Cargo.toml --no-default-features --features alloc + - name: cargo test (async-stream feature) + rust: nightly + script: + - cargo test --manifest-path futures/Cargo.toml --features async-stream,nightly --test async_stream_tests + - cargo clean --manifest-path futures/testcrate/Cargo.toml + - cargo test --manifest-path futures/testcrate/Cargo.toml + - name: cargo build --target=thumbv6m-none-eabi rust: nightly install: diff --git a/futures/testcrate/Cargo.toml b/futures/testcrate/Cargo.toml index 96ed23238c..13a6665dad 100644 --- a/futures/testcrate/Cargo.toml +++ b/futures/testcrate/Cargo.toml @@ -2,16 +2,17 @@ name = "testcrate" version = "0.1.0" authors = ["Alex Crichton "] +edition = "2018" +publish = false [lib] path = "lib.rs" -[dependencies.futures] -features = ["std", "nightly"] -path = ".." +[dependencies] +futures-preview = { path = "..", features = ["async-stream", "nightly"] } [dev-dependencies] -compiletest_rs = "0.3.7" +compiletest = { version = "0.3.21", package = "compiletest_rs", features = ["stable"] } [[test]] name = "ui" diff --git a/futures/testcrate/lib.rs b/futures/testcrate/lib.rs index e69de29bb2..8b13789179 100644 --- a/futures/testcrate/lib.rs +++ b/futures/testcrate/lib.rs @@ -0,0 +1 @@ + diff --git a/futures/testcrate/tests/ui.rs b/futures/testcrate/tests/ui.rs index dc1f49044a..fc4f116121 100644 --- a/futures/testcrate/tests/ui.rs +++ b/futures/testcrate/tests/ui.rs @@ -2,18 +2,24 @@ fn run_mode(mode: &'static str) { use std::env; use std::path::PathBuf; - let mut config = compiletest_rs::Config::default(); + let mut config = compiletest::Config::default(); config.mode = mode.parse().expect("invalid mode"); let mut me = env::current_exe().unwrap(); me.pop(); - config.target_rustcflags = Some(format!("-L {}", me.display())); + config.target_rustcflags = Some(format!( + "--edition=2018 \ + -Z unstable-options \ + --extern futures \ + -L {}", + me.display() + )); let src = PathBuf::from(env!("CARGO_MANIFEST_DIR")); config.src_base = src.join(mode); me.pop(); me.pop(); config.build_base = me.join("tests").join(mode); - compiletest_rs::run_tests(&config); + compiletest::run_tests(&config); } fn main() { diff --git a/futures/testcrate/ui/bad-input-1.rs b/futures/testcrate/ui/bad-input-1.rs new file mode 100644 index 0000000000..3087bb8720 --- /dev/null +++ b/futures/testcrate/ui/bad-input-1.rs @@ -0,0 +1,13 @@ +#![feature(async_await, futures_api, generators)] + +use futures::*; + +#[async_stream] +fn foo() -> i32 { + #[for_await] + for i in stream::iter(vec![1, 2]) { + stream_yield!(bar!!()); + } +} + +fn main() {} diff --git a/futures/testcrate/ui/bad-input-1.stderr b/futures/testcrate/ui/bad-input-1.stderr new file mode 100644 index 0000000000..a285c24da7 --- /dev/null +++ b/futures/testcrate/ui/bad-input-1.stderr @@ -0,0 +1,8 @@ +error: expected open delimiter + --> $DIR/bad-input-1.rs:9:27 + | +9 | stream_yield!(bar!!()); + | ^ expected open delimiter + +error: aborting due to previous error + diff --git a/futures/testcrate/ui/bad-input-2.rs b/futures/testcrate/ui/bad-input-2.rs new file mode 100644 index 0000000000..945414da9c --- /dev/null +++ b/futures/testcrate/ui/bad-input-2.rs @@ -0,0 +1,22 @@ + +#![feature(async_await, futures_api, generators)] + +use futures::*; + +#[async_stream] +fn foo() -> i32 { + #[for_await(bar)] + for i in stream::iter(vec![1, 2]) { + stream_yield!(i); + } +} + +#[async_stream(baz)] +fn bar() -> i32 { + #[for_await] + for i in stream::iter(vec![1, 2]) { + stream_yield!(i); + } +} + +fn main() {} diff --git a/futures/testcrate/ui/bad-input-2.stderr b/futures/testcrate/ui/bad-input-2.stderr new file mode 100644 index 0000000000..161240539a --- /dev/null +++ b/futures/testcrate/ui/bad-input-2.stderr @@ -0,0 +1,14 @@ +error: attribute must be of the form `#[for_await]` + --> $DIR/bad-input-2.rs:8:5 + | +8 | #[for_await(bar)] + | ^^^^^^^^^^^^^^^^^ + +error: attribute must be of the form `#[async_stream]` + --> $DIR/bad-input-2.rs:14:1 + | +14 | #[async_stream(baz)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/futures/testcrate/ui/bad-return-type.rs b/futures/testcrate/ui/bad-return-type.rs index 0fd2187064..fbaf06801a 100644 --- a/futures/testcrate/ui/bad-return-type.rs +++ b/futures/testcrate/ui/bad-return-type.rs @@ -1,33 +1,24 @@ -#![feature(proc_macro, generators)] +#![feature(async_await, futures_api, generators)] -#[async] -fn foobar() -> Result, ()> { - let val = Some(42); - if val.is_none() { - return Ok(None) - } - let val = val.unwrap(); - Ok(val) -} +use futures::*; -#[async_stream(item = Option)] -fn foobars() -> Result<(), ()> { +#[async_stream] +fn foobar() -> Option { let val = Some(42); if val.is_none() { stream_yield!(None); - return Ok(()) + return; } let val = val.unwrap(); stream_yield!(val); - Ok(()) } -#[async] -fn tuple() -> Result<(i32, i32), ()> { +#[async_stream] +fn tuple() -> (i32, i32) { if false { - return Ok(3); + stream_yield!(3); } - Ok((1, 2)) + stream_yield!((1, 2)) } fn main() {} diff --git a/futures/testcrate/ui/bad-return-type.stderr b/futures/testcrate/ui/bad-return-type.stderr index 1e71aa7ab8..5da8b2e357 100644 --- a/futures/testcrate/ui/bad-return-type.stderr +++ b/futures/testcrate/ui/bad-return-type.stderr @@ -1,90 +1,190 @@ error[E0308]: mismatched types - --> $DIR/bad-return-type.rs:14:8 + --> $DIR/bad-return-type.rs:13:19 | -14 | Ok(val) - | ^^^ - | | - | expected enum `std::option::Option`, found integral variable - | help: try using a variant of the expected type: `Some(val)` +13 | stream_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` + = note: expected type `std::option::Option<_>` found type `{integer}` -error[E0308]: mismatched types - --> $DIR/bad-return-type.rs:25:5 +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:7:9 + | +7 | let val = Some(42); + | ^^^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/bad-return-type.rs:13:5 + | +13| stream_yield!(val); + | ^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:12:9 + | +12 | let val = val.unwrap(); + | ^^^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/bad-return-type.rs:13:5 | -25 | stream_yield!(val); +13 | stream_yield!(val); | ^^^^^^^^^^^^^^^^^^^ - | | - | expected enum `std::option::Option`, found integral variable - | help: try using a variant of the expected type: `Some(e)` + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:13:19 | - = note: expected type `std::option::Option<_>` - found type `{integer}` +13 | stream_yield!(val); + | ^^^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/bad-return-type.rs:13:5 + | +13 | stream_yield!(val); + | ^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) -error[E0907]: type inside generator must be known in this context +error[E0308]: mismatched types + --> $DIR/bad-return-type.rs:21:19 + | +21 | stream_yield!((1, 2)) + | ^^^^^^ expected integer, found tuple + | + = note: expected type `{integer}` + found type `({integer}, {integer})` + +error[E0271]: type mismatch resolving `::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:19:9 | -19 | let val = Some(42); - | ^^^ +19 | stream_yield!(3); + | ^^^^^^^^^^^^^^^^^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:25:5 + --> $DIR/bad-return-type.rs:19:9 | -25 | stream_yield!(val); - | ^^^^^^^^^^^^^^^^^^^ +19 | stream_yield!(3); + | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) -error[E0907]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:24:9 +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:19:23 | -24 | let val = val.unwrap(); - | ^^^ +19 | stream_yield!(3); + | ^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:25:5 + --> $DIR/bad-return-type.rs:19:9 | -25 | stream_yield!(val); - | ^^^^^^^^^^^^^^^^^^^ +19 | stream_yield!(3); + | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) -error[E0907]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:25:5 +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:19:9 | -25 | stream_yield!(val); - | ^^^^^^^^^^^^^^^^^^^ +19 | stream_yield!(3); + | ^^^^^^^^^^^^^^^^^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:25:5 + --> $DIR/bad-return-type.rs:19:9 | -25 | stream_yield!(val); - | ^^^^^^^^^^^^^^^^^^^ +19 | stream_yield!(3); + | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) -error[E0907]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:25:5 +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:21:5 | -25 | stream_yield!(val); - | ^^^^^^^^^^^^^^^^^^^ +21 | stream_yield!((1, 2)) + | ^^^^^^^^^^^^^^^^^^^^^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:25:5 + --> $DIR/bad-return-type.rs:21:5 | -25 | stream_yield!(val); - | ^^^^^^^^^^^^^^^^^^^ +21 | stream_yield!((1, 2)) + | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) -error[E0308]: mismatched types - --> $DIR/bad-return-type.rs:32:19 +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:21:20 | -32 | return Ok(3); - | ^ expected tuple, found integral variable +21 | stream_yield!((1, 2)) + | ^ | - = note: expected type `(i32, i32)` - found type `{integer}` +note: the type is part of the generator because of this `yield` + --> $DIR/bad-return-type.rs:21:5 + | +21 | stream_yield!((1, 2)) + | ^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:21:23 + | +21 | stream_yield!((1, 2)) + | ^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/bad-return-type.rs:21:5 + | +21 | stream_yield!((1, 2)) + | ^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:21:19 + | +21 | stream_yield!((1, 2)) + | ^^^^^^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/bad-return-type.rs:21:5 + | +21 | stream_yield!((1, 2)) + | ^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:21:5 + | +21 | stream_yield!((1, 2)) + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/bad-return-type.rs:21:5 + | +21 | stream_yield!((1, 2)) + | ^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:16:1 + | +16 | #[async_stream] + | ^^^^^^^^^^^^^^^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/bad-return-type.rs:16:1 + | +16 | #[async_stream] + | ^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 15 previous errors -Some errors occurred: E0308, E0907. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/futures/testcrate/ui/forget-ok.rs b/futures/testcrate/ui/forget-ok.rs deleted file mode 100644 index eb86ce7f76..0000000000 --- a/futures/testcrate/ui/forget-ok.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(proc_macro, generators)] - -#[async] -fn foo() -> Result<(), ()> { -} - -#[async_stream(item = i32)] -fn foos() -> Result<(), ()> { -} - -fn main() {} diff --git a/futures/testcrate/ui/forget-ok.stderr b/futures/testcrate/ui/forget-ok.stderr deleted file mode 100644 index 3cb1ad5f34..0000000000 --- a/futures/testcrate/ui/forget-ok.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/forget-ok.rs:8:28 - | -8 | fn foo() -> Result<(), ()> { - | ____________________________^ -9 | | } - | |_^ expected enum `std::result::Result`, found () - | - = note: expected type `std::result::Result<(), ()>` - found type `()` - -error[E0308]: mismatched types - --> $DIR/forget-ok.rs:12:29 - | -12 | fn foos() -> Result<(), ()> { - | _____________________________^ -13 | | } - | |_^ expected enum `std::result::Result`, found () - | - = note: expected type `std::result::Result<(), ()>` - found type `()` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/futures/testcrate/ui/forget-semicolon.rs b/futures/testcrate/ui/forget-semicolon.rs new file mode 100644 index 0000000000..c01ad7e6af --- /dev/null +++ b/futures/testcrate/ui/forget-semicolon.rs @@ -0,0 +1,11 @@ +#![feature(async_await, futures_api, generators)] + +use futures::*; + +#[async_stream] +fn foo() { + stream_yield!(()); + Some(()) +} + +fn main() {} diff --git a/futures/testcrate/ui/forget-semicolon.stderr b/futures/testcrate/ui/forget-semicolon.stderr new file mode 100644 index 0000000000..1061bdc7b9 --- /dev/null +++ b/futures/testcrate/ui/forget-semicolon.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/forget-semicolon.rs:8:5 + | +8 | Some(()) + | ^^^^^^^^- help: try adding a semicolon: `;` + | | + | expected (), found enum `std::option::Option` + | + = note: expected type `()` + found type `std::option::Option<()>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/futures/testcrate/ui/missing-item.rs b/futures/testcrate/ui/missing-item.rs deleted file mode 100644 index 60972fbe43..0000000000 --- a/futures/testcrate/ui/missing-item.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![allow(warnings)] -#![feature(proc_macro, generators)] - -#[async_stream] -fn foos(a: String) -> Result<(), u32> { - Ok(()) -} - -fn main() {} diff --git a/futures/testcrate/ui/missing-item.stderr b/futures/testcrate/ui/missing-item.stderr deleted file mode 100644 index 2a446ba00b..0000000000 --- a/futures/testcrate/ui/missing-item.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: custom attribute panicked - --> $DIR/missing-item.rs:8:1 - | -8 | #[async_stream] - | ^^^^^^^^^^^^^^^ - | - = help: message: #[async_stream] requires item type to be specified - -error: aborting due to previous error - diff --git a/futures/testcrate/ui/move-captured-variable.rs b/futures/testcrate/ui/move-captured-variable.rs index cf14818633..202d32af75 100644 --- a/futures/testcrate/ui/move-captured-variable.rs +++ b/futures/testcrate/ui/move-captured-variable.rs @@ -1,12 +1,14 @@ -#![feature(proc_macro, generators)] +#![feature(async_await, futures_api, generators, proc_macro_hygiene)] + +use futures::*; fn foo(_f: F) {} fn main() { let a = String::new(); foo(|| { - async_block! { - Ok::(a) + async_stream_block! { + stream_yield!(a) }; }); } diff --git a/futures/testcrate/ui/move-captured-variable.stderr b/futures/testcrate/ui/move-captured-variable.stderr index 30828ab534..7b48d6fba9 100644 --- a/futures/testcrate/ui/move-captured-variable.stderr +++ b/futures/testcrate/ui/move-captured-variable.stderr @@ -1,13 +1,13 @@ -error[E0507]: cannot move out of captured outer variable in an `FnMut` closure - --> $DIR/move-captured-variable.rs:12:9 +error[E0507]: cannot move out of captured variable in an `FnMut` closure + --> $DIR/move-captured-variable.rs:10:9 | -10 | let a = String::new(); +8 | let a = String::new(); | - captured outer variable -11 | foo(|| { -12 | / async_block! { -13 | | Ok::(a) -14 | | }; - | |__________^ cannot move out of captured outer variable in an `FnMut` closure +9 | foo(|| { +10 | / async_stream_block! { +11 | | stream_yield!(a) +12 | | }; + | |__________^ cannot move out of captured variable in an `FnMut` closure error: aborting due to previous error diff --git a/futures/testcrate/ui/nested.rs b/futures/testcrate/ui/nested.rs new file mode 100644 index 0000000000..604bcd31cd --- /dev/null +++ b/futures/testcrate/ui/nested.rs @@ -0,0 +1,15 @@ +#![feature(async_await, futures_api, generators)] + +use futures::*; + +#[async_stream] +fn _stream1() -> i32 { + let _ = async { + #[for_await] + for i in stream::iter(vec![1, 2]) { + stream_yield!(i * i); + } + }; +} + +fn main() {} diff --git a/futures/testcrate/ui/nested.stderr b/futures/testcrate/ui/nested.stderr new file mode 100644 index 0000000000..dfbe4390fe --- /dev/null +++ b/futures/testcrate/ui/nested.stderr @@ -0,0 +1,20 @@ +error: stream_yield! cannot be allowed outside of async_stream blocks, and functions. + --> $DIR/nested.rs:10:13 + | +10 | stream_yield!(i * i); + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0698]: type inside generator must be known in this context + --> $DIR/nested.rs:5:1 + | +5 | #[async_stream] + | ^^^^^^^^^^^^^^^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/nested.rs:5:1 + | +5 | #[async_stream] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/futures/testcrate/ui/not-a-result.rs b/futures/testcrate/ui/not-a-result.rs deleted file mode 100644 index 8bbc52bf64..0000000000 --- a/futures/testcrate/ui/not-a-result.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![feature(proc_macro, generators)] - -#[async] -fn foo() -> u32 { - 3 -} - -#[async(boxed)] -fn bar() -> u32 { - 3 -} - -#[async_stream(item = u32)] -fn foos() -> u32 { - 3 -} - -#[async_stream(boxed, item = u32)] -fn bars() -> u32 { - 3 -} - -fn main() {} diff --git a/futures/testcrate/ui/not-a-result.stderr b/futures/testcrate/ui/not-a-result.stderr deleted file mode 100644 index 7f95faff36..0000000000 --- a/futures/testcrate/ui/not-a-result.stderr +++ /dev/null @@ -1,61 +0,0 @@ -error[E0277]: the trait bound `u32: futures::__rt::IsResult` is not satisfied - --> $DIR/not-a-result.rs:8:13 - | -8 | fn foo() -> u32 { - | ^^^ async functions must return a `Result` or a typedef of `Result` - | - = help: the trait `futures::__rt::IsResult` is not implemented for `u32` - = note: required by `futures::__rt::gen_future` - -error[E0277]: the trait bound `u32: futures::__rt::IsResult` is not satisfied - --> $DIR/not-a-result.rs:13:17 - | -13 | fn bar() -> u32 { - | _________________^ -14 | | 3 -15 | | } - | |_^ async functions must return a `Result` or a typedef of `Result` - | - = help: the trait `futures::__rt::IsResult` is not implemented for `u32` - -error[E0277]: the trait bound `u32: futures::__rt::IsResult` is not satisfied - --> $DIR/not-a-result.rs:13:13 - | -13 | fn bar() -> u32 { - | ^^^ async functions must return a `Result` or a typedef of `Result` - | - = help: the trait `futures::__rt::IsResult` is not implemented for `u32` - = note: required by `futures::__rt::gen_future` - -error[E0277]: the trait bound `u32: futures::__rt::IsResult` is not satisfied - --> $DIR/not-a-result.rs:18:14 - | -18 | fn foos() -> u32 { - | ^^^ async functions must return a `Result` or a typedef of `Result` - | - = help: the trait `futures::__rt::IsResult` is not implemented for `u32` - = note: required by `futures::__rt::gen_stream` - -error[E0277]: the trait bound `u32: futures::__rt::IsResult` is not satisfied - --> $DIR/not-a-result.rs:23:18 - | -23 | fn bars() -> u32 { - | __________________^ -24 | | 3 -25 | | } - | |_^ async functions must return a `Result` or a typedef of `Result` - | - = help: the trait `futures::__rt::IsResult` is not implemented for `u32` - -error[E0277]: the trait bound `u32: futures::__rt::IsResult` is not satisfied - --> $DIR/not-a-result.rs:23:14 - | -23 | fn bars() -> u32 { - | ^^^ async functions must return a `Result` or a typedef of `Result` - | - = help: the trait `futures::__rt::IsResult` is not implemented for `u32` - = note: required by `futures::__rt::gen_stream` - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/futures/testcrate/ui/type_error.rs b/futures/testcrate/ui/type_error.rs index 419bc4e630..202d38b6ae 100644 --- a/futures/testcrate/ui/type_error.rs +++ b/futures/testcrate/ui/type_error.rs @@ -1,9 +1,11 @@ -#![feature(proc_macro, generators)] +#![feature(async_await, futures_api, generators)] -#[async] -fn foo() -> Result { +use futures::*; + +#[async_stream] +fn foo() -> i32 { let a: i32 = "a"; //~ ERROR: mismatched types - Ok(1) + stream_yield!(1); } fn main() {} diff --git a/futures/testcrate/ui/type_error.stderr b/futures/testcrate/ui/type_error.stderr index efb4b224f6..4cac5e9608 100644 --- a/futures/testcrate/ui/type_error.stderr +++ b/futures/testcrate/ui/type_error.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types - --> $DIR/type_error.rs:9:18 + --> $DIR/type_error.rs:7:18 | -9 | let a: i32 = "a"; //~ ERROR: mismatched types +7 | let a: i32 = "a"; //~ ERROR: mismatched types | ^^^ expected i32, found reference | = note: expected type `i32` diff --git a/futures/testcrate/ui/unresolved-type.rs b/futures/testcrate/ui/unresolved-type.rs index 00317b1f73..0046815e04 100644 --- a/futures/testcrate/ui/unresolved-type.rs +++ b/futures/testcrate/ui/unresolved-type.rs @@ -1,13 +1,8 @@ -#![feature(proc_macro, generators)] +#![feature(async_await, futures_api, generators)] -#[async] -fn foo() -> Result { - Err(3) -} +use futures::*; -#[async_stream(item = Left)] -fn foos() -> Result<(), u32> { - Err(3) -} +#[async_stream] +fn foo() -> Left {} fn main() {} diff --git a/futures/testcrate/ui/unresolved-type.stderr b/futures/testcrate/ui/unresolved-type.stderr index da37f5757f..2888d16098 100644 --- a/futures/testcrate/ui/unresolved-type.stderr +++ b/futures/testcrate/ui/unresolved-type.stderr @@ -1,34 +1,20 @@ error[E0412]: cannot find type `Left` in this scope - --> $DIR/unresolved-type.rs:8:20 + --> $DIR/unresolved-type.rs:6:13 | -8 | fn foo() -> Result { - | ^^^^ not found in this scope +6 | fn foo() -> Left {} + | ^^^^ not found in this scope +help: there is an enum variant `core::fmt::Alignment::Left` and 7 others; try using the variant's enum | - = help: there is an enum variant `futures::future::Either::Left`, try using `futures::future::Either`? - = help: there is an enum variant `std::fmt::rt::v1::Alignment::Left`, try using `std::fmt::rt::v1::Alignment`? +6 | fn foo() -> core::fmt::Alignment {} + | ^^^^^^^^^^^^^^^^^^^^ +6 | fn foo() -> core::fmt::rt::v1::Alignment {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 | fn foo() -> futures::core_reexport::fmt::Alignment {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 | fn foo() -> futures::core_reexport::fmt::rt::v1::Alignment {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +and 3 other candidates -error[E0412]: cannot find type `Left` in this scope - --> $DIR/unresolved-type.rs:12:23 - | -12 | #[async_stream(item = Left)] - | ^^^^ not found in this scope - | - = help: there is an enum variant `futures::future::Either::Left`, try using `futures::future::Either`? - = help: there is an enum variant `std::fmt::rt::v1::Alignment::Left`, try using `std::fmt::rt::v1::Alignment`? - -error[E0907]: type inside generator must be known in this context - --> $DIR/unresolved-type.rs:12:1 - | -12 | #[async_stream(item = Left)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/unresolved-type.rs:12:1 - | -12 | #[async_stream(item = Left)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to previous error -Some errors occurred: E0412, E0907. -For more information about an error, try `rustc --explain E0412`. +For more information about this error, try `rustc --explain E0412`. diff --git a/futures/testcrate/ui/update-all-references.sh b/futures/testcrate/ui/update-all-references.sh index 7b757a4790..13545510c0 100755 --- a/futures/testcrate/ui/update-all-references.sh +++ b/futures/testcrate/ui/update-all-references.sh @@ -18,6 +18,6 @@ # # See all `update-references.sh`, if you just want to update a single test. -MY_DIR=$(dirname $0) +MY_DIR="$(dirname "${BASH_SOURCE[0]}")" cd $MY_DIR find . -name '*.rs' | xargs ./update-references.sh diff --git a/futures/testcrate/ui/update-references.sh b/futures/testcrate/ui/update-references.sh index 13c35dc044..67aa4f857e 100755 --- a/futures/testcrate/ui/update-references.sh +++ b/futures/testcrate/ui/update-references.sh @@ -19,9 +19,9 @@ # If you find yourself manually editing a foo.stderr file, you're # doing it wrong. -MYDIR=$(dirname $0) +MYDIR="$(dirname "${BASH_SOURCE[0]}")" -BUILD_DIR="../../target/tests/ui" +BUILD_DIR="../target/tests/ui" while [[ "$1" != "" ]]; do STDERR_NAME="${1/%.rs/.stderr}" @@ -38,5 +38,3 @@ while [[ "$1" != "" ]]; do cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME fi done - - diff --git a/futures/tests/async_stream/elisions.rs b/futures/tests/async_stream/elisions.rs new file mode 100644 index 0000000000..9d3eafe614 --- /dev/null +++ b/futures/tests/async_stream/elisions.rs @@ -0,0 +1,66 @@ +use futures::executor::block_on; +use futures::*; + +struct Ref<'a, T>(&'a T); + +#[async_stream] +fn references(x: &i32) -> i32 { + stream_yield!(*x); +} + +#[async_stream] +fn new_types(x: Ref<'_, i32>) -> i32 { + stream_yield!(*x.0); +} + +struct Foo(i32); + +impl Foo { + #[async_stream] + fn foo(&self) -> &i32 { + stream_yield!(&self.0) + } +} + +#[async_stream] +fn single_ref(x: &i32) -> &i32 { + stream_yield!(x) +} + +#[async_stream] +fn check_for_name_colision<'_async0, T>(_x: &T, _y: &'_async0 i32) { + stream_yield!(()) +} + +#[test] +fn main() { + block_on(async { + let x = 0; + let foo = Foo(x); + + #[for_await] + for y in references(&x) { + assert_eq!(y, x); + } + + #[for_await] + for y in new_types(Ref(&x)) { + assert_eq!(y, x); + } + + #[for_await] + for y in single_ref(&x) { + assert_eq!(y, &x); + } + + #[for_await] + for y in foo.foo() { + assert_eq!(y, &x); + } + + #[for_await] + for y in check_for_name_colision(&x, &x) { + assert_eq!(y, ()); + } + }); +} diff --git a/futures/tests_disabled/async_await/mod.rs b/futures/tests/async_stream/mod.rs similarity index 75% rename from futures/tests_disabled/async_await/mod.rs rename to futures/tests/async_stream/mod.rs index e7c8866eab..2791fdc408 100644 --- a/futures/tests_disabled/async_await/mod.rs +++ b/futures/tests/async_stream/mod.rs @@ -1,3 +1,4 @@ mod elisions; +mod nested; mod pinned; mod smoke; diff --git a/futures/tests/async_stream/nested.rs b/futures/tests/async_stream/nested.rs new file mode 100644 index 0000000000..dfa22db216 --- /dev/null +++ b/futures/tests/async_stream/nested.rs @@ -0,0 +1,13 @@ + +use futures::*; + +#[async_stream] // impl Generator, Return = ()> +fn _stream1() -> i32 { + let _ = async { // impl Generator + #[for_await] + for i in stream::iter(vec![1, 2]) { + await!(future::lazy(|_| i * i)); + } + }; + await!(future::lazy(|_| ())); +} diff --git a/futures/tests/async_stream/pinned.rs b/futures/tests/async_stream/pinned.rs new file mode 100644 index 0000000000..42a271b678 --- /dev/null +++ b/futures/tests/async_stream/pinned.rs @@ -0,0 +1,45 @@ +use futures::executor::block_on; +use futures::*; + +#[async_stream] +fn stream1() -> u64 { + fn integer() -> u64 { + 1 + } + let x = &integer(); + stream_yield!(0); + stream_yield!(*x); +} + +fn stream1_block() -> impl Stream { + async_stream_block! { + #[for_await] + for item in stream1() { + stream_yield!(item) + } + } +} + +async fn uses_async_for() -> Vec { + let mut v = vec![]; + #[for_await] + for i in stream1() { + v.push(i); + } + v +} + +async fn uses_async_for_block() -> Vec { + let mut v = vec![]; + #[for_await] + for i in stream1_block() { + v.push(i); + } + v +} + +#[test] +fn main() { + assert_eq!(block_on(uses_async_for()), vec![0, 1]); + assert_eq!(block_on(uses_async_for_block()), vec![0, 1]); +} diff --git a/futures/tests/async_stream/smoke.rs b/futures/tests/async_stream/smoke.rs new file mode 100644 index 0000000000..fc479ac213 --- /dev/null +++ b/futures/tests/async_stream/smoke.rs @@ -0,0 +1,147 @@ +//! A bunch of ways to use async/await syntax. +//! +//! This is mostly a test f r this repository itself, not necessarily serving +//! much more purpose than that. + +use futures::executor::block_on; +use futures::*; + +async fn future1() -> i32 { + let mut cnt = 0; + #[for_await] + for x in stream::iter(vec![1, 2, 3, 4]) { + cnt += x; + } + cnt +} + +#[async_stream] +fn stream1() -> u64 { + stream_yield!(0); + stream_yield!(1); +} + +#[async_stream] +fn stream2(t: T) -> T { + stream_yield!(t.clone()); + stream_yield!(t.clone()); +} + +#[async_stream] +fn stream3() -> i32 { + let mut cnt = 0; + #[for_await] + for x in stream::iter(vec![1, 2, 3, 4]) { + cnt += x; + stream_yield!(x); + } + stream_yield!(cnt); +} + +mod foo { + pub struct _Foo(pub i32); +} + +#[async_stream] +fn _stream5() -> foo::_Foo { + stream_yield!(foo::_Foo(0)); + stream_yield!(foo::_Foo(1)); +} + +#[async_stream] +fn _stream6() -> i32 { + #[for_await] + for foo::_Foo(i) in _stream5() { + stream_yield!(i * i); + } +} + +#[async_stream] +fn _stream7() -> () { + stream_yield!(()); +} + +#[async_stream] +fn _stream8() -> [u32; 4] { + stream_yield!([1, 2, 3, 4]); +} + +struct A(i32); + +impl A { + #[async_stream] + fn a_foo(self) -> i32 { + stream_yield!(self.0) + } + + #[async_stream] + fn _a_foo2(self: Box) -> i32 { + stream_yield!(self.0) + } +} + +async fn loop_in_loop() -> bool { + let mut cnt = 0; + let vec = vec![1, 2, 3, 4]; + #[for_await] + for x in stream::iter(vec.clone()) { + #[for_await] + for y in stream::iter(vec.clone()) { + cnt += x * y; + } + } + + let sum = (1..5).map(|x| (1..5).map(|y| x * y).sum::()).sum::(); + cnt == sum +} + +fn await_item_stream() -> impl Stream { + stream::iter(vec![0, 1]) +} + +async fn test_await_item() { + let mut stream = await_item_stream(); + + assert_eq!(await_item!(&mut stream), Some(0)); + assert_eq!(await_item!(&mut stream), Some(1)); + assert_eq!(await_item!(&mut stream), None); + assert_eq!(await_item!(&mut stream), None); +} + +#[test] +fn main() { + // https://github.com/alexcrichton/futures-await/issues/45 + #[async_stream] + fn _stream10() { + stream_yield!(()); + } + + block_on(async { + let mut v = 0..=1; + #[for_await] + for x in stream1() { + assert_eq!(x, v.next().unwrap()); + } + + let mut v = [(), ()].iter(); + #[for_await] + for x in stream2(()) { + assert_eq!(x, *v.next().unwrap()); + } + + let mut v = [1, 2, 3, 4, 10].iter(); + #[for_await] + for x in stream3() { + assert_eq!(x, *v.next().unwrap()); + } + + #[for_await] + for x in A(11).a_foo() { + assert_eq!(x, 11); + } + }); + + assert_eq!(block_on(future1()), 10); + assert_eq!(block_on(loop_in_loop()), true); + assert_eq!(block_on(test_await_item()), ()); +} diff --git a/futures/tests/async_stream_tests.rs b/futures/tests/async_stream_tests.rs new file mode 100644 index 0000000000..e2d3e475d2 --- /dev/null +++ b/futures/tests/async_stream_tests.rs @@ -0,0 +1,5 @@ +#![feature(async_await, await_macro, futures_api)] +#![cfg_attr(all(feature = "async-stream", feature = "nightly"), feature(generators, stmt_expr_attributes, proc_macro_hygiene))] + +#[cfg(all(feature = "async-stream", feature = "nightly"))] +mod async_stream; diff --git a/futures/tests_disabled/async_await/elisions.rs b/futures/tests_disabled/async_await/elisions.rs deleted file mode 100644 index 15c3a88ca0..0000000000 --- a/futures/tests_disabled/async_await/elisions.rs +++ /dev/null @@ -1,49 +0,0 @@ -use futures::stable::block_on_stable; - -struct Ref<'a, T: 'a>(&'a T); - -#[async] -fn references(x: &i32) -> Result { - Ok(*x) -} - -#[async] -fn new_types(x: Ref<'_, i32>) -> Result { - Ok(*x.0) -} - -#[async_stream(item = i32)] -fn _streams(x: &i32) -> Result<(), i32> { - stream_yield!(*x); - Ok(()) -} - -struct Foo(i32); - -impl Foo { - #[async] - fn foo(&self) -> Result<&i32, i32> { - Ok(&self.0) - } -} - -#[async] -fn single_ref(x: &i32) -> Result<&i32, i32> { - Ok(x) -} - -#[async] -fn check_for_name_colision<'_async0, T>(_x: &T, _y: &'_async0 i32) -> Result<(), ()> { - Ok(()) -} - -#[test] -fn main() { - let x = 0; - let foo = Foo(x); - assert_eq!(block_on_stable(references(&x)), Ok(x)); - assert_eq!(block_on_stable(new_types(Ref(&x))), Ok(x)); - assert_eq!(block_on_stable(single_ref(&x)), Ok(&x)); - assert_eq!(block_on_stable(foo.foo()), Ok(&x)); - assert_eq!(block_on_stable(check_for_name_colision(&x, &x)), Ok(())); -} diff --git a/futures/tests_disabled/async_await/pinned.rs b/futures/tests_disabled/async_await/pinned.rs deleted file mode 100644 index 39357c4274..0000000000 --- a/futures/tests_disabled/async_await/pinned.rs +++ /dev/null @@ -1,116 +0,0 @@ -use futures::stable::block_on_stable; -use futures::executor::{block_on, ThreadPool}; - -#[async] -fn foo() -> Result { - Ok(1) -} - -#[async] -fn bar(x: &i32) -> Result { - Ok(*x) -} - -#[async] -fn baz(x: i32) -> Result { - bar(&x).await -} - -#[async(boxed)] -fn boxed(x: i32) -> Result { - Ok(x) -} - -#[async(boxed)] -fn boxed_borrow(x: &i32) -> Result { - Ok(*x) -} - -#[async(boxed, send)] -fn boxed_send(x: i32) -> Result { - Ok(x) -} - -#[async(boxed, send)] -fn boxed_send_borrow(x: &i32) -> Result { - Ok(*x) -} - -#[async(boxed, send)] -fn spawnable() -> Result<(), Never> { - Ok(()) -} - -fn baz_block(x: i32) -> impl StableFuture { - async_block! { - bar(&x).await - } -} - -#[async_stream(item = u64)] -fn stream1() -> Result<(), i32> { - fn integer() -> u64 { 1 } - let x = &integer(); - stream_yield!(0); - stream_yield!(*x); - Ok(()) -} - -fn stream1_block() -> impl StableStream { - async_stream_block! { - #[async] - for item in stream1() { - stream_yield!(item) - } - Ok(()) - } -} - -#[async_stream(boxed, item = u64)] -fn _stream_boxed() -> Result<(), i32> { - fn integer() -> u64 { 1 } - let x = &integer(); - stream_yield!(0); - stream_yield!(*x); - Ok(()) -} - -#[async] -pub fn uses_async_for() -> Result, i32> { - let mut v = vec![]; - #[async] - for i in stream1() { - v.push(i); - } - Ok(v) -} - -#[async] -pub fn uses_async_for_block() -> Result, i32> { - let mut v = vec![]; - #[async] - for i in stream1_block() { - v.push(i); - } - Ok(v) -} - -#[test] -fn main() { - assert_eq!(block_on_stable(foo()), Ok(1)); - assert_eq!(block_on_stable(bar(&1)), Ok(1)); - assert_eq!(block_on_stable(baz(17)), Ok(17)); - assert_eq!(block_on(boxed(17)), Ok(17)); - assert_eq!(block_on(boxed_send(17)), Ok(17)); - assert_eq!(block_on(boxed_borrow(&17)), Ok(17)); - assert_eq!(block_on(boxed_send_borrow(&17)), Ok(17)); - assert_eq!(block_on_stable(baz_block(18)), Ok(18)); - assert_eq!(block_on_stable(uses_async_for()), Ok(vec![0, 1])); - assert_eq!(block_on_stable(uses_async_for_block()), Ok(vec![0, 1])); -} - -#[test] -fn run_pinned_future_in_thread_pool() { - let mut pool = ThreadPool::new().unwrap(); - pool.spawn_pinned(spawnable()).unwrap(); -} diff --git a/futures/tests_disabled/async_await/smoke.rs b/futures/tests_disabled/async_await/smoke.rs deleted file mode 100644 index 86566eb7f6..0000000000 --- a/futures/tests_disabled/async_await/smoke.rs +++ /dev/null @@ -1,223 +0,0 @@ -//! A bunch of ways to use async/await syntax. -//! -//! This is mostly a test f r this repository itself, not necessarily serving -//! much more purpose than that. - -use futures; -use futures::executor; -use futures::stable::block_on_stable; - -use std::io; - -use futures::Never; -use futures::future::poll_fn; - -#[async] -fn foo() -> Result { - Ok(1) -} - -#[async] -extern fn _foo1() -> Result { - Ok(1) -} - -#[async] -unsafe fn _foo2() -> io::Result { - Ok(1) -} - -#[async] -unsafe extern fn _foo3() -> io::Result { - Ok(1) -} - -#[async] -pub fn _foo4() -> io::Result { - Ok(1) -} - -#[async] -fn _foo5(t: T) -> Result { - Ok(t.clone()) -} - -#[async] -fn _foo6(ref a: i32) -> Result { - Err(*a) -} - -#[async] -fn _foo7(t: T) -> Result - where T: Clone + 'static, -{ - Ok(t.clone()) -} - -#[async(boxed)] -fn _foo8(a: i32, b: i32) -> Result { - return Ok(a + b) -} - -#[async(boxed, send)] -fn _foo9() -> Result<(), Never> { - Ok(()) -} - -#[async] -fn _bar() -> Result { - foo().await -} - -#[async] -fn _bar2() -> Result { - let a = foo().await?; - let b = foo().await?; - Ok(a + b) -} - -#[async] -fn _bar4() -> Result { - let mut cnt = 0; - #[async] - for x in futures::stream::iter_ok::<_, i32>(vec![1, 2, 3, 4]) { - cnt += x; - } - Ok(cnt) -} - -#[async_stream(item = u64)] -fn _stream1() -> Result<(), i32> { - stream_yield!(0); - stream_yield!(1); - Ok(()) -} - -#[async_stream(item = T)] -fn _stream2(t: T) -> Result<(), i32> { - stream_yield!(t.clone()); - stream_yield!(t.clone()); - Ok(()) -} - -#[async_stream(item = i32)] -fn _stream3() -> Result<(), i32> { - let mut cnt = 0; - #[async] - for x in futures::stream::iter_ok::<_, i32>(vec![1, 2, 3, 4]) { - cnt += x; - stream_yield!(x); - } - Err(cnt) -} - -#[async_stream(boxed, item = u64)] -fn _stream4() -> Result<(), i32> { - stream_yield!(0); - stream_yield!(1); - Ok(()) -} - -#[allow(dead_code)] -mod foo { pub struct Foo(pub i32); } - -#[allow(dead_code)] -#[async_stream(boxed, item = foo::Foo)] -pub fn stream5() -> Result<(), i32> { - stream_yield!(foo::Foo(0)); - stream_yield!(foo::Foo(1)); - Ok(()) -} - -#[async_stream(boxed, item = i32)] -pub fn _stream6() -> Result<(), i32> { - #[async] - for foo::Foo(i) in stream5() { - stream_yield!(i * i); - } - Ok(()) -} - -#[async_stream(item = ())] -pub fn _stream7() -> Result<(), i32> { - stream_yield!(()); - Ok(()) -} - -#[async_stream(item = [u32; 4])] -pub fn _stream8() -> Result<(), i32> { - stream_yield!([1, 2, 3, 4]); - Ok(()) -} - -struct A(i32); - -impl A { - #[async] - fn a_foo(self) -> Result { - Ok(self.0) - } - - #[async] - fn _a_foo2(self: Box) -> Result { - Ok(self.0) - } -} - -fn await_item_stream() -> impl Stream + Send { - ::futures::stream::iter_ok(vec![0, 1]) -} - -#[async] -fn test_await_item() -> Result<(), Never> { - let mut stream = await_item_stream(); - - assert_eq!(await_item!(stream), Ok(Some(0))); - assert_eq!(await_item!(stream), Ok(Some(1))); - assert_eq!(await_item!(stream), Ok(None)); - - Ok(()) -} - -#[test] -fn main() { - assert_eq!(block_on_stable(foo()), Ok(1)); - assert_eq!(block_on_stable(foo()), Ok(1)); - assert_eq!(block_on_stable(_bar()), Ok(1)); - assert_eq!(block_on_stable(_bar2()), Ok(2)); - assert_eq!(block_on_stable(_bar4()), Ok(10)); - assert_eq!(block_on_stable(_foo6(8)), Err(8)); - assert_eq!(block_on_stable(A(11).a_foo()), Ok(11)); - assert_eq!(block_on_stable(loop_in_loop()), Ok(true)); - assert_eq!(block_on_stable(test_await_item()), Ok(())); -} - -#[async] -fn loop_in_loop() -> Result { - let mut cnt = 0; - let vec = vec![1, 2, 3, 4]; - #[async] - for x in futures::stream::iter_ok::<_, i32>(vec.clone()) { - #[async] - for y in futures::stream::iter_ok::<_, i32>(vec.clone()) { - cnt += x * y; - } - } - - let sum = (1..5).map(|x| (1..5).map(|y| x * y).sum::()).sum::(); - Ok(cnt == sum) -} - -#[async_stream(item = i32)] -fn poll_stream_after_error_stream() -> Result<(), ()> { - stream_yield!(5); - Err(()) -} - -#[test] -fn poll_stream_after_error() { - let mut s = poll_stream_after_error_stream().pin(); - assert_eq!(executor::block_on(poll_fn(|ctx| s.poll_next(ctx))), Ok(Some(5))); - assert_eq!(executor::block_on(poll_fn(|ctx| s.poll_next(ctx))), Err(())); - assert_eq!(executor::block_on(poll_fn(|ctx| s.poll_next(ctx))), Ok(None)); -} diff --git a/futures/tests_disabled/async_await_tests.rs b/futures/tests_disabled/async_await_tests.rs deleted file mode 100644 index b3d59f5849..0000000000 --- a/futures/tests_disabled/async_await_tests.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![cfg_attr(feature = "nightly", feature(proc_macro, generators))] - -#[cfg(feature = "nightly")] -mod async_await; From fcc8b1fdd9ed7a5602f751b397843d1417ba1295 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 24 Apr 2019 21:32:11 +0900 Subject: [PATCH 03/15] Remove await_item! macro --- futures-async-macro/src/lib.rs | 19 +++------ futures-util/src/async_stream/await_item.rs | 46 --------------------- futures-util/src/async_stream/mod.rs | 4 -- futures/src/lib.rs | 2 +- futures/tests/async_stream/smoke.rs | 14 ------- 5 files changed, 7 insertions(+), 78 deletions(-) delete mode 100644 futures-util/src/async_stream/await_item.rs diff --git a/futures-async-macro/src/lib.rs b/futures-async-macro/src/lib.rs index 88249f5bbe..321357b545 100644 --- a/futures-async-macro/src/lib.rs +++ b/futures-async-macro/src/lib.rs @@ -207,7 +207,7 @@ pub fn async_stream_block(input: TokenStream) -> TokenStream { tokens.into() } -/// The scope in which `#[for_await]`, `await!` and `await_item!` was called. +/// The scope in which `#[for_await]`, `await!` was called. /// /// The type of generator depends on which scope is called. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -218,7 +218,7 @@ enum Scope { Stream, /// `static move ||`, `||` /// - /// It cannot call `#[for_await]`, `await!` and `await_item!` in this scope. + /// It cannot call `#[for_await]`, `await!` in this scope. Closure, } @@ -297,13 +297,7 @@ impl Expand { /// Expands a macro. fn expand_macro(&mut self, mut expr: ExprMacro) -> Expr { if self.0 == Stream && expr.mac.path.is_ident("await") { - return self.expand_await_macros(expr, "poll_with_tls_context"); - } else if expr.mac.path.is_ident("await_item") { - match self.0 { - Stream => return self.expand_await_macros(expr, "poll_next_with_tls_context"), - Closure => return outside_of_async_error!(expr, "await_item!"), - Future => {} - } + return self.expand_await_macros(expr); } else if expr.mac.path.is_ident("async_stream_block") { // FIXME: When added Parse impl for ExprCall, replace `if let ..` + `unreachable!()` // with `let` + `.unwrap()` @@ -322,16 +316,15 @@ impl Expand { Expr::Macro(expr) } - /// Expands `await!(expr)` or `await_item!(expr)` in `async_stream` scope. + /// Expands `await!(expr)` in `async_stream` scope. /// /// It needs to adjust the type yielded by the macro because generators used internally by /// async fn yield `()` type, but generators used internally by `async_stream` yield /// `Poll` type. - fn expand_await_macros(&mut self, expr: ExprMacro, poll_fn: &str) -> Expr { + fn expand_await_macros(&mut self, expr: ExprMacro) -> Expr { assert_eq!(self.0, Stream); let expr = expr.mac.tts; - let poll_fn = Ident::new(poll_fn, Span::call_site()); // Because macro input (`#expr`) is untrusted, use `syn::parse2` + `expr_compile_error` // instead of `syn::parse_quote!` to generate better error messages (`syn::parse_quote!` @@ -340,7 +333,7 @@ impl Expand { let mut __pinned = #expr; loop { if let ::futures::core_reexport::task::Poll::Ready(x) = - ::futures::async_stream::#poll_fn(unsafe { + ::futures::async_stream::poll_with_tls_context(unsafe { ::futures::core_reexport::pin::Pin::new_unchecked(&mut __pinned) }) { diff --git a/futures-util/src/async_stream/await_item.rs b/futures-util/src/async_stream/await_item.rs deleted file mode 100644 index 6f56c3b705..0000000000 --- a/futures-util/src/async_stream/await_item.rs +++ /dev/null @@ -1,46 +0,0 @@ -/// Await an item from a stream inside an `async` function. -/// -/// You should pass an object implementing [`Stream`] to this macro, it will -/// implicitly `yield` while that stream returns [`Poll::Pending`] and evaluate -/// to a [`Result`] containing the next item or error from that stream. -/// -/// If you want to iterate over all items in a `Stream` you should instead see -/// the documentation on `#[async] for` in the main `#[async]` documentation. -/// -/// # Examples -/// -/// ```no-run -/// #![feature(futures_api, async_await, generators)] -/// use futures::prelude::*; -/// use futures::executor::block_on; -/// use futures::await_item; -/// -/// async fn eventually_ten() -> u32 { -/// let mut stream = stream::repeat(5); -/// if let Some(first) = await_item!(&mut stream) { -/// if let Some(second) = await_item!(&mut stream) { -/// return first + second; -/// } -/// } -/// 0 -/// } -/// -/// assert_eq!(10, block_on(eventually_ten())); -/// ``` -#[macro_export] -macro_rules! await_item { - ($e:expr) => {{ - let mut pinned = $e; - loop { - if let $crate::core_reexport::task::Poll::Ready(x) = - $crate::async_stream::poll_next_with_tls_context(unsafe { - $crate::core_reexport::pin::Pin::new_unchecked(&mut pinned) - }) - { - break x; - } - - yield - } - }} -} diff --git a/futures-util/src/async_stream/mod.rs b/futures-util/src/async_stream/mod.rs index 6c599e75f5..2eedbc24fe 100644 --- a/futures-util/src/async_stream/mod.rs +++ b/futures-util/src/async_stream/mod.rs @@ -3,10 +3,6 @@ //! This module contains a number of functions and combinators for working //! with `async`/`await` code. -#[macro_use] -mod await_item; -pub use self::await_item::*; - #[macro_use] mod stream_yield; pub use self::stream_yield::*; diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 9cc899019e..dc69a7e5e0 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -77,7 +77,7 @@ pub use futures_util::{ }; // Async stream #[cfg(feature = "async-stream")] -pub use futures_util::{await_item, stream_yield}; +pub use futures_util::stream_yield; #[cfg(feature = "async-stream")] #[doc(hidden)] pub use futures_util::async_stream; diff --git a/futures/tests/async_stream/smoke.rs b/futures/tests/async_stream/smoke.rs index fc479ac213..9dae1e86f9 100644 --- a/futures/tests/async_stream/smoke.rs +++ b/futures/tests/async_stream/smoke.rs @@ -95,19 +95,6 @@ async fn loop_in_loop() -> bool { cnt == sum } -fn await_item_stream() -> impl Stream { - stream::iter(vec![0, 1]) -} - -async fn test_await_item() { - let mut stream = await_item_stream(); - - assert_eq!(await_item!(&mut stream), Some(0)); - assert_eq!(await_item!(&mut stream), Some(1)); - assert_eq!(await_item!(&mut stream), None); - assert_eq!(await_item!(&mut stream), None); -} - #[test] fn main() { // https://github.com/alexcrichton/futures-await/issues/45 @@ -143,5 +130,4 @@ fn main() { assert_eq!(block_on(future1()), 10); assert_eq!(block_on(loop_in_loop()), true); - assert_eq!(block_on(test_await_item()), ()); } From af456bae0d71233a5d8972396c537bbd3edf66d0 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 24 Apr 2019 21:33:26 +0900 Subject: [PATCH 04/15] Remove the _pin field from GenStream --- futures-util/src/async_stream/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/futures-util/src/async_stream/mod.rs b/futures-util/src/async_stream/mod.rs index 2eedbc24fe..a8ca435bc1 100644 --- a/futures-util/src/async_stream/mod.rs +++ b/futures-util/src/async_stream/mod.rs @@ -10,7 +10,7 @@ pub use self::stream_yield::*; use futures_core::future::Future; use futures_core::stream::Stream; use std::future::{self, get_task_context, set_task_context}; -use std::marker::{PhantomData, PhantomPinned}; +use std::marker::PhantomData; use std::ops::{Generator, GeneratorState}; use std::pin::Pin; use std::task::{Context, Poll}; @@ -23,7 +23,7 @@ pub fn from_generator(x: T) -> impl Stream where T: Generator, Return = ()>, { - GenStream { gen: x, done: false, _phantom: PhantomData, _pin: PhantomPinned } + GenStream { gen: x, done: false, _phantom: PhantomData } } /// A wrapper around generators used to implement `Stream` for `async`/`await` code. @@ -35,9 +35,6 @@ struct GenStream, Return = ()>> { // because it is possible to call `next` after `next` returns `None` in many iterators. done: bool, _phantom: PhantomData, - // We rely on the fact that async/await streams are immovable in order to create - // self-referential borrows in the underlying generator. - _pin: PhantomPinned, } impl, Return = ()>> Stream for GenStream { From debe7c49933224daae421aa685630117d4984932 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 25 Apr 2019 12:19:24 +0900 Subject: [PATCH 05/15] Remove stream_yield! macro --- futures-async-macro/README.md | 6 +- futures-async-macro/src/error.rs | 9 - futures-async-macro/src/lib.rs | 14 +- futures-util/src/async_stream/mod.rs | 4 - futures-util/src/async_stream/stream_yield.rs | 24 --- futures/src/lib.rs | 2 - futures/testcrate/ui/bad-input-1.rs | 13 -- futures/testcrate/ui/bad-input-1.stderr | 8 - .../ui/{bad-input-2.rs => bad-input.rs} | 4 +- .../{bad-input-2.stderr => bad-input.stderr} | 4 +- futures/testcrate/ui/bad-return-type.rs | 8 +- futures/testcrate/ui/bad-return-type.stderr | 157 ++++++------------ futures/testcrate/ui/forget-semicolon.rs | 2 +- .../testcrate/ui/move-captured-variable.rs | 2 +- .../ui/move-captured-variable.stderr | 2 +- futures/testcrate/ui/nested.rs | 2 +- futures/testcrate/ui/nested.stderr | 74 ++++++++- futures/testcrate/ui/type_error.rs | 2 +- futures/tests/async_stream/elisions.rs | 10 +- futures/tests/async_stream/pinned.rs | 6 +- futures/tests/async_stream/smoke.rs | 28 ++-- 21 files changed, 159 insertions(+), 222 deletions(-) delete mode 100644 futures-util/src/async_stream/stream_yield.rs delete mode 100644 futures/testcrate/ui/bad-input-1.rs delete mode 100644 futures/testcrate/ui/bad-input-1.stderr rename futures/testcrate/ui/{bad-input-2.rs => bad-input.rs} (84%) rename futures/testcrate/ui/{bad-input-2.stderr => bad-input.stderr} (81%) diff --git a/futures-async-macro/README.md b/futures-async-macro/README.md index 598bec6141..6753080ddd 100644 --- a/futures-async-macro/README.md +++ b/futures-async-macro/README.md @@ -34,18 +34,18 @@ This is a reimplement of [futures-await]'s `#[async_stream]` for futures 0.3 and ```rust use futures::prelude::*; -use futures::{async_stream, stream_yield}; +use futures::async_stream; // Returns a stream of i32 #[async_stream] fn foo(stream: impl Stream) -> i32 { #[for_await] for x in stream { - stream_yield!(x.parse().unwrap()); + 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 `stream_yield!` macro. +`#[async_stream]` have an item type specified via `-> some::Path` and the values output from the stream must be yielded via the `yield` expression. [futures-await]: https://github.com/alexcrichton/futures-await diff --git a/futures-async-macro/src/error.rs b/futures-async-macro/src/error.rs index 19534dd116..cfedc1105f 100644 --- a/futures-async-macro/src/error.rs +++ b/futures-async-macro/src/error.rs @@ -41,12 +41,3 @@ macro_rules! outside_of_async_error { )) }; } - -macro_rules! outside_of_async_stream_error { - ($tokens:expr, $name:expr) => { - $crate::error::expr_compile_error(&syn::Error::new_spanned( - $tokens, - concat!($name, " cannot be allowed outside of async_stream blocks, and functions."), - )) - }; -} diff --git a/futures-async-macro/src/lib.rs b/futures-async-macro/src/lib.rs index 321357b545..5a7422f324 100644 --- a/futures-async-macro/src/lib.rs +++ b/futures-async-macro/src/lib.rs @@ -10,8 +10,8 @@ use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree as TokenTree2}; use quote::{quote, ToTokens}; use syn::{ fold::{self, Fold}, - token, ArgCaptured, Error, Expr, ExprForLoop, ExprMacro, FnArg, FnDecl, Ident, Item, ItemFn, - Pat, PatIdent, ReturnType, TypeTuple, + token, ArgCaptured, Error, Expr, ExprForLoop, ExprMacro, ExprYield, FnArg, FnDecl, Ident, Item, + ItemFn, Pat, PatIdent, ReturnType, TypeTuple, }; #[macro_use] @@ -276,9 +276,6 @@ impl Expand { }} } - /* TODO: If this is enabled, it can be used for `yield` instead of `stream_yield!`. - Raw `yield` is simpler, but it may be preferable to distinguish it from `yield` called - in other scopes. /// Expands `yield expr` in `async_stream` scope. fn expand_yield(&self, expr: ExprYield) -> ExprYield { if self.0 != Stream { @@ -292,7 +289,6 @@ impl Expand { }; ExprYield { attrs, yield_token, expr: Some(Box::new(expr)) } } - */ /// Expands a macro. fn expand_macro(&mut self, mut expr: ExprMacro) -> Expr { @@ -307,10 +303,6 @@ impl Expand { } else { unreachable!() } - } else if self.0 != Stream && expr.mac.path.is_ident("stream_yield") { - // TODO: Should we remove real `stream_yield!` macro and replace `stream_yield!` call in - // here? -- or should we use `yield` instead of ``? - return outside_of_async_stream_error!(expr, "stream_yield!"); } Expr::Macro(expr) @@ -360,7 +352,7 @@ impl Fold for Expand { let expr = match fold::fold_expr(self, expr) { Expr::ForLoop(expr) => self.expand_for_await(expr), - // Expr::Yield(expr) => Expr::Yield(self.expand_yield(expr)), + Expr::Yield(expr) => Expr::Yield(self.expand_yield(expr)), Expr::Macro(expr) => self.expand_macro(expr), expr => expr, }; diff --git a/futures-util/src/async_stream/mod.rs b/futures-util/src/async_stream/mod.rs index a8ca435bc1..1391ab4a4a 100644 --- a/futures-util/src/async_stream/mod.rs +++ b/futures-util/src/async_stream/mod.rs @@ -3,10 +3,6 @@ //! This module contains a number of functions and combinators for working //! with `async`/`await` code. -#[macro_use] -mod stream_yield; -pub use self::stream_yield::*; - use futures_core::future::Future; use futures_core::stream::Stream; use std::future::{self, get_task_context, set_task_context}; diff --git a/futures-util/src/async_stream/stream_yield.rs b/futures-util/src/async_stream/stream_yield.rs deleted file mode 100644 index 9cabccf0e6..0000000000 --- a/futures-util/src/async_stream/stream_yield.rs +++ /dev/null @@ -1,24 +0,0 @@ -/// Yield an item from an `#[async_stream]` function. -/// -/// # Examples -/// -/// ```no-run -/// #![feature(futures_api, generators)] -/// use futures::prelude::*; -/// use futures::executor::block_on; -/// use futures::{async_stream, stream_yield}; -/// -/// #[async_stream] -/// fn one_five() -> u32 { -/// stream_yield!(1); -/// stream_yield!(5); -/// } -/// -/// assert_eq!(vec![1, 5], block_on(one_five().collect::>())); -/// ``` -#[macro_export] -macro_rules! stream_yield { - ($e:expr) => {{ - yield $crate::core_reexport::task::Poll::Ready($e) - }} -} diff --git a/futures/src/lib.rs b/futures/src/lib.rs index dc69a7e5e0..dff675eb84 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -77,8 +77,6 @@ pub use futures_util::{ }; // Async stream #[cfg(feature = "async-stream")] -pub use futures_util::stream_yield; -#[cfg(feature = "async-stream")] #[doc(hidden)] pub use futures_util::async_stream; #[cfg(feature = "async-stream")] diff --git a/futures/testcrate/ui/bad-input-1.rs b/futures/testcrate/ui/bad-input-1.rs deleted file mode 100644 index 3087bb8720..0000000000 --- a/futures/testcrate/ui/bad-input-1.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![feature(async_await, futures_api, generators)] - -use futures::*; - -#[async_stream] -fn foo() -> i32 { - #[for_await] - for i in stream::iter(vec![1, 2]) { - stream_yield!(bar!!()); - } -} - -fn main() {} diff --git a/futures/testcrate/ui/bad-input-1.stderr b/futures/testcrate/ui/bad-input-1.stderr deleted file mode 100644 index a285c24da7..0000000000 --- a/futures/testcrate/ui/bad-input-1.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected open delimiter - --> $DIR/bad-input-1.rs:9:27 - | -9 | stream_yield!(bar!!()); - | ^ expected open delimiter - -error: aborting due to previous error - diff --git a/futures/testcrate/ui/bad-input-2.rs b/futures/testcrate/ui/bad-input.rs similarity index 84% rename from futures/testcrate/ui/bad-input-2.rs rename to futures/testcrate/ui/bad-input.rs index 945414da9c..8c64129b49 100644 --- a/futures/testcrate/ui/bad-input-2.rs +++ b/futures/testcrate/ui/bad-input.rs @@ -7,7 +7,7 @@ use futures::*; fn foo() -> i32 { #[for_await(bar)] for i in stream::iter(vec![1, 2]) { - stream_yield!(i); + yield i; } } @@ -15,7 +15,7 @@ fn foo() -> i32 { fn bar() -> i32 { #[for_await] for i in stream::iter(vec![1, 2]) { - stream_yield!(i); + yield i; } } diff --git a/futures/testcrate/ui/bad-input-2.stderr b/futures/testcrate/ui/bad-input.stderr similarity index 81% rename from futures/testcrate/ui/bad-input-2.stderr rename to futures/testcrate/ui/bad-input.stderr index 161240539a..76a83516d1 100644 --- a/futures/testcrate/ui/bad-input-2.stderr +++ b/futures/testcrate/ui/bad-input.stderr @@ -1,11 +1,11 @@ error: attribute must be of the form `#[for_await]` - --> $DIR/bad-input-2.rs:8:5 + --> $DIR/bad-input.rs:8:5 | 8 | #[for_await(bar)] | ^^^^^^^^^^^^^^^^^ error: attribute must be of the form `#[async_stream]` - --> $DIR/bad-input-2.rs:14:1 + --> $DIR/bad-input.rs:14:1 | 14 | #[async_stream(baz)] | ^^^^^^^^^^^^^^^^^^^^ diff --git a/futures/testcrate/ui/bad-return-type.rs b/futures/testcrate/ui/bad-return-type.rs index fbaf06801a..6fc64107ae 100644 --- a/futures/testcrate/ui/bad-return-type.rs +++ b/futures/testcrate/ui/bad-return-type.rs @@ -6,19 +6,19 @@ use futures::*; fn foobar() -> Option { let val = Some(42); if val.is_none() { - stream_yield!(None); + yield None; return; } let val = val.unwrap(); - stream_yield!(val); + yield val; } #[async_stream] fn tuple() -> (i32, i32) { if false { - stream_yield!(3); + yield 3; } - stream_yield!((1, 2)) + yield (1, 2) } fn main() {} diff --git a/futures/testcrate/ui/bad-return-type.stderr b/futures/testcrate/ui/bad-return-type.stderr index 5da8b2e357..cd9d36e90f 100644 --- a/futures/testcrate/ui/bad-return-type.stderr +++ b/futures/testcrate/ui/bad-return-type.stderr @@ -1,11 +1,11 @@ error[E0308]: mismatched types - --> $DIR/bad-return-type.rs:13:19 + --> $DIR/bad-return-type.rs:13:11 | -13 | stream_yield!(val); - | ^^^ - | | - | expected enum `std::option::Option`, found integer - | help: try using a variant of the expected type: `Some(val)` +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}` @@ -17,11 +17,10 @@ error[E0698]: type inside generator must be known in this context | ^^^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:13:5 + --> $DIR/bad-return-type.rs:5:1 | -13| stream_yield!(val); - | ^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) +5 | #[async_stream] + | ^^^^^^^^^^^^^^^ error[E0698]: type inside generator must be known in this context --> $DIR/bad-return-type.rs:12:9 @@ -30,30 +29,28 @@ error[E0698]: type inside generator must be known in this context | ^^^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:13:5 + --> $DIR/bad-return-type.rs:5:1 | -13 | stream_yield!(val); - | ^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) +5 | #[async_stream] + | ^^^^^^^^^^^^^^^ error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:13:19 + --> $DIR/bad-return-type.rs:13:11 | -13 | stream_yield!(val); - | ^^^ +13 | yield val; + | ^^^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:13:5 + --> $DIR/bad-return-type.rs:5:1 | -13 | stream_yield!(val); - | ^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) +5 | #[async_stream] + | ^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/bad-return-type.rs:21:19 + --> $DIR/bad-return-type.rs:21:11 | -21 | stream_yield!((1, 2)) - | ^^^^^^ expected integer, found tuple +21 | yield (1, 2) + | ^^^^^^ expected integer, found tuple | = note: expected type `{integer}` found type `({integer}, {integer})` @@ -69,114 +66,58 @@ error[E0271]: type mismatch resolving ` $DIR/bad-return-type.rs:19:9 - | -19 | stream_yield!(3); - | ^^^^^^^^^^^^^^^^^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:19:9 - | -19 | stream_yield!(3); - | ^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) - -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:19:23 - | -19 | stream_yield!(3); - | ^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:19:9 - | -19 | stream_yield!(3); - | ^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) - -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:19:9 - | -19 | stream_yield!(3); - | ^^^^^^^^^^^^^^^^^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:19:9 - | -19 | stream_yield!(3); - | ^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) - -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:21:5 - | -21 | stream_yield!((1, 2)) - | ^^^^^^^^^^^^^^^^^^^^^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:21:5 - | -21 | stream_yield!((1, 2)) - | ^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) - -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:21:20 + --> $DIR/bad-return-type.rs:16:1 | -21 | stream_yield!((1, 2)) - | ^ +16 | #[async_stream] + | ^^^^^^^^^^^^^^^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:21:5 + --> $DIR/bad-return-type.rs:16:1 | -21 | stream_yield!((1, 2)) - | ^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) +16 | #[async_stream] + | ^^^^^^^^^^^^^^^ error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:21:23 + --> $DIR/bad-return-type.rs:19:15 | -21 | stream_yield!((1, 2)) - | ^ +19 | yield 3; + | ^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:21:5 + --> $DIR/bad-return-type.rs:16:1 | -21 | stream_yield!((1, 2)) - | ^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) +16 | #[async_stream] + | ^^^^^^^^^^^^^^^ error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:21:19 + --> $DIR/bad-return-type.rs:21:12 | -21 | stream_yield!((1, 2)) - | ^^^^^^ +21 | yield (1, 2) + | ^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:21:5 + --> $DIR/bad-return-type.rs:16:1 | -21 | stream_yield!((1, 2)) - | ^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) +16 | #[async_stream] + | ^^^^^^^^^^^^^^^ error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:21:5 + --> $DIR/bad-return-type.rs:21:15 | -21 | stream_yield!((1, 2)) - | ^^^^^^^^^^^^^^^^^^^^^ +21 | yield (1, 2) + | ^ | note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:21:5 - | -21 | stream_yield!((1, 2)) - | ^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) - -error[E0698]: type inside generator must be known in this context --> $DIR/bad-return-type.rs:16:1 | 16 | #[async_stream] | ^^^^^^^^^^^^^^^ + +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-return-type.rs:21:11 + | +21 | yield (1, 2) + | ^^^^^^ | note: the type is part of the generator because of this `yield` --> $DIR/bad-return-type.rs:16:1 @@ -184,7 +125,7 @@ note: the type is part of the generator because of this `yield` 16 | #[async_stream] | ^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: aborting due to 11 previous errors Some errors have detailed explanations: E0271, E0308. For more information about an error, try `rustc --explain E0271`. diff --git a/futures/testcrate/ui/forget-semicolon.rs b/futures/testcrate/ui/forget-semicolon.rs index c01ad7e6af..ccda64f725 100644 --- a/futures/testcrate/ui/forget-semicolon.rs +++ b/futures/testcrate/ui/forget-semicolon.rs @@ -4,7 +4,7 @@ use futures::*; #[async_stream] fn foo() { - stream_yield!(()); + yield; Some(()) } diff --git a/futures/testcrate/ui/move-captured-variable.rs b/futures/testcrate/ui/move-captured-variable.rs index 202d32af75..74e1754014 100644 --- a/futures/testcrate/ui/move-captured-variable.rs +++ b/futures/testcrate/ui/move-captured-variable.rs @@ -8,7 +8,7 @@ fn main() { let a = String::new(); foo(|| { async_stream_block! { - stream_yield!(a) + yield a }; }); } diff --git a/futures/testcrate/ui/move-captured-variable.stderr b/futures/testcrate/ui/move-captured-variable.stderr index 7b48d6fba9..bacd96c626 100644 --- a/futures/testcrate/ui/move-captured-variable.stderr +++ b/futures/testcrate/ui/move-captured-variable.stderr @@ -5,7 +5,7 @@ error[E0507]: cannot move out of captured variable in an `FnMut` closure | - captured outer variable 9 | foo(|| { 10 | / async_stream_block! { -11 | | stream_yield!(a) +11 | | yield a 12 | | }; | |__________^ cannot move out of captured variable in an `FnMut` closure diff --git a/futures/testcrate/ui/nested.rs b/futures/testcrate/ui/nested.rs index 604bcd31cd..762f658c79 100644 --- a/futures/testcrate/ui/nested.rs +++ b/futures/testcrate/ui/nested.rs @@ -7,7 +7,7 @@ fn _stream1() -> i32 { let _ = async { #[for_await] for i in stream::iter(vec![1, 2]) { - stream_yield!(i * i); + yield i * i; } }; } diff --git a/futures/testcrate/ui/nested.stderr b/futures/testcrate/ui/nested.stderr index dfbe4390fe..c437395e22 100644 --- a/futures/testcrate/ui/nested.stderr +++ b/futures/testcrate/ui/nested.stderr @@ -1,8 +1,11 @@ -error: stream_yield! cannot be allowed outside of async_stream blocks, and functions. - --> $DIR/nested.rs:10:13 +error[E0308]: mismatched types + --> $DIR/nested.rs:10:19 + | +10 | yield i * i; + | ^^^^^ expected (), found integer | -10 | stream_yield!(i * i); - | ^^^^^^^^^^^^^^^^^^^^^ + = note: expected type `()` + found type `{integer}` error[E0698]: type inside generator must be known in this context --> $DIR/nested.rs:5:1 @@ -11,10 +14,71 @@ error[E0698]: type inside generator must be known in this context | ^^^^^^^^^^^^^^^ | note: the type is part of the generator because of this `yield` + --> $DIR/nested.rs:10:13 + | +10| yield i * i; + | ^^^^^^^^^^^ + +error[E0698]: type inside generator must be known in this context --> $DIR/nested.rs:5:1 | 5 | #[async_stream] | ^^^^^^^^^^^^^^^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/nested.rs:5:1 + | +5 | #[async_stream] + | ^^^^^^^^^^^^^^^ + +error[E0698]: type inside generator must be known in this context + --> $DIR/nested.rs:9:13 + | +9 | for i in stream::iter(vec![1, 2]) { + | ^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/nested.rs:10:13 + | +10| yield i * i; + | ^^^^^^^^^^^ + +error[E0698]: type inside generator must be known in this context + --> $DIR/nested.rs:10:19 + | +10 | yield i * i; + | ^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/nested.rs:10:13 + | +10 | yield i * i; + | ^^^^^^^^^^^ + +error[E0698]: type inside generator must be known in this context + --> $DIR/nested.rs:10:23 + | +10 | yield i * i; + | ^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/nested.rs:10:13 + | +10 | yield i * i; + | ^^^^^^^^^^^ + +error[E0698]: type inside generator must be known in this context + --> $DIR/nested.rs:10:19 + | +10 | yield i * i; + | ^^^^^ + | +note: the type is part of the generator because of this `yield` + --> $DIR/nested.rs:10:13 + | +10 | yield i * i; + | ^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 7 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/futures/testcrate/ui/type_error.rs b/futures/testcrate/ui/type_error.rs index 202d38b6ae..3a1e827969 100644 --- a/futures/testcrate/ui/type_error.rs +++ b/futures/testcrate/ui/type_error.rs @@ -5,7 +5,7 @@ use futures::*; #[async_stream] fn foo() -> i32 { let a: i32 = "a"; //~ ERROR: mismatched types - stream_yield!(1); + yield 1; } fn main() {} diff --git a/futures/tests/async_stream/elisions.rs b/futures/tests/async_stream/elisions.rs index 9d3eafe614..03e648ff8f 100644 --- a/futures/tests/async_stream/elisions.rs +++ b/futures/tests/async_stream/elisions.rs @@ -5,12 +5,12 @@ struct Ref<'a, T>(&'a T); #[async_stream] fn references(x: &i32) -> i32 { - stream_yield!(*x); + yield *x; } #[async_stream] fn new_types(x: Ref<'_, i32>) -> i32 { - stream_yield!(*x.0); + yield *x.0; } struct Foo(i32); @@ -18,18 +18,18 @@ struct Foo(i32); impl Foo { #[async_stream] fn foo(&self) -> &i32 { - stream_yield!(&self.0) + yield &self.0 } } #[async_stream] fn single_ref(x: &i32) -> &i32 { - stream_yield!(x) + yield x } #[async_stream] fn check_for_name_colision<'_async0, T>(_x: &T, _y: &'_async0 i32) { - stream_yield!(()) + yield } #[test] diff --git a/futures/tests/async_stream/pinned.rs b/futures/tests/async_stream/pinned.rs index 42a271b678..d407262f90 100644 --- a/futures/tests/async_stream/pinned.rs +++ b/futures/tests/async_stream/pinned.rs @@ -7,15 +7,15 @@ fn stream1() -> u64 { 1 } let x = &integer(); - stream_yield!(0); - stream_yield!(*x); + yield 0; + yield *x; } fn stream1_block() -> impl Stream { async_stream_block! { #[for_await] for item in stream1() { - stream_yield!(item) + yield item } } } diff --git a/futures/tests/async_stream/smoke.rs b/futures/tests/async_stream/smoke.rs index 9dae1e86f9..c13bf57a3a 100644 --- a/futures/tests/async_stream/smoke.rs +++ b/futures/tests/async_stream/smoke.rs @@ -17,14 +17,14 @@ async fn future1() -> i32 { #[async_stream] fn stream1() -> u64 { - stream_yield!(0); - stream_yield!(1); + yield 0; + yield 1; } #[async_stream] fn stream2(t: T) -> T { - stream_yield!(t.clone()); - stream_yield!(t.clone()); + yield t.clone(); + yield t.clone(); } #[async_stream] @@ -33,9 +33,9 @@ fn stream3() -> i32 { #[for_await] for x in stream::iter(vec![1, 2, 3, 4]) { cnt += x; - stream_yield!(x); + yield x; } - stream_yield!(cnt); + yield cnt; } mod foo { @@ -44,26 +44,26 @@ mod foo { #[async_stream] fn _stream5() -> foo::_Foo { - stream_yield!(foo::_Foo(0)); - stream_yield!(foo::_Foo(1)); + yield foo::_Foo(0); + yield foo::_Foo(1); } #[async_stream] fn _stream6() -> i32 { #[for_await] for foo::_Foo(i) in _stream5() { - stream_yield!(i * i); + yield i * i; } } #[async_stream] fn _stream7() -> () { - stream_yield!(()); + yield (); } #[async_stream] fn _stream8() -> [u32; 4] { - stream_yield!([1, 2, 3, 4]); + yield [1, 2, 3, 4]; } struct A(i32); @@ -71,12 +71,12 @@ struct A(i32); impl A { #[async_stream] fn a_foo(self) -> i32 { - stream_yield!(self.0) + yield self.0 } #[async_stream] fn _a_foo2(self: Box) -> i32 { - stream_yield!(self.0) + yield self.0 } } @@ -100,7 +100,7 @@ fn main() { // https://github.com/alexcrichton/futures-await/issues/45 #[async_stream] fn _stream10() { - stream_yield!(()); + yield; } block_on(async { From 13abd7ea3e53caf2100fac1d571129be8460177f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 25 Apr 2019 12:20:01 +0900 Subject: [PATCH 06/15] Remove #![feature(futures_api)] --- futures/testcrate/ui/bad-input.rs | 2 +- futures/testcrate/ui/bad-return-type.rs | 2 +- futures/testcrate/ui/forget-semicolon.rs | 2 +- futures/testcrate/ui/move-captured-variable.rs | 2 +- futures/testcrate/ui/nested.rs | 2 +- futures/testcrate/ui/type_error.rs | 2 +- futures/testcrate/ui/unresolved-type.rs | 2 +- futures/tests/async_stream_tests.rs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/futures/testcrate/ui/bad-input.rs b/futures/testcrate/ui/bad-input.rs index 8c64129b49..5573a91b61 100644 --- a/futures/testcrate/ui/bad-input.rs +++ b/futures/testcrate/ui/bad-input.rs @@ -1,5 +1,5 @@ -#![feature(async_await, futures_api, generators)] +#![feature(async_await, generators)] use futures::*; diff --git a/futures/testcrate/ui/bad-return-type.rs b/futures/testcrate/ui/bad-return-type.rs index 6fc64107ae..d086fab3e0 100644 --- a/futures/testcrate/ui/bad-return-type.rs +++ b/futures/testcrate/ui/bad-return-type.rs @@ -1,4 +1,4 @@ -#![feature(async_await, futures_api, generators)] +#![feature(async_await, generators)] use futures::*; diff --git a/futures/testcrate/ui/forget-semicolon.rs b/futures/testcrate/ui/forget-semicolon.rs index ccda64f725..d15f648972 100644 --- a/futures/testcrate/ui/forget-semicolon.rs +++ b/futures/testcrate/ui/forget-semicolon.rs @@ -1,4 +1,4 @@ -#![feature(async_await, futures_api, generators)] +#![feature(async_await, generators)] use futures::*; diff --git a/futures/testcrate/ui/move-captured-variable.rs b/futures/testcrate/ui/move-captured-variable.rs index 74e1754014..a0c3106872 100644 --- a/futures/testcrate/ui/move-captured-variable.rs +++ b/futures/testcrate/ui/move-captured-variable.rs @@ -1,4 +1,4 @@ -#![feature(async_await, futures_api, generators, proc_macro_hygiene)] +#![feature(async_await, generators, proc_macro_hygiene)] use futures::*; diff --git a/futures/testcrate/ui/nested.rs b/futures/testcrate/ui/nested.rs index 762f658c79..bb5b04f56a 100644 --- a/futures/testcrate/ui/nested.rs +++ b/futures/testcrate/ui/nested.rs @@ -1,4 +1,4 @@ -#![feature(async_await, futures_api, generators)] +#![feature(async_await, generators)] use futures::*; diff --git a/futures/testcrate/ui/type_error.rs b/futures/testcrate/ui/type_error.rs index 3a1e827969..4e1f9e0b02 100644 --- a/futures/testcrate/ui/type_error.rs +++ b/futures/testcrate/ui/type_error.rs @@ -1,4 +1,4 @@ -#![feature(async_await, futures_api, generators)] +#![feature(async_await, generators)] use futures::*; diff --git a/futures/testcrate/ui/unresolved-type.rs b/futures/testcrate/ui/unresolved-type.rs index 0046815e04..fbf5ca5ddb 100644 --- a/futures/testcrate/ui/unresolved-type.rs +++ b/futures/testcrate/ui/unresolved-type.rs @@ -1,4 +1,4 @@ -#![feature(async_await, futures_api, generators)] +#![feature(async_await, generators)] use futures::*; diff --git a/futures/tests/async_stream_tests.rs b/futures/tests/async_stream_tests.rs index e2d3e475d2..d9d3cff6ef 100644 --- a/futures/tests/async_stream_tests.rs +++ b/futures/tests/async_stream_tests.rs @@ -1,4 +1,4 @@ -#![feature(async_await, await_macro, futures_api)] +#![feature(async_await, await_macro)] #![cfg_attr(all(feature = "async-stream", feature = "nightly"), feature(generators, stmt_expr_attributes, proc_macro_hygiene))] #[cfg(all(feature = "async-stream", feature = "nightly"))] From 508d486e42b8b8218ae269a24ed8f88dfe667ac9 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 28 Apr 2019 00:18:04 +0900 Subject: [PATCH 07/15] "async-await" feature --- futures-util/Cargo.toml | 2 +- futures/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index c56ff632a8..97ea4293f6 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -28,7 +28,7 @@ sink = ["futures-sink-preview"] io = ["std", "futures-io-preview", "memchr"] channel = ["std", "futures-channel-preview"] select-macro = ["async-await", "futures-select-macro-preview", "proc-macro-hack", "proc-macro-nested", "rand"] -async-stream = ["std"] +async-stream = ["std", "async-await"] [dependencies] futures-core-preview = { path = "../futures-core", version = "=0.3.0-alpha.17", default-features = false } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 683373305e..47d3b48784 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -45,7 +45,7 @@ async-await = ["futures-util-preview/async-await", "futures-util-preview/select- compat = ["std", "futures-util-preview/compat"] io-compat = ["compat", "futures-util-preview/io-compat"] cfg-target-has-atomic = ["futures-core-preview/cfg-target-has-atomic", "futures-channel-preview/cfg-target-has-atomic", "futures-util-preview/cfg-target-has-atomic"] -async-stream = ["std", "futures-util-preview/async-stream", "futures-async-macro-preview"] +async-stream = ["std", "async-await", "futures-util-preview/async-stream", "futures-async-macro-preview"] [package.metadata.docs.rs] all-features = true From c00b4060fc69c40656e740d70a220ab1e546e5f7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 28 Apr 2019 04:30:25 +0900 Subject: [PATCH 08/15] Update ui tests --- futures/testcrate/ui/bad-return-type.stderr | 84 ++------------------- futures/testcrate/ui/nested.stderr | 73 ++++-------------- 2 files changed, 19 insertions(+), 138 deletions(-) diff --git a/futures/testcrate/ui/bad-return-type.stderr b/futures/testcrate/ui/bad-return-type.stderr index cd9d36e90f..dd7790e44e 100644 --- a/futures/testcrate/ui/bad-return-type.stderr +++ b/futures/testcrate/ui/bad-return-type.stderr @@ -11,10 +11,10 @@ error[E0308]: mismatched types found type `{integer}` error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:7:9 + --> $DIR/bad-return-type.rs:5:1 | -7 | let val = Some(42); - | ^^^ +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 @@ -22,30 +22,6 @@ note: the type is part of the generator because of this `yield` 5 | #[async_stream] | ^^^^^^^^^^^^^^^ -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:12:9 - | -12 | let val = val.unwrap(); - | ^^^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:5:1 - | -5 | #[async_stream] - | ^^^^^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:13:11 - | -13 | yield val; - | ^^^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:5:1 - | -5 | #[async_stream] - | ^^^^^^^^^^^^^^^ - error[E0308]: mismatched types --> $DIR/bad-return-type.rs:21:11 | @@ -69,55 +45,7 @@ error[E0698]: type inside generator must be known in this context --> $DIR/bad-return-type.rs:16:1 | 16 | #[async_stream] - | ^^^^^^^^^^^^^^^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:16:1 - | -16 | #[async_stream] - | ^^^^^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:19:15 - | -19 | yield 3; - | ^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:16:1 - | -16 | #[async_stream] - | ^^^^^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:21:12 - | -21 | yield (1, 2) - | ^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:16:1 - | -16 | #[async_stream] - | ^^^^^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:21:15 - | -21 | yield (1, 2) - | ^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/bad-return-type.rs:16:1 - | -16 | #[async_stream] - | ^^^^^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/bad-return-type.rs:21:11 - | -21 | yield (1, 2) - | ^^^^^^ + | ^^^^^^^^^^^^^^^ cannot infer type for `{integer}` | note: the type is part of the generator because of this `yield` --> $DIR/bad-return-type.rs:16:1 @@ -125,7 +53,7 @@ note: the type is part of the generator because of this `yield` 16 | #[async_stream] | ^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0271, E0308. +Some errors have detailed explanations: E0271, E0308, E0698. For more information about an error, try `rustc --explain E0271`. diff --git a/futures/testcrate/ui/nested.stderr b/futures/testcrate/ui/nested.stderr index c437395e22..f6f5709f69 100644 --- a/futures/testcrate/ui/nested.stderr +++ b/futures/testcrate/ui/nested.stderr @@ -8,58 +8,10 @@ error[E0308]: mismatched types found type `{integer}` error[E0698]: type inside generator must be known in this context - --> $DIR/nested.rs:5:1 - | -5 | #[async_stream] - | ^^^^^^^^^^^^^^^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/nested.rs:10:13 - | -10| yield i * i; - | ^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/nested.rs:5:1 - | -5 | #[async_stream] - | ^^^^^^^^^^^^^^^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/nested.rs:5:1 - | -5 | #[async_stream] - | ^^^^^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/nested.rs:9:13 - | -9 | for i in stream::iter(vec![1, 2]) { - | ^ - | -note: the type is part of the generator because of this `yield` - --> $DIR/nested.rs:10:13 - | -10| yield i * i; - | ^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/nested.rs:10:19 - | -10 | yield i * i; - | ^ - | -note: the type is part of the generator because of this `yield` --> $DIR/nested.rs:10:13 | 10 | yield i * i; - | ^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/nested.rs:10:23 - | -10 | yield i * i; - | ^ + | ^^^^^^^^^^^ cannot infer type for `{integer}` | note: the type is part of the generator because of this `yield` --> $DIR/nested.rs:10:13 @@ -68,17 +20,18 @@ note: the type is part of the generator because of this `yield` | ^^^^^^^^^^^ error[E0698]: type inside generator must be known in this context - --> $DIR/nested.rs:10:19 - | -10 | yield i * i; - | ^^^^^ - | + --> $DIR/nested.rs:5:1 + | +5 | #[async_stream] + | ^^^^^^^^^^^^^^^ cannot infer type for `{integer}` + | note: the type is part of the generator because of this `yield` - --> $DIR/nested.rs:10:13 - | -10 | yield i * i; - | ^^^^^^^^^^^ + --> $DIR/nested.rs:5:1 + | +5 | #[async_stream] + | ^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0308, E0698. +For more information about an error, try `rustc --explain E0308`. From cc7f1e52f6163e4530c025008b483cd6a9296b25 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 29 Apr 2019 00:36:06 +0900 Subject: [PATCH 09/15] Adjust documents --- futures-async-macro/README.md | 8 ++++++-- futures-async-macro/src/lib.rs | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/futures-async-macro/README.md b/futures-async-macro/README.md index 6753080ddd..ff519c71ab 100644 --- a/futures-async-macro/README.md +++ b/futures-async-macro/README.md @@ -7,7 +7,7 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -futures-preview = { version = "0.3.0-alpha.14", features = ["async-stream", "nightly"] } +futures-preview = { version = "0.3.0-alpha.15", features = ["async-stream", "nightly"] } ``` ### \#\[for_await\] @@ -17,6 +17,7 @@ Processes streams using a for loop. This is a reimplement of [futures-await]'s `#[async]` for loops for futures 0.3 and is an experimental implementation of [the idea listed as the next step of async/await](https://github.com/rust-lang/rfcs/blob/master/text/2394-async_await.md#for-await-and-processing-streams). ```rust +#![feature(async_await, generators, stmt_expr_attributes, proc_macro_hygiene)] use futures::for_await; use futures::prelude::*; @@ -26,13 +27,16 @@ for value in stream::iter(1..=5) { } ``` -`value` has the `Item` type of the stream passed in. Note that async for loops can only be used inside of `async` functions or `#[async_stream]` functions. +`value` has the `Item` type of the stream passed in. Note that async for loops can only be used inside of `async` functions, closures, blocks, `#[async_stream]` functions and `async_stream_block!` macros. ### \#\[async_stream\] +Creates streams via generators. + This is a reimplement of [futures-await]'s `#[async_stream]` for futures 0.3 and is an experimental implementation of [the idea listed as the next step of async/await](https://github.com/rust-lang/rfcs/blob/master/text/2394-async_await.md#generators-and-streams). ```rust +#![feature(async_await, generators)] use futures::prelude::*; use futures::async_stream; diff --git a/futures-async-macro/src/lib.rs b/futures-async-macro/src/lib.rs index 5a7422f324..2d6eeb73d3 100644 --- a/futures-async-macro/src/lib.rs +++ b/futures-async-macro/src/lib.rs @@ -19,6 +19,7 @@ mod error; mod elision; +/// Processes streams using a for loop. #[proc_macro_attribute] pub fn for_await(args: TokenStream, input: TokenStream) -> TokenStream { assert_!(args.is_empty(), args_is_not_empty!("for_await")); @@ -28,6 +29,7 @@ pub fn for_await(args: TokenStream, input: TokenStream) -> TokenStream { Expand(Future).fold_expr(Expr::ForLoop(expr)).into_token_stream().into() } +/// 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")); @@ -180,6 +182,7 @@ fn expand_async_stream_fn(item: ItemFn) -> TokenStream { }) } +/// Creates streams via generators. #[proc_macro] pub fn async_stream_block(input: TokenStream) -> TokenStream { let input = TokenStream::from(TokenTree::Group(Group::new(Delimiter::Brace, input))); From a10d0ad17c33a6945587b7e0ad412908700c85ef Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 8 May 2019 07:38:41 +0900 Subject: [PATCH 10/15] Update syn dependency to 0.15.34 --- futures-async-macro/Cargo.toml | 2 +- futures-async-macro/src/lib.rs | 15 +++++---------- futures-select-macro/Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/futures-async-macro/Cargo.toml b/futures-async-macro/Cargo.toml index aa75e5d05b..49cfc8844a 100644 --- a/futures-async-macro/Cargo.toml +++ b/futures-async-macro/Cargo.toml @@ -21,4 +21,4 @@ std = [] [dependencies] proc-macro2 = "0.4" quote = "0.6" -syn = { version = "0.15.31", features = ["full", "fold"] } +syn = { version = "0.15.34", features = ["full", "fold"] } diff --git a/futures-async-macro/src/lib.rs b/futures-async-macro/src/lib.rs index 2d6eeb73d3..108a202c84 100644 --- a/futures-async-macro/src/lib.rs +++ b/futures-async-macro/src/lib.rs @@ -10,8 +10,8 @@ use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree as TokenTree2}; use quote::{quote, ToTokens}; use syn::{ fold::{self, Fold}, - token, ArgCaptured, Error, Expr, ExprForLoop, ExprMacro, ExprYield, FnArg, FnDecl, Ident, Item, - ItemFn, Pat, PatIdent, ReturnType, TypeTuple, + token, ArgCaptured, Error, Expr, ExprCall, ExprForLoop, ExprMacro, ExprYield, FnArg, FnDecl, + Ident, Item, ItemFn, Pat, PatIdent, ReturnType, TypeTuple, }; #[macro_use] @@ -298,14 +298,9 @@ impl Expand { if self.0 == Stream && expr.mac.path.is_ident("await") { return self.expand_await_macros(expr); } else if expr.mac.path.is_ident("async_stream_block") { - // FIXME: When added Parse impl for ExprCall, replace `if let ..` + `unreachable!()` - // with `let` + `.unwrap()` - if let Ok(Expr::Call(mut e)) = syn::parse(async_stream_block(expr.mac.tts.into())) { - e.attrs.append(&mut expr.attrs); - return Expr::Call(e); - } else { - unreachable!() - } + let mut e: ExprCall = syn::parse(async_stream_block(expr.mac.tts.into())).unwrap(); + e.attrs.append(&mut expr.attrs); + return Expr::Call(e); } Expr::Macro(expr) diff --git a/futures-select-macro/Cargo.toml b/futures-select-macro/Cargo.toml index 039f4f9dc0..ad7ff2907e 100644 --- a/futures-select-macro/Cargo.toml +++ b/futures-select-macro/Cargo.toml @@ -22,4 +22,4 @@ std = [] proc-macro2 = "0.4" proc-macro-hack = "0.5.3" quote = "0.6" -syn = { version = "0.15.31", features = ["full"] } +syn = { version = "0.15.34", features = ["full"] } From 9f04a0960032a816ea5f365d34fc226329c86e09 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 25 May 2019 20:21:00 +0900 Subject: [PATCH 11/15] Migrate to await syntax --- futures-async-macro/README.md | 4 +- futures-async-macro/src/lib.rs | 115 ++++++++++++++++----------- futures/testcrate/ui/nested.stderr | 45 +++-------- futures/tests/async_stream/nested.rs | 4 +- futures/tests/async_stream_tests.rs | 2 +- 5 files changed, 84 insertions(+), 86 deletions(-) diff --git a/futures-async-macro/README.md b/futures-async-macro/README.md index ff519c71ab..5ba70641c6 100644 --- a/futures-async-macro/README.md +++ b/futures-async-macro/README.md @@ -7,7 +7,7 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -futures-preview = { version = "0.3.0-alpha.15", features = ["async-stream", "nightly"] } +futures-preview = { version = "0.3.0-alpha.16", features = ["async-stream", "nightly"] } ``` ### \#\[for_await\] @@ -17,7 +17,7 @@ Processes streams using a for loop. This is a reimplement of [futures-await]'s `#[async]` for loops for futures 0.3 and is an experimental implementation of [the idea listed as the next step of async/await](https://github.com/rust-lang/rfcs/blob/master/text/2394-async_await.md#for-await-and-processing-streams). ```rust -#![feature(async_await, generators, stmt_expr_attributes, proc_macro_hygiene)] +#![feature(async_await, stmt_expr_attributes, proc_macro_hygiene)] use futures::for_await; use futures::prelude::*; diff --git a/futures-async-macro/src/lib.rs b/futures-async-macro/src/lib.rs index 108a202c84..597184212a 100644 --- a/futures-async-macro/src/lib.rs +++ b/futures-async-macro/src/lib.rs @@ -1,7 +1,8 @@ //! Procedural macro for the `#[async_stream]` attribute. #![recursion_limit = "128"] -#![warn(rust_2018_idioms)] +#![warn(rust_2018_idioms, unreachable_pub)] +#![warn(clippy::all)] extern crate proc_macro; @@ -10,8 +11,8 @@ use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree as TokenTree2}; use quote::{quote, ToTokens}; use syn::{ fold::{self, Fold}, - token, ArgCaptured, Error, Expr, ExprCall, ExprForLoop, ExprMacro, ExprYield, FnArg, FnDecl, - Ident, Item, ItemFn, Pat, PatIdent, ReturnType, TypeTuple, + token, ArgCaptured, Error, Expr, ExprCall, ExprField, ExprForLoop, ExprMacro, ExprYield, FnArg, + FnDecl, Ident, Item, ItemFn, Member, Pat, PatIdent, ReturnType, TypeTuple, }; #[macro_use] @@ -210,7 +211,7 @@ pub fn async_stream_block(input: TokenStream) -> TokenStream { tokens.into() } -/// The scope in which `#[for_await]`, `await!` was called. +/// The scope in which `#[for_await]`, `.await` was called. /// /// The type of generator depends on which scope is called. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -221,7 +222,7 @@ enum Scope { Stream, /// `static move ||`, `||` /// - /// It cannot call `#[for_await]`, `await!` in this scope. + /// It cannot call `#[for_await]`, `.await` in this scope. Closure, } @@ -241,49 +242,66 @@ impl Expand { )); } + let ExprForLoop { label, pat, expr, body, .. } = &expr; // It needs to adjust the type yielded by the macro because generators used internally by // async fn yield `()` type, but generators used internally by `async_stream` yield // `Poll` type. - let yield_ = match self.0 { - Future => TokenStream2::new(), - Stream => quote! { ::futures::core_reexport::task::Poll::Pending }, - Closure => return outside_of_async_error!(expr, "#[for_await]"), - }; - let ExprForLoop { label, pat, expr, body, .. } = expr; - - // Basically just expand to a `poll` loop - syn::parse_quote! {{ - let mut __pinned = #expr; - #label - loop { - let #pat = { - match ::futures::async_stream::poll_next_with_tls_context(unsafe { - ::futures::core_reexport::pin::Pin::new_unchecked(&mut __pinned) - }) - { - ::futures::core_reexport::task::Poll::Ready(e) => { - match e { + match self.0 { + Future => { + // Basically just expand to a `poll` loop + syn::parse_quote! {{ + let mut __pinned = #expr; + let mut __pinned = unsafe { + ::futures::core_reexport::pin::Pin::new_unchecked(&mut __pinned) + }; + #label + loop { + let #pat = { + match ::futures::stream::StreamExt::next(&mut __pinned).await { ::futures::core_reexport::option::Option::Some(e) => e, ::futures::core_reexport::option::Option::None => break, } - } - ::futures::core_reexport::task::Poll::Pending => { - yield #yield_; - continue - } + }; + + #body } - }; + }} + } + Stream => { + // Basically just expand to a `poll` loop + syn::parse_quote! {{ + let mut __pinned = #expr; + #label + loop { + let #pat = { + match ::futures::async_stream::poll_next_with_tls_context(unsafe { + ::futures::core_reexport::pin::Pin::new_unchecked(&mut __pinned) + }) + { + ::futures::core_reexport::task::Poll::Ready(e) => { + match e { + ::futures::core_reexport::option::Option::Some(e) => e, + ::futures::core_reexport::option::Option::None => break, + } + } + ::futures::core_reexport::task::Poll::Pending => { + yield ::futures::core_reexport::task::Poll::Pending; + continue + } + } + }; - #body + #body + } + }} } - }} + Closure => return outside_of_async_error!(expr, "#[for_await]"), + } } /// Expands `yield expr` in `async_stream` scope. fn expand_yield(&self, expr: ExprYield) -> ExprYield { - if self.0 != Stream { - return expr; - } + if self.0 != Stream { return expr } let ExprYield { attrs, yield_token, expr } = expr; let expr = expr.map_or_else(|| quote!(()), ToTokens::into_token_stream); @@ -293,28 +311,30 @@ impl Expand { ExprYield { attrs, yield_token, expr: Some(Box::new(expr)) } } - /// Expands a macro. + /// Expands `async_stream_block!` macro. fn expand_macro(&mut self, mut expr: ExprMacro) -> Expr { - if self.0 == Stream && expr.mac.path.is_ident("await") { - return self.expand_await_macros(expr); - } else if expr.mac.path.is_ident("async_stream_block") { + if expr.mac.path.is_ident("async_stream_block") { let mut e: ExprCall = syn::parse(async_stream_block(expr.mac.tts.into())).unwrap(); e.attrs.append(&mut expr.attrs); - return Expr::Call(e); + Expr::Call(e) + } else { + Expr::Macro(expr) } - - Expr::Macro(expr) } - /// Expands `await!(expr)` in `async_stream` scope. + /// Expands `expr.await` in `async_stream` scope. /// /// It needs to adjust the type yielded by the macro because generators used internally by /// async fn yield `()` type, but generators used internally by `async_stream` yield /// `Poll` type. - fn expand_await_macros(&mut self, expr: ExprMacro) -> Expr { - assert_eq!(self.0, Stream); + fn expand_await(&mut self, expr: ExprField) -> Expr { + if self.0 != Stream { return Expr::Field(expr) } - let expr = expr.mac.tts; + match &expr.member { + Member::Named(x) if x == "await" => {} + _ => return Expr::Field(expr), + } + let expr = expr.base; // Because macro input (`#expr`) is untrusted, use `syn::parse2` + `expr_compile_error` // instead of `syn::parse_quote!` to generate better error messages (`syn::parse_quote!` @@ -349,8 +369,9 @@ impl Fold for Expand { } let expr = match fold::fold_expr(self, expr) { - Expr::ForLoop(expr) => self.expand_for_await(expr), Expr::Yield(expr) => Expr::Yield(self.expand_yield(expr)), + Expr::Field(expr) => self.expand_await(expr), + Expr::ForLoop(expr) => self.expand_for_await(expr), Expr::Macro(expr) => self.expand_macro(expr), expr => expr, }; diff --git a/futures/testcrate/ui/nested.stderr b/futures/testcrate/ui/nested.stderr index f6f5709f69..abe9207df0 100644 --- a/futures/testcrate/ui/nested.stderr +++ b/futures/testcrate/ui/nested.stderr @@ -1,37 +1,14 @@ -error[E0308]: mismatched types - --> $DIR/nested.rs:10:19 +error[E0727]: `async` generators are not yet supported + --> $DIR/nested.rs:7:19 | -10 | yield i * i; - | ^^^^^ expected (), found integer - | - = note: expected type `()` - found type `{integer}` - -error[E0698]: type inside generator must be known in this context - --> $DIR/nested.rs:10:13 - | -10 | yield i * i; - | ^^^^^^^^^^^ cannot infer type for `{integer}` - | -note: the type is part of the generator because of this `yield` - --> $DIR/nested.rs:10:13 - | -10 | yield i * i; - | ^^^^^^^^^^^ - -error[E0698]: type inside generator must be known in this context - --> $DIR/nested.rs:5:1 - | -5 | #[async_stream] - | ^^^^^^^^^^^^^^^ cannot infer type for `{integer}` - | -note: the type is part of the generator because of this `yield` - --> $DIR/nested.rs:5:1 - | -5 | #[async_stream] - | ^^^^^^^^^^^^^^^ +7 | let _ = async { + | ___________________^ +8 | | #[for_await] +9 | | for i in stream::iter(vec![1, 2]) { +10 | | yield i * i; +11 | | } +12 | | }; + | |_____^ -error: aborting due to 3 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0308, E0698. -For more information about an error, try `rustc --explain E0308`. diff --git a/futures/tests/async_stream/nested.rs b/futures/tests/async_stream/nested.rs index dfa22db216..1e295f26a5 100644 --- a/futures/tests/async_stream/nested.rs +++ b/futures/tests/async_stream/nested.rs @@ -6,8 +6,8 @@ fn _stream1() -> i32 { let _ = async { // impl Generator #[for_await] for i in stream::iter(vec![1, 2]) { - await!(future::lazy(|_| i * i)); + future::lazy(|_| i * i).await; } }; - await!(future::lazy(|_| ())); + future::lazy(|_| ()).await; } diff --git a/futures/tests/async_stream_tests.rs b/futures/tests/async_stream_tests.rs index d9d3cff6ef..9d68393268 100644 --- a/futures/tests/async_stream_tests.rs +++ b/futures/tests/async_stream_tests.rs @@ -1,4 +1,4 @@ -#![feature(async_await, await_macro)] +#![feature(async_await)] #![cfg_attr(all(feature = "async-stream", feature = "nightly"), feature(generators, stmt_expr_attributes, proc_macro_hygiene))] #[cfg(all(feature = "async-stream", feature = "nightly"))] From 66b3ef916781e56ac2ff70b252fae60e79a89aa3 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 25 May 2019 22:02:26 +0900 Subject: [PATCH 12/15] Revert Item type specification --- futures-async-macro/README.md | 8 +-- futures-async-macro/src/error.rs | 5 ++ futures-async-macro/src/lib.rs | 49 +++++++++++------ futures/testcrate/ui/bad-input.rs | 8 +-- futures/testcrate/ui/bad-input.stderr | 8 +-- futures/testcrate/ui/bad-item-type.rs | 24 ++++++++ futures/testcrate/ui/bad-item-type.stderr | 59 ++++++++++++++++++++ futures/testcrate/ui/bad-return-type.rs | 21 ++----- futures/testcrate/ui/bad-return-type.stderr | 61 ++------------------- futures/testcrate/ui/forget-semicolon.rs | 2 +- futures/testcrate/ui/missing-item.rs | 8 +++ futures/testcrate/ui/missing-item.stderr | 8 +++ futures/testcrate/ui/nested.rs | 4 +- futures/testcrate/ui/type_error.rs | 4 +- futures/testcrate/ui/unresolved-type.rs | 4 +- futures/testcrate/ui/unresolved-type.stderr | 22 ++++---- futures/tests/async_stream/elisions.rs | 18 +++--- futures/tests/async_stream/nested.rs | 4 +- futures/tests/async_stream/pinned.rs | 4 +- futures/tests/async_stream/smoke.rs | 38 ++++++------- 20 files changed, 206 insertions(+), 153 deletions(-) create mode 100644 futures/testcrate/ui/bad-item-type.rs create mode 100644 futures/testcrate/ui/bad-item-type.stderr create mode 100644 futures/testcrate/ui/missing-item.rs create mode 100644 futures/testcrate/ui/missing-item.stderr diff --git a/futures-async-macro/README.md b/futures-async-macro/README.md index 5ba70641c6..49610f96e6 100644 --- a/futures-async-macro/README.md +++ b/futures-async-macro/README.md @@ -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\] @@ -41,8 +41,8 @@ use futures::prelude::*; use futures::async_stream; // Returns a stream of i32 -#[async_stream] -fn foo(stream: impl Stream) -> i32 { +#[async_stream(item = i32)] +fn foo(stream: impl Stream) { #[for_await] for x in stream { yield x.parse().unwrap(); @@ -50,6 +50,6 @@ fn foo(stream: impl Stream) -> i32 { } ``` -`#[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 diff --git a/futures-async-macro/src/error.rs b/futures-async-macro/src/error.rs index cfedc1105f..ffd0417325 100644 --- a/futures-async-macro/src/error.rs +++ b/futures-async-macro/src/error.rs @@ -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? diff --git a/futures-async-macro/src/lib.rs b/futures-async-macro/src/lib.rs index 597184212a..4e04dbe631 100644 --- a/futures-async-macro/src/lib.rs +++ b/futures-async-macro/src/lib.rs @@ -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] @@ -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: @@ -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! { @@ -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 + #(#lifetimes +)* + #impl_token ::futures::stream::Stream + #(#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 }) @@ -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 { + 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. diff --git a/futures/testcrate/ui/bad-input.rs b/futures/testcrate/ui/bad-input.rs index 5573a91b61..7479a652c3 100644 --- a/futures/testcrate/ui/bad-input.rs +++ b/futures/testcrate/ui/bad-input.rs @@ -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; diff --git a/futures/testcrate/ui/bad-input.stderr b/futures/testcrate/ui/bad-input.stderr index 76a83516d1..1b36885022 100644 --- a/futures/testcrate/ui/bad-input.stderr +++ b/futures/testcrate/ui/bad-input.stderr @@ -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 diff --git a/futures/testcrate/ui/bad-item-type.rs b/futures/testcrate/ui/bad-item-type.rs new file mode 100644 index 0000000000..eebf91cdfc --- /dev/null +++ b/futures/testcrate/ui/bad-item-type.rs @@ -0,0 +1,24 @@ +#![feature(async_await, generators)] + +use futures::*; + +#[async_stream(item = Option)] +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() {} diff --git a/futures/testcrate/ui/bad-item-type.stderr b/futures/testcrate/ui/bad-item-type.stderr new file mode 100644 index 0000000000..3a13a5e627 --- /dev/null +++ b/futures/testcrate/ui/bad-item-type.stderr @@ -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)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +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 `::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`. diff --git a/futures/testcrate/ui/bad-return-type.rs b/futures/testcrate/ui/bad-return-type.rs index d086fab3e0..fdf1101b22 100644 --- a/futures/testcrate/ui/bad-return-type.rs +++ b/futures/testcrate/ui/bad-return-type.rs @@ -2,23 +2,10 @@ use futures::*; -#[async_stream] -fn foobar() -> Option { - let val = Some(42); - if val.is_none() { - yield None; - return; - } - let val = val.unwrap(); - yield val; -} +#[async_stream(item = Option)] +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() {} diff --git a/futures/testcrate/ui/bad-return-type.stderr b/futures/testcrate/ui/bad-return-type.stderr index dd7790e44e..bd53bbde07 100644 --- a/futures/testcrate/ui/bad-return-type.stderr +++ b/futures/testcrate/ui/bad-return-type.stderr @@ -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 `::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`. diff --git a/futures/testcrate/ui/forget-semicolon.rs b/futures/testcrate/ui/forget-semicolon.rs index d15f648972..f671a941f5 100644 --- a/futures/testcrate/ui/forget-semicolon.rs +++ b/futures/testcrate/ui/forget-semicolon.rs @@ -2,7 +2,7 @@ use futures::*; -#[async_stream] +#[async_stream(item = ())] fn foo() { yield; Some(()) diff --git a/futures/testcrate/ui/missing-item.rs b/futures/testcrate/ui/missing-item.rs new file mode 100644 index 0000000000..426f619481 --- /dev/null +++ b/futures/testcrate/ui/missing-item.rs @@ -0,0 +1,8 @@ +#![feature(async_await, generators)] + +use futures::*; + +#[async_stream] +fn foo(a: String) {} + +fn main() {} diff --git a/futures/testcrate/ui/missing-item.stderr b/futures/testcrate/ui/missing-item.stderr new file mode 100644 index 0000000000..d0eb3bb434 --- /dev/null +++ b/futures/testcrate/ui/missing-item.stderr @@ -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 + diff --git a/futures/testcrate/ui/nested.rs b/futures/testcrate/ui/nested.rs index bb5b04f56a..e2fa2039a1 100644 --- a/futures/testcrate/ui/nested.rs +++ b/futures/testcrate/ui/nested.rs @@ -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]) { diff --git a/futures/testcrate/ui/type_error.rs b/futures/testcrate/ui/type_error.rs index 4e1f9e0b02..b075c2aa60 100644 --- a/futures/testcrate/ui/type_error.rs +++ b/futures/testcrate/ui/type_error.rs @@ -2,8 +2,8 @@ use futures::*; -#[async_stream] -fn foo() -> i32 { +#[async_stream(item = i32)] +fn foo() { let a: i32 = "a"; //~ ERROR: mismatched types yield 1; } diff --git a/futures/testcrate/ui/unresolved-type.rs b/futures/testcrate/ui/unresolved-type.rs index fbf5ca5ddb..b33fcad725 100644 --- a/futures/testcrate/ui/unresolved-type.rs +++ b/futures/testcrate/ui/unresolved-type.rs @@ -2,7 +2,7 @@ use futures::*; -#[async_stream] -fn foo() -> Left {} +#[async_stream(item = Left)] +fn foo() {} fn main() {} diff --git a/futures/testcrate/ui/unresolved-type.stderr b/futures/testcrate/ui/unresolved-type.stderr index 2888d16098..af4917f3bf 100644 --- a/futures/testcrate/ui/unresolved-type.stderr +++ b/futures/testcrate/ui/unresolved-type.stderr @@ -1,18 +1,18 @@ error[E0412]: cannot find type `Left` in this scope - --> $DIR/unresolved-type.rs:6:13 + --> $DIR/unresolved-type.rs:5:23 | -6 | fn foo() -> Left {} - | ^^^^ not found in this scope +5 | #[async_stream(item = Left)] + | ^^^^ not found in this scope help: there is an enum variant `core::fmt::Alignment::Left` and 7 others; try using the variant's enum | -6 | fn foo() -> core::fmt::Alignment {} - | ^^^^^^^^^^^^^^^^^^^^ -6 | fn foo() -> core::fmt::rt::v1::Alignment {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -6 | fn foo() -> futures::core_reexport::fmt::Alignment {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -6 | fn foo() -> futures::core_reexport::fmt::rt::v1::Alignment {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +5 | #[async_stream(item = core::fmt::Alignment)] + | ^^^^^^^^^^^^^^^^^^^^ +5 | #[async_stream(item = core::fmt::rt::v1::Alignment)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +5 | #[async_stream(item = futures::core_reexport::fmt::Alignment)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +5 | #[async_stream(item = futures::core_reexport::fmt::rt::v1::Alignment)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ and 3 other candidates error: aborting due to previous error diff --git a/futures/tests/async_stream/elisions.rs b/futures/tests/async_stream/elisions.rs index 03e648ff8f..855c9f4623 100644 --- a/futures/tests/async_stream/elisions.rs +++ b/futures/tests/async_stream/elisions.rs @@ -3,31 +3,31 @@ use futures::*; struct Ref<'a, T>(&'a T); -#[async_stream] -fn references(x: &i32) -> i32 { +#[async_stream(item = i32)] +fn references(x: &i32) { yield *x; } -#[async_stream] -fn new_types(x: Ref<'_, i32>) -> i32 { +#[async_stream(item = i32)] +fn new_types(x: Ref<'_, i32>) { yield *x.0; } struct Foo(i32); impl Foo { - #[async_stream] - fn foo(&self) -> &i32 { + #[async_stream(item = &i32)] + fn foo(&self) { yield &self.0 } } -#[async_stream] -fn single_ref(x: &i32) -> &i32 { +#[async_stream(item = &i32)] +fn single_ref(x: &i32) { yield x } -#[async_stream] +#[async_stream(item = ())] fn check_for_name_colision<'_async0, T>(_x: &T, _y: &'_async0 i32) { yield } diff --git a/futures/tests/async_stream/nested.rs b/futures/tests/async_stream/nested.rs index 1e295f26a5..a1c304676d 100644 --- a/futures/tests/async_stream/nested.rs +++ b/futures/tests/async_stream/nested.rs @@ -1,8 +1,8 @@ use futures::*; -#[async_stream] // impl Generator, Return = ()> -fn _stream1() -> i32 { +#[async_stream(item = i32)] // impl Generator, Return = ()> +fn _stream1() { let _ = async { // impl Generator #[for_await] for i in stream::iter(vec![1, 2]) { diff --git a/futures/tests/async_stream/pinned.rs b/futures/tests/async_stream/pinned.rs index d407262f90..58548bc7a0 100644 --- a/futures/tests/async_stream/pinned.rs +++ b/futures/tests/async_stream/pinned.rs @@ -1,8 +1,8 @@ use futures::executor::block_on; use futures::*; -#[async_stream] -fn stream1() -> u64 { +#[async_stream(item = u64)] +fn stream1() { fn integer() -> u64 { 1 } diff --git a/futures/tests/async_stream/smoke.rs b/futures/tests/async_stream/smoke.rs index c13bf57a3a..e55aadcfab 100644 --- a/futures/tests/async_stream/smoke.rs +++ b/futures/tests/async_stream/smoke.rs @@ -15,20 +15,20 @@ async fn future1() -> i32 { cnt } -#[async_stream] -fn stream1() -> u64 { +#[async_stream(item = u64)] +fn stream1() { yield 0; yield 1; } -#[async_stream] -fn stream2(t: T) -> T { +#[async_stream(item = T)] +fn stream2(t: T) { yield t.clone(); yield t.clone(); } -#[async_stream] -fn stream3() -> i32 { +#[async_stream(item = i32)] +fn stream3() { let mut cnt = 0; #[for_await] for x in stream::iter(vec![1, 2, 3, 4]) { @@ -42,40 +42,40 @@ mod foo { pub struct _Foo(pub i32); } -#[async_stream] -fn _stream5() -> foo::_Foo { +#[async_stream(item = foo::_Foo)] +fn _stream5() { yield foo::_Foo(0); yield foo::_Foo(1); } -#[async_stream] -fn _stream6() -> i32 { +#[async_stream(item = i32)] +fn _stream6() { #[for_await] for foo::_Foo(i) in _stream5() { yield i * i; } } -#[async_stream] -fn _stream7() -> () { +#[async_stream(item = ())] +fn _stream7() { yield (); } -#[async_stream] -fn _stream8() -> [u32; 4] { +#[async_stream(item = [u32; 4])] +fn _stream8() { yield [1, 2, 3, 4]; } struct A(i32); impl A { - #[async_stream] - fn a_foo(self) -> i32 { + #[async_stream(item = i32)] + fn a_foo(self) { yield self.0 } - #[async_stream] - fn _a_foo2(self: Box) -> i32 { + #[async_stream(item = i32)] + fn _a_foo2(self: Box) { yield self.0 } } @@ -98,7 +98,7 @@ async fn loop_in_loop() -> bool { #[test] fn main() { // https://github.com/alexcrichton/futures-await/issues/45 - #[async_stream] + #[async_stream(item = ())] fn _stream10() { yield; } From 380ddc5623652449599e03418a9df3164f284fe0 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 27 Jun 2019 04:07:54 +0900 Subject: [PATCH 13/15] Fix ui tests --- futures/testcrate/ui/move-captured-variable.stderr | 8 ++++++-- futures/testcrate/ui/nested.stderr | 12 +++--------- futures/testcrate/ui/update-all-references.sh | 3 ++- futures/testcrate/ui/update-references.sh | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/futures/testcrate/ui/move-captured-variable.stderr b/futures/testcrate/ui/move-captured-variable.stderr index bacd96c626..956dba9012 100644 --- a/futures/testcrate/ui/move-captured-variable.stderr +++ b/futures/testcrate/ui/move-captured-variable.stderr @@ -1,4 +1,4 @@ -error[E0507]: cannot move out of captured variable in an `FnMut` closure +error[E0507]: cannot move out of `a`, a captured variable in an `FnMut` closure --> $DIR/move-captured-variable.rs:10:9 | 8 | let a = String::new(); @@ -6,8 +6,12 @@ error[E0507]: cannot move out of captured variable in an `FnMut` closure 9 | foo(|| { 10 | / async_stream_block! { 11 | | yield a + | | - + | | | + | | move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait + | | move occurs due to use in generator 12 | | }; - | |__________^ cannot move out of captured variable in an `FnMut` closure + | |__________^ move out of `a` occurs here error: aborting due to previous error diff --git a/futures/testcrate/ui/nested.stderr b/futures/testcrate/ui/nested.stderr index abe9207df0..444c03e5d1 100644 --- a/futures/testcrate/ui/nested.stderr +++ b/futures/testcrate/ui/nested.stderr @@ -1,14 +1,8 @@ error[E0727]: `async` generators are not yet supported - --> $DIR/nested.rs:7:19 + --> $DIR/nested.rs:10:13 | -7 | let _ = async { - | ___________________^ -8 | | #[for_await] -9 | | for i in stream::iter(vec![1, 2]) { -10 | | yield i * i; -11 | | } -12 | | }; - | |_____^ +10 | yield i * i; + | ^^^^^^^^^^^ error: aborting due to previous error diff --git a/futures/testcrate/ui/update-all-references.sh b/futures/testcrate/ui/update-all-references.sh index 13545510c0..207a562c7e 100755 --- a/futures/testcrate/ui/update-all-references.sh +++ b/futures/testcrate/ui/update-all-references.sh @@ -18,6 +18,7 @@ # # See all `update-references.sh`, if you just want to update a single test. -MY_DIR="$(dirname "${BASH_SOURCE[0]}")" +MY_DIR=$(dirname $0) cd $MY_DIR find . -name '*.rs' | xargs ./update-references.sh +cd - diff --git a/futures/testcrate/ui/update-references.sh b/futures/testcrate/ui/update-references.sh index 67aa4f857e..fa83607914 100755 --- a/futures/testcrate/ui/update-references.sh +++ b/futures/testcrate/ui/update-references.sh @@ -19,7 +19,7 @@ # If you find yourself manually editing a foo.stderr file, you're # doing it wrong. -MYDIR="$(dirname "${BASH_SOURCE[0]}")" +MYDIR=$(dirname $0) BUILD_DIR="../target/tests/ui" From 276d1684e010a5dea49e583abe3c977213c33684 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 27 Jun 2019 04:08:07 +0900 Subject: [PATCH 14/15] Use #![warn(single_use_lifetimes)] in futures-async-macro Apply https://github.com/rust-lang-nursery/futures-rs/pull/1680 --- futures-async-macro/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/futures-async-macro/src/lib.rs b/futures-async-macro/src/lib.rs index 4e04dbe631..9ad6874e5d 100644 --- a/futures-async-macro/src/lib.rs +++ b/futures-async-macro/src/lib.rs @@ -2,6 +2,8 @@ #![recursion_limit = "128"] #![warn(rust_2018_idioms, unreachable_pub)] +// It cannot be included in the published code because this lints have false positives in the minimum required version. +#![cfg_attr(test, warn(single_use_lifetimes))] #![warn(clippy::all)] extern crate proc_macro; From 75eac0ad306abc76cf9f1b488a54f7e939e902b3 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 7 Jul 2019 18:25:58 +0900 Subject: [PATCH 15/15] Fix ui tests --- futures/testcrate/ui/bad-item-type.stderr | 80 +++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/futures/testcrate/ui/bad-item-type.stderr b/futures/testcrate/ui/bad-item-type.stderr index 3a13a5e627..3de92a4dc8 100644 --- a/futures/testcrate/ui/bad-item-type.stderr +++ b/futures/testcrate/ui/bad-item-type.stderr @@ -11,10 +11,10 @@ error[E0308]: mismatched types found type `{integer}` error[E0698]: type inside generator must be known in this context - --> $DIR/bad-item-type.rs:5:1 + --> $DIR/bad-item-type.rs:7:9 | -5 | #[async_stream(item = Option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `{integer}` +7 | let val = Some(42); + | ^^^ cannot infer type for `{integer}` | note: the type is part of the generator because of this `yield` --> $DIR/bad-item-type.rs:5:1 @@ -22,6 +22,30 @@ note: the type is part of the generator because of this `yield` 5 | #[async_stream(item = Option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-item-type.rs:12:9 + | +12 | let val = val.unwrap(); + | ^^^ 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)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-item-type.rs:13:11 + | +13 | yield val; + | ^^^ 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)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0308]: mismatched types --> $DIR/bad-item-type.rs:21:11 | @@ -53,7 +77,55 @@ note: the type is part of the generator because of this `yield` 16 | #[async_stream(item = (i32, i32))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error[E0698]: type inside generator must be known in this context + --> $DIR/bad-item-type.rs:19:15 + | +19 | yield 3; + | ^ 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[E0698]: type inside generator must be known in this context + --> $DIR/bad-item-type.rs:21:12 + | +21 | yield (1, 2) + | ^ 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[E0698]: type inside generator must be known in this context + --> $DIR/bad-item-type.rs:21:15 + | +21 | yield (1, 2) + | ^ 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[E0698]: type inside generator must be known in this context + --> $DIR/bad-item-type.rs:21:11 + | +21 | yield (1, 2) + | ^^^^^^ 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 11 previous errors Some errors have detailed explanations: E0271, E0308, E0698. For more information about an error, try `rustc --explain E0271`.