From b05d5db87bd9a3f31729a5c48dc5dd5bec6dbd2d Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 1 May 2019 13:35:34 +0100 Subject: Ensure that drop order of `async fn` matches `fn`. This commit modifies the lowering of `async fn` arguments so that the drop order matches the equivalent `fn`. Previously, async function arguments were lowered as shown below: async fn foo(: ) { async move { } } // <-- dropped as you "exit" the fn // ...becomes... fn foo(__arg0: ) { async move { let = __arg0; } // <-- dropped as you "exit" the async block } After this PR, async function arguments will be lowered as: async fn foo(: , : , : ) { async move { } } // <-- dropped as you "exit" the fn // ...becomes... fn foo(__arg0: , __arg1: , __arg2: ) { async move { let __arg2 = __arg2; let = __arg2; let __arg1 = __arg1; let = __arg1; let __arg0 = __arg0; let = __arg0; } // <-- dropped as you "exit" the async block } If `` is a simple ident, then it is lowered to a single `let = ;` statement as an optimization. --- src/libsyntax/parse/parser.rs | 62 +++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 14 deletions(-) (limited to 'src/libsyntax/parse/parser.rs') diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8efe84cdf01..a10ee17b7e7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -8880,11 +8880,37 @@ impl<'a> Parser<'a> { let name = format!("__arg{}", index); let ident = Ident::from_str(&name); + // Check if this is a ident pattern, if so, we can optimize and avoid adding a + // `let = __argN;` statement, instead just adding a `let = ;` + // statement. + let (ident, is_simple_pattern) = match input.pat.node { + PatKind::Ident(_, ident, _) => (ident, true), + _ => (ident, false), + }; + // Construct an argument representing `__argN: ` to replace the argument of the - // async function. - let arg = Arg { - ty: input.ty.clone(), - id, + // async function if it isn't a simple pattern. + let arg = if is_simple_pattern { + None + } else { + Some(Arg { + ty: input.ty.clone(), + id, + pat: P(Pat { + id, + node: PatKind::Ident( + BindingMode::ByValue(Mutability::Immutable), ident, None, + ), + span, + }), + source: ArgSource::AsyncFn(input.pat.clone()), + }) + }; + + // Construct a `let __argN = __argN;` statement to insert at the top of the + // async closure. This makes sure that the argument is captured by the closure and + // that the drop order is correct. + let move_local = Local { pat: P(Pat { id, node: PatKind::Ident( @@ -8892,13 +8918,6 @@ impl<'a> Parser<'a> { ), span, }), - source: ArgSource::AsyncFn(input.pat.clone()), - }; - - // Construct a `let = __argN;` statement to insert at the top of the - // async closure. - let local = P(Local { - pat: input.pat.clone(), // We explicitly do not specify the type for this statement. When the user's // argument type is `impl Trait` then this would require the // `impl_trait_in_bindings` feature to also be present for that same type to @@ -8918,10 +8937,25 @@ impl<'a> Parser<'a> { span, attrs: ThinVec::new(), source: LocalSource::AsyncFn, - }); - let stmt = Stmt { id, node: StmtKind::Local(local), span, }; + }; + + // Construct a `let = __argN;` statement to insert at the top of the + // async closure if this isn't a simple pattern. + let pat_stmt = if is_simple_pattern { + None + } else { + Some(Stmt { + id, + node: StmtKind::Local(P(Local { + pat: input.pat.clone(), + ..move_local.clone() + })), + span, + }) + }; - arguments.push(AsyncArgument { ident, arg, stmt }); + let move_stmt = Stmt { id, node: StmtKind::Local(P(move_local)), span }; + arguments.push(AsyncArgument { ident, arg, pat_stmt, move_stmt }); } } } -- cgit 1.4.1-3-g733a5 From f47735c3dc596c0b55c2221a18915a7b25ed1492 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 1 May 2019 14:31:27 +0100 Subject: Ensure that users cannot use generated arguments. This commit gensyms the generated ident for replacement arguments so that users cannot refer to them. It also ensures that levenshtein distance suggestions do not suggest gensymed identifiers. --- src/librustc_resolve/lib.rs | 23 ++++---- src/libsyntax/parse/parser.rs | 2 +- .../ui/async-await-drop-order-locals-are-hidden.rs | 11 ++++ ...async-await-drop-order-locals-are-hidden.stderr | 15 +++++ src/test/ui/auxiliary/arc_wake.rs | 64 ++++++++++++++++++++++ 5 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 src/test/ui/async-await-drop-order-locals-are-hidden.rs create mode 100644 src/test/ui/async-await-drop-order-locals-are-hidden.stderr create mode 100644 src/test/ui/auxiliary/arc_wake.rs (limited to 'src/libsyntax/parse/parser.rs') diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 281be201a66..dcfe00069c5 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -4183,7 +4183,7 @@ impl<'a> Resolver<'a> { let add_module_candidates = |module: Module<'_>, names: &mut Vec| { for (&(ident, _), resolution) in module.resolutions.borrow().iter() { if let Some(binding) = resolution.borrow().binding { - if filter_fn(binding.def()) { + if !ident.name.is_gensymed() && filter_fn(binding.def()) { names.push(TypoSuggestion { candidate: ident.name, article: binding.def().article(), @@ -4201,7 +4201,7 @@ impl<'a> Resolver<'a> { for rib in self.ribs[ns].iter().rev() { // Locals and type parameters for (ident, def) in &rib.bindings { - if filter_fn(*def) { + if !ident.name.is_gensymed() && filter_fn(*def) { names.push(TypoSuggestion { candidate: ident.name, article: def.article(), @@ -4228,7 +4228,7 @@ impl<'a> Resolver<'a> { index: CRATE_DEF_INDEX, }); - if filter_fn(crate_mod) { + if !ident.name.is_gensymed() && filter_fn(crate_mod) { Some(TypoSuggestion { candidate: ident.name, article: "a", @@ -4251,13 +4251,16 @@ impl<'a> Resolver<'a> { // Add primitive types to the mix if filter_fn(Def::PrimTy(Bool)) { names.extend( - self.primitive_type_table.primitive_types.iter().map(|(name, _)| { - TypoSuggestion { - candidate: *name, - article: "a", - kind: "primitive type", - } - }) + self.primitive_type_table.primitive_types + .iter() + .filter(|(name, _)| !name.is_gensymed()) + .map(|(name, _)| { + TypoSuggestion { + candidate: *name, + article: "a", + kind: "primitive type", + } + }) ) } } else { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a10ee17b7e7..8d95b390001 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -8878,7 +8878,7 @@ impl<'a> Parser<'a> { // Construct a name for our temporary argument. let name = format!("__arg{}", index); - let ident = Ident::from_str(&name); + let ident = Ident::from_str(&name).gensym(); // Check if this is a ident pattern, if so, we can optimize and avoid adding a // `let = __argN;` statement, instead just adding a `let = ;` diff --git a/src/test/ui/async-await-drop-order-locals-are-hidden.rs b/src/test/ui/async-await-drop-order-locals-are-hidden.rs new file mode 100644 index 00000000000..10dc5e27f6f --- /dev/null +++ b/src/test/ui/async-await-drop-order-locals-are-hidden.rs @@ -0,0 +1,11 @@ +// edition:2018 + +#![allow(unused_variables)] +#![feature(async_await)] + +async fn foobar_async(x: u32, (a, _, _c): (u32, u32, u32), _: u32, _y: u32) { + assert_eq!(__arg1, (1, 2, 3)); //~ ERROR cannot find value `__arg1` in this scope [E0425] + assert_eq!(__arg2, 4); //~ ERROR cannot find value `__arg2` in this scope [E0425] +} + +fn main() {} diff --git a/src/test/ui/async-await-drop-order-locals-are-hidden.stderr b/src/test/ui/async-await-drop-order-locals-are-hidden.stderr new file mode 100644 index 00000000000..b988b85d63d --- /dev/null +++ b/src/test/ui/async-await-drop-order-locals-are-hidden.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find value `__arg1` in this scope + --> $DIR/async-await-drop-order-locals-are-hidden.rs:7:16 + | +LL | assert_eq!(__arg1, (1, 2, 3)); + | ^^^^^^ not found in this scope + +error[E0425]: cannot find value `__arg2` in this scope + --> $DIR/async-await-drop-order-locals-are-hidden.rs:8:16 + | +LL | assert_eq!(__arg2, 4); + | ^^^^^^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/auxiliary/arc_wake.rs b/src/test/ui/auxiliary/arc_wake.rs new file mode 100644 index 00000000000..c21886f26f4 --- /dev/null +++ b/src/test/ui/auxiliary/arc_wake.rs @@ -0,0 +1,64 @@ +// edition:2018 + +use std::sync::Arc; +use std::task::{ + Waker, RawWaker, RawWakerVTable, +}; + +macro_rules! waker_vtable { + ($ty:ident) => { + &RawWakerVTable::new( + clone_arc_raw::<$ty>, + wake_arc_raw::<$ty>, + wake_by_ref_arc_raw::<$ty>, + drop_arc_raw::<$ty>, + ) + }; +} + +pub trait ArcWake { + fn wake(self: Arc); + + fn wake_by_ref(arc_self: &Arc) { + arc_self.clone().wake() + } + + fn into_waker(wake: Arc) -> Waker where Self: Sized + { + let ptr = Arc::into_raw(wake) as *const (); + + unsafe { + Waker::from_raw(RawWaker::new(ptr, waker_vtable!(Self))) + } + } +} + +unsafe fn increase_refcount(data: *const ()) { + // Retain Arc by creating a copy + let arc: Arc = Arc::from_raw(data as *const T); + let arc_clone = arc.clone(); + // Forget the Arcs again, so that the refcount isn't decrased + let _ = Arc::into_raw(arc); + let _ = Arc::into_raw(arc_clone); +} + +unsafe fn clone_arc_raw(data: *const ()) -> RawWaker { + increase_refcount::(data); + RawWaker::new(data, waker_vtable!(T)) +} + +unsafe fn drop_arc_raw(data: *const ()) { + // Drop Arc + let _: Arc = Arc::from_raw(data as *const T); +} + +unsafe fn wake_arc_raw(data: *const ()) { + let arc: Arc = Arc::from_raw(data as *const T); + ArcWake::wake(arc); +} + +unsafe fn wake_by_ref_arc_raw(data: *const ()) { + let arc: Arc = Arc::from_raw(data as *const T); + ArcWake::wake_by_ref(&arc); + let _ = Arc::into_raw(arc); +} -- cgit 1.4.1-3-g733a5