From 1e5f496143aabf1d4d158d99b73afee7b00f0650 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 29 May 2019 19:28:51 +0300 Subject: [PATCH] rustc: async fn drop order lowering in HIR This commit re-implements the async fn drop order lowering changes so that it all takes place in HIR lowering, building atop the work done by `@eddyb` to refactor `Res::Upvar`. Previously, this types involved in the lowering were constructed in libsyntax as they had to be used during name resolution and HIR lowering. This was awful because none of that logic should have existed in libsyntax. This commit also changes `ArgSource` to keep a `HirId` to the original argument pattern rather than a cloned copy of the pattern. --- src/librustc/hir/intravisit.rs | 10 - src/librustc/hir/lowering.rs | 294 ++++++++++++++---- src/librustc/hir/map/mod.rs | 13 + src/librustc/hir/mod.rs | 14 +- .../nice_region_error/different_lifetimes.rs | 6 +- .../nice_region_error/named_anon_conflict.rs | 3 +- src/librustc/middle/resolve_lifetime.rs | 3 +- src/librustc_mir/build/mod.rs | 66 ++-- src/librustc_privacy/lib.rs | 20 -- src/librustc_typeck/check/mod.rs | 10 - src/librustc_typeck/check/writeback.rs | 10 - src/librustdoc/clean/mod.rs | 3 +- src/test/ui/async-await/issues/issue-61187.rs | 9 + .../ui/async-await/issues/issue-61187.stderr | 11 + 14 files changed, 317 insertions(+), 155 deletions(-) create mode 100644 src/test/ui/async-await/issues/issue-61187.rs create mode 100644 src/test/ui/async-await/issues/issue-61187.stderr diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 8000666044a..9cf365addca 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -262,9 +262,6 @@ fn visit_arm(&mut self, a: &'v Arm) { fn visit_pat(&mut self, p: &'v Pat) { walk_pat(self, p) } - fn visit_argument_source(&mut self, s: &'v ArgSource) { - walk_argument_source(self, s) - } fn visit_anon_const(&mut self, c: &'v AnonConst) { walk_anon_const(self, c) } @@ -402,17 +399,10 @@ pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body) { for argument in &body.arguments { visitor.visit_id(argument.hir_id); visitor.visit_pat(&argument.pat); - visitor.visit_argument_source(&argument.source); } visitor.visit_expr(&body.value); } -pub fn walk_argument_source<'v, V: Visitor<'v>>(visitor: &mut V, source: &'v ArgSource) { - if let ArgSource::AsyncFn(pat) = source { - visitor.visit_pat(pat); - } -} - pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local) { // Intentionally visiting the expr first - the initialization expr // dominates the local's definition. diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index df0a23c1b86..e82b3df8550 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -781,7 +781,7 @@ fn lower_node_id_with_owner(&mut self, ast_node_id: NodeId, owner: NodeId) -> hi }) } - fn record_body(&mut self, value: hir::Expr, arguments: HirVec) -> hir::BodyId { + fn record_body(&mut self, arguments: HirVec, value: hir::Expr) -> hir::BodyId { if self.is_generator && self.is_async_body { span_err!( self.sess, @@ -1121,27 +1121,21 @@ fn make_async_expr( span: Span, body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr, ) -> hir::ExprKind { - let prev_is_generator = mem::replace(&mut self.is_generator, false); - let prev_is_async_body = mem::replace(&mut self.is_async_body, true); + let capture_clause = self.lower_capture_clause(capture_clause); let output = match ret_ty { Some(ty) => FunctionRetTy::Ty(P(ty.clone())), None => FunctionRetTy::Default(span), }; - let decl = FnDecl { + let ast_decl = FnDecl { inputs: vec![], output, c_variadic: false }; - // Lower the arguments before the body otherwise the body will call `lower_res` expecting - // the argument to have been assigned an id already. - let arguments = self.lower_args(Some(&decl)); - let body_expr = body(self); - let body_id = self.record_body(body_expr, arguments); - self.is_generator = prev_is_generator; - self.is_async_body = prev_is_async_body; - - let capture_clause = self.lower_capture_clause(capture_clause); - let decl = self.lower_fn_decl(&decl, None, /* impl trait allowed */ false, None); + let decl = self.lower_fn_decl(&ast_decl, None, /* impl trait allowed */ false, None); + let body_id = self.lower_fn_body(&ast_decl, |this| { + this.is_async_body = true; + body(this) + }); let generator = hir::Expr { hir_id: self.lower_node_id(closure_node_id), node: hir::ExprKind::Closure(capture_clause, decl, body_id, span, @@ -1160,18 +1154,32 @@ fn make_async_expr( hir::ExprKind::Call(P(gen_future), hir_vec![generator]) } - fn lower_body(&mut self, decl: Option<&FnDecl>, f: F) -> hir::BodyId - where - F: FnOnce(&mut LoweringContext<'_>) -> hir::Expr, - { - let prev_generator = mem::replace(&mut self.is_generator, false); - let prev_async = mem::replace(&mut self.is_async_body, false); - let arguments = self.lower_args(decl); - let result = f(self); - let r = self.record_body(result, arguments); - self.is_generator = prev_generator; - self.is_async_body = prev_async; - return r; + fn lower_body( + &mut self, + f: impl FnOnce(&mut LoweringContext<'_>) -> (HirVec, hir::Expr), + ) -> hir::BodyId { + let prev_is_generator = mem::replace(&mut self.is_generator, false); + let prev_is_async_body = mem::replace(&mut self.is_async_body, false); + let (arguments, result) = f(self); + let body_id = self.record_body(arguments, result); + self.is_generator = prev_is_generator; + self.is_async_body = prev_is_async_body; + body_id + } + + fn lower_fn_body( + &mut self, + decl: &FnDecl, + body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr, + ) -> hir::BodyId { + self.lower_body(|this| ( + decl.inputs.iter().map(|x| this.lower_arg(x)).collect(), + body(this), + )) + } + + fn lower_const_body(&mut self, expr: &Expr) -> hir::BodyId { + self.lower_body(|this| (hir_vec![], this.lower_expr(expr))) } fn with_loop_scope(&mut self, loop_id: NodeId, f: F) -> T @@ -2273,10 +2281,6 @@ fn lower_mutability(&mut self, m: Mutability) -> hir::Mutability { } } - fn lower_args(&mut self, decl: Option<&FnDecl>) -> HirVec { - decl.map_or(hir_vec![], |decl| decl.inputs.iter().map(|x| self.lower_arg(x)).collect()) - } - fn lower_arg(&mut self, arg: &Arg) -> hir::Arg { hir::Arg { hir_id: self.lower_node_id(arg.id), @@ -2980,11 +2984,14 @@ fn lower_param_bounds(&mut self, bounds: &[GenericBound], mut itctx: ImplTraitCo bounds.iter().map(|bound| self.lower_param_bound(bound, itctx.reborrow())).collect() } - fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P { + fn lower_block_with_stmts( + &mut self, + b: &Block, + targeted_by_break: bool, + mut stmts: Vec, + ) -> P { let mut expr = None; - let mut stmts = vec![]; - for (index, stmt) in b.stmts.iter().enumerate() { if index == b.stmts.len() - 1 { if let StmtKind::Expr(ref e) = stmt.node { @@ -3007,25 +3014,177 @@ fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P { }) } - fn lower_async_body( + fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P { + self.lower_block_with_stmts(b, targeted_by_break, vec![]) + } + + fn lower_maybe_async_body( &mut self, decl: &FnDecl, asyncness: IsAsync, body: &Block, ) -> hir::BodyId { - self.lower_body(Some(&decl), |this| { - if let IsAsync::Async { closure_id, .. } = asyncness { - let async_expr = this.make_async_expr( - CaptureBy::Value, closure_id, None, body.span, - |this| { - let body = this.lower_block(&body, false); - this.expr_block(body, ThinVec::new()) - }); - this.expr(body.span, async_expr, ThinVec::new()) - } else { + let closure_id = match asyncness { + IsAsync::Async { closure_id, .. } => closure_id, + IsAsync::NotAsync => return self.lower_fn_body(&decl, |this| { let body = this.lower_block(body, false); this.expr_block(body, ThinVec::new()) + }), + }; + + self.lower_body(|this| { + let mut arguments: Vec = Vec::new(); + let mut statements: Vec<(hir::Stmt, Option)> = Vec::new(); + + // Async function arguments are lowered into the closure body so that they are + // captured and so that the drop order matches the equivalent non-async functions. + // + // async fn foo(: , : , : ) { + // async move { + // } + // } + // + // // ...becomes... + // fn foo(__arg0: , __arg1: , __arg2: ) { + // async move { + // let __arg2 = __arg2; + // let = __arg2; + // let __arg1 = __arg1; + // let = __arg1; + // let __arg0 = __arg0; + // let = __arg0; + // } + // } + // + // If `` is a simple ident, then it is lowered to a single + // `let = ;` statement as an optimization. + for (index, argument) in decl.inputs.iter().enumerate() { + let argument = this.lower_arg(argument); + let span = argument.pat.span; + + // Check if this is a binding pattern, if so, we can optimize and avoid adding a + // `let = __argN;` statement. In this case, we do not rename the argument. + let (ident, is_simple_argument) = match argument.pat.node { + hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, _) => + (ident, true), + _ => { + // Replace the ident for bindings that aren't simple. + let name = format!("__arg{}", index); + let ident = Ident::from_str(&name); + + (ident, false) + }, + }; + + // Construct an argument representing `__argN: ` to replace the argument of the + // async function. + // + // If this is the simple case, this argument will end up being the same as the + // original argument, but with a different pattern id. + let new_argument_id = this.next_id(); + let desugared_span = + this.mark_span_with_reason(CompilerDesugaringKind::Async, span, None); + let new_argument = hir::Arg { + hir_id: argument.hir_id, + pat: P(hir::Pat { + hir_id: new_argument_id, + node: hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, + new_argument_id, ident, None), + span: desugared_span, + }), + source: hir::ArgSource::AsyncFn(argument.pat.hir_id), + }; + + let construct_stmt = |this: &mut LoweringContext<'_>, pat: P, + init_pat_id: hir::HirId| { + hir::Stmt { + hir_id: this.next_id(), + node: hir::StmtKind::Local(P(hir::Local { + pat, + // We explicitly do not specify the type for any statements. 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 be valid in this binding. At the time of writing (13 Mar 19), + // `impl_trait_in_bindings` is not stable. + ty: None, + init: Some(P(hir::Expr { + span, + node: hir::ExprKind::Path(hir::QPath::Resolved(None, P(hir::Path { + span, + res: Res::Local(init_pat_id), + segments: hir_vec![ hir::PathSegment::from_ident(ident) ], + }))), + attrs: ThinVec::new(), + hir_id: this.next_id(), + })), + hir_id: this.next_id(), + span: desugared_span, + attrs: ThinVec::new(), + source: hir::LocalSource::AsyncFn, + })), + span: desugared_span, + } + }; + + let new_statements = if is_simple_argument { + // If this is the simple case, then we only insert one statement that is + // `let = ;`. We re-use the original argument's pattern so that + // `HirId`s are densely assigned. + (construct_stmt(this, argument.pat, new_argument_id), None) + } else { + // If this is not the simple case, then we construct two statements: + // + // ``` + // let __argN = __argN; + // let = __argN; + // ``` + // + // The first statement moves the argument into the closure and thus ensures + // that the drop order is correct. + // + // The second statement creates the bindings that the user wrote. + + // Construct the `let mut __argN = __argN;` statement. It must be a mut binding + // because the user may have specified a `ref mut` binding in the next + // statement. + let hir_id = this.next_id(); + let move_stmt = construct_stmt( + this, + P(hir::Pat { + hir_id, + node: hir::PatKind::Binding(hir::BindingAnnotation::Mutable, + hir_id, ident, None), + span: desugared_span, + }), + new_argument_id, + ); + + // Construct the `let = __argN;` statement. We re-use the original + // argument's pattern so that `HirId`s are densely assigned. + let pattern_stmt = construct_stmt(this, argument.pat, hir_id); + (move_stmt, Some(pattern_stmt)) + }; + + arguments.push(new_argument); + statements.push(new_statements); } + + let async_expr = this.make_async_expr( + CaptureBy::Value, closure_id, None, body.span, + |this| { + let mut stmts = vec![]; + for (move_stmt, pattern_stmt) in statements.drain(..) { + // Insert the `let __argN = __argN` statement first. + stmts.push(move_stmt); + // Then insert the `let = __argN` statement, if there is one. + if let Some(pattern_stmt) = pattern_stmt { + stmts.push(pattern_stmt); + } + } + let body = this.lower_block_with_stmts(body, false, stmts); + this.expr_block(body, ThinVec::new()) + }); + (HirVec::from(arguments), this.expr(body.span, async_expr, ThinVec::new())) }) } @@ -3049,7 +3208,6 @@ fn lower_item_kind( self.lower_use_tree(use_tree, &prefix, id, vis, ident, attrs) } ItemKind::Static(ref t, m, ref e) => { - let value = self.lower_body(None, |this| this.lower_expr(e)); hir::ItemKind::Static( self.lower_ty( t, @@ -3060,11 +3218,10 @@ fn lower_item_kind( } ), self.lower_mutability(m), - value, + self.lower_const_body(e), ) } ItemKind::Const(ref t, ref e) => { - let value = self.lower_body(None, |this| this.lower_expr(e)); hir::ItemKind::Const( self.lower_ty( t, @@ -3074,29 +3231,31 @@ fn lower_item_kind( ImplTraitContext::Disallowed(ImplTraitPosition::Binding) } ), - value + self.lower_const_body(e) ) } ItemKind::Fn(ref decl, header, ref generics, ref body) => { let fn_def_id = self.resolver.definitions().local_def_id(id); self.with_new_scopes(|this| { + this.current_item = Some(ident.span); + // Note: we don't need to change the return type from `T` to // `impl Future` here because lower_body // only cares about the input argument patterns in the function // declaration (decl), not the return types. - let body_id = this.lower_async_body(decl, header.asyncness.node, body); + let body_id = this.lower_maybe_async_body(&decl, header.asyncness.node, body); + let (generics, fn_decl) = this.add_in_band_defs( generics, fn_def_id, AnonymousLifetimeMode::PassThrough, |this, idty| this.lower_fn_decl( - decl, + &decl, Some((fn_def_id, idty)), true, header.asyncness.node.opt_return_id() ), ); - this.current_item = Some(ident.span); hir::ItemKind::Fn( fn_decl, @@ -3478,7 +3637,7 @@ fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem { self.lower_ty(ty, ImplTraitContext::disallowed()), default .as_ref() - .map(|x| self.lower_body(None, |this| this.lower_expr(x))), + .map(|x| self.lower_const_body(x)), ), ), TraitItemKind::Method(ref sig, None) => { @@ -3493,7 +3652,7 @@ fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem { (generics, hir::TraitItemKind::Method(sig, hir::TraitMethod::Required(names))) } TraitItemKind::Method(ref sig, Some(ref body)) => { - let body_id = self.lower_body(Some(&sig.decl), |this| { + let body_id = self.lower_fn_body(&sig.decl, |this| { let body = this.lower_block(body, false); this.expr_block(body, ThinVec::new()) }); @@ -3557,18 +3716,18 @@ fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem { let impl_item_def_id = self.resolver.definitions().local_def_id(i.id); let (generics, node) = match i.node { - ImplItemKind::Const(ref ty, ref expr) => { - let body_id = self.lower_body(None, |this| this.lower_expr(expr)); - ( - self.lower_generics(&i.generics, ImplTraitContext::disallowed()), - hir::ImplItemKind::Const( - self.lower_ty(ty, ImplTraitContext::disallowed()), - body_id, - ), - ) - } + ImplItemKind::Const(ref ty, ref expr) => ( + self.lower_generics(&i.generics, ImplTraitContext::disallowed()), + hir::ImplItemKind::Const( + self.lower_ty(ty, ImplTraitContext::disallowed()), + self.lower_const_body(expr), + ), + ), ImplItemKind::Method(ref sig, ref body) => { - let body_id = self.lower_async_body(&sig.decl, sig.header.asyncness.node, body); + self.current_item = Some(i.span); + let body_id = self.lower_maybe_async_body( + &sig.decl, sig.header.asyncness.node, body + ); let impl_trait_return_allow = !self.is_in_trait_impl; let (generics, sig) = self.lower_method_sig( &i.generics, @@ -3577,7 +3736,6 @@ fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem { impl_trait_return_allow, sig.header.asyncness.node.opt_return_id(), ); - self.current_item = Some(i.span); (generics, hir::ImplItemKind::Method(sig, body_id)) } @@ -3973,7 +4131,7 @@ fn lower_anon_const(&mut self, c: &AnonConst) -> hir::AnonConst { self.with_new_scopes(|this| { hir::AnonConst { hir_id: this.lower_node_id(c.id), - body: this.lower_body(None, |this| this.lower_expr(&c.value)), + body: this.lower_const_body(&c.value), } }) } @@ -4161,7 +4319,7 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr { // Transform `async |x: u8| -> X { ... }` into // `|x: u8| future_from_generator(|| -> X { ... })`. - let body_id = this.lower_body(Some(&outer_decl), |this| { + let body_id = this.lower_fn_body(&outer_decl, |this| { let async_ret_ty = if let FunctionRetTy::Ty(ty) = &decl.output { Some(&**ty) } else { None }; @@ -4187,7 +4345,7 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr { self.with_new_scopes(|this| { this.current_item = Some(fn_decl_span); let mut is_generator = false; - let body_id = this.lower_body(Some(decl), |this| { + let body_id = this.lower_fn_body(decl, |this| { let e = this.lower_expr(body); is_generator = this.is_generator; e diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 75799a19031..2d3de5af992 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -699,6 +699,19 @@ pub fn is_argument(&self, id: NodeId) -> bool { } } + /// Returns the `HirId` of this pattern, or, if this is an `async fn` desugaring, the `HirId` + /// of the original pattern that the user wrote. + pub fn original_pat_of_argument(&self, arg: &'hir Arg) -> &'hir Pat { + match &arg.source { + ArgSource::Normal => &*arg.pat, + ArgSource::AsyncFn(hir_id) => match self.find_by_hir_id(*hir_id) { + Some(Node::Pat(pat)) | Some(Node::Binding(pat)) => &pat, + Some(Node::Local(local)) => &*local.pat, + x => bug!("ArgSource::AsyncFn HirId not a pattern/binding/local: {:?}", x), + }, + } + } + pub fn is_const_scope(&self, hir_id: HirId) -> bool { self.walk_parent_nodes(hir_id, |node| match *node { Node::Item(Item { node: ItemKind::Const(_, _), .. }) => true, diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 210c0c9225a..817c4cb540f 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1932,23 +1932,13 @@ pub struct Arg { pub source: ArgSource, } -impl Arg { - /// Returns the pattern representing the original binding for this argument. - pub fn original_pat(&self) -> &P { - match &self.source { - ArgSource::Normal => &self.pat, - ArgSource::AsyncFn(pat) => &pat, - } - } -} - /// Represents the source of an argument in a function header. #[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)] pub enum ArgSource { /// Argument as specified by the user. Normal, - /// Generated argument from `async fn` lowering, contains the original binding pattern. - AsyncFn(P), + /// Generated argument from `async fn` lowering, `HirId` is the original pattern. + AsyncFn(HirId), } /// Represents the header (not the body) of a function declaration. diff --git a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs index 944cc8a8b19..46785f7ada1 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -86,12 +86,14 @@ pub(super) fn try_report_anon_anon_conflict(&self) -> Option { let sub_is_ret_type = self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); - let span_label_var1 = match anon_arg_sup.original_pat().simple_ident() { + let arg_sup_pat = self.tcx().hir().original_pat_of_argument(anon_arg_sup); + let span_label_var1 = match arg_sup_pat.simple_ident() { Some(simple_ident) => format!(" from `{}`", simple_ident), None => String::new(), }; - let span_label_var2 = match anon_arg_sub.original_pat().simple_ident() { + let arg_sub_pat = self.tcx().hir().original_pat_of_argument(anon_arg_sub); + let span_label_var2 = match arg_sub_pat.simple_ident() { Some(simple_ident) => format!(" into `{}`", simple_ident), None => String::new(), }; diff --git a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 2d7587b11b6..1eb2af2bd58 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -95,7 +95,8 @@ pub(super) fn try_report_named_anon_conflict(&self) -> Option ( format!("the type of `{}`", simple_ident), format!("the type of `{}`", simple_ident), diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 593a09b6866..f48da4762b7 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -2416,7 +2416,8 @@ fn report_elision_failure( let help_name = if let Some(body) = parent { let arg = &self.tcx.hir().body(body).arguments[index]; - format!("`{}`", self.tcx.hir().hir_to_pretty_string(arg.original_pat().hir_id)) + let original_pat = self.tcx.hir().original_pat_of_argument(arg); + format!("`{}`", self.tcx.hir().hir_to_pretty_string(original_pat.hir_id)) } else { format!("argument {}", index + 1) }; diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 123b46cb048..a5d92d6c88f 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -84,11 +84,23 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Body<' // HACK(eddyb) Avoid having RustCall on closures, // as it adds unnecessary (and wrong) auto-tupling. abi = Abi::Rust; - Some(ArgInfo(liberated_closure_env_ty(tcx, id, body_id), None, None, None)) + Some(ArgInfo { + ty: liberated_closure_env_ty(tcx, id, body_id), + span: None, + pattern: None, + user_pattern: None, + self_kind: None, + }) } ty::Generator(..) => { let gen_ty = tcx.body_tables(body_id).node_type(id); - Some(ArgInfo(gen_ty, None, None, None)) + Some(ArgInfo { + ty: gen_ty, + span: None, + pattern: None, + user_pattern: None, + self_kind: None, + }) } _ => None, }; @@ -126,7 +138,15 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Body<' opt_ty_info = None; self_arg = None; } - ArgInfo(fn_sig.inputs()[index], opt_ty_info, Some(&*arg.pat), self_arg) + + let original_pat = tcx.hir().original_pat_of_argument(arg); + ArgInfo { + ty: fn_sig.inputs()[index], + span: opt_ty_info, + pattern: Some(&*arg.pat), + user_pattern: Some(&original_pat), + self_kind: self_arg, + } }); let arguments = implicit_argument.into_iter().chain(explicit_arguments); @@ -614,10 +634,13 @@ fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, /////////////////////////////////////////////////////////////////////////// /// the main entry point for building MIR for a function -struct ArgInfo<'gcx>(Ty<'gcx>, - Option, - Option<&'gcx hir::Pat>, - Option); +struct ArgInfo<'gcx> { + ty: Ty<'gcx>, + span: Option, + pattern: Option<&'gcx hir::Pat>, + user_pattern: Option<&'gcx hir::Pat>, + self_kind: Option, +} fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, fn_id: hir::HirId, @@ -878,26 +901,23 @@ fn args_and_body(&mut self, -> BlockAnd<()> { // Allocate locals for the function arguments - for &ArgInfo(ty, _, pattern, _) in arguments.iter() { + for &ArgInfo { ty, span: _, pattern, user_pattern, self_kind: _ } in arguments.iter() { // If this is a simple binding pattern, give the local a name for // debuginfo and so that error reporting knows that this is a user // variable. For any other pattern the pattern introduces new // variables which will be named instead. - let mut name = None; - if let Some(pat) = pattern { + let (name, span) = if let Some(pat) = user_pattern { match pat.node { hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, _) - | hir::PatKind::Binding(hir::BindingAnnotation::Mutable, _, ident, _) => { - name = Some(ident.name); - } - _ => (), + | hir::PatKind::Binding(hir::BindingAnnotation::Mutable, _, ident, _) => + (Some(ident.name), pat.span), + _ => (None, pattern.map_or(self.fn_span, |pat| pat.span)) } - } - - let source_info = SourceInfo { - scope: OUTERMOST_SOURCE_SCOPE, - span: pattern.map_or(self.fn_span, |pat| pat.span) + } else { + (None, self.fn_span) }; + + let source_info = SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span, }; self.local_decls.push(LocalDecl { mutability: Mutability::Mut, ty, @@ -917,7 +937,13 @@ fn args_and_body(&mut self, // Function arguments always get the first Local indices after the return place let local = Local::new(index + 1); let place = Place::Base(PlaceBase::Local(local)); - let &ArgInfo(ty, opt_ty_info, pattern, ref self_binding) = arg_info; + let &ArgInfo { + ty, + span: opt_ty_info, + pattern, + user_pattern: _, + self_kind: ref self_binding + } = arg_info; // Make sure we drop (parts of) the argument even when not matched on. self.schedule_drop( diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 20e18d60f07..f084d3b9f28 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -948,16 +948,6 @@ fn visit_pat(&mut self, pat: &'tcx hir::Pat) { intravisit::walk_pat(self, pat); } - - fn visit_argument_source(&mut self, s: &'tcx hir::ArgSource) { - match s { - // Don't visit the pattern in `ArgSource::AsyncFn`, it contains a pattern which has - // a `NodeId` w/out a type, as it is only used for getting the name of the original - // pattern for diagnostics where only an `hir::Arg` is present. - hir::ArgSource::AsyncFn(..) => {}, - _ => intravisit::walk_argument_source(self, s), - } - } } //////////////////////////////////////////////////////////////////////////////////////////// @@ -1147,16 +1137,6 @@ fn visit_pat(&mut self, pattern: &'tcx hir::Pat) { intravisit::walk_pat(self, pattern); } - fn visit_argument_source(&mut self, s: &'tcx hir::ArgSource) { - match s { - // Don't visit the pattern in `ArgSource::AsyncFn`, it contains a pattern which has - // a `NodeId` w/out a type, as it is only used for getting the name of the original - // pattern for diagnostics where only an `hir::Arg` is present. - hir::ArgSource::AsyncFn(..) => {}, - _ => intravisit::walk_argument_source(self, s), - } - } - fn visit_local(&mut self, local: &'tcx hir::Local) { if let Some(ref init) = local.init { if self.check_expr_pat_type(init.hir_id, init.span) { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 82d198f0b78..3ada80b3e8b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1009,16 +1009,6 @@ fn visit_pat(&mut self, p: &'gcx hir::Pat) { // Don't descend into the bodies of nested closures fn visit_fn(&mut self, _: intravisit::FnKind<'gcx>, _: &'gcx hir::FnDecl, _: hir::BodyId, _: Span, _: hir::HirId) { } - - fn visit_argument_source(&mut self, s: &'gcx hir::ArgSource) { - match s { - // Don't visit the pattern in `ArgSource::AsyncFn`, it contains a pattern which has - // a `NodeId` w/out a type, as it is only used for getting the name of the original - // pattern for diagnostics where only an `hir::Arg` is present. - hir::ArgSource::AsyncFn(..) => {}, - _ => intravisit::walk_argument_source(self, s), - } - } } /// When `check_fn` is invoked on a generator (i.e., a body that diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index a535f776dfe..6f8682e6467 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -311,16 +311,6 @@ fn visit_ty(&mut self, hir_ty: &'gcx hir::Ty) { let ty = self.resolve(&ty, &hir_ty.span); self.write_ty_to_tables(hir_ty.hir_id, ty); } - - fn visit_argument_source(&mut self, s: &'gcx hir::ArgSource) { - match s { - // Don't visit the pattern in `ArgSource::AsyncFn`, it contains a pattern which has - // a `NodeId` w/out a type, as it is only used for getting the name of the original - // pattern for diagnostics where only an `hir::Arg` is present. - hir::ArgSource::AsyncFn(..) => {}, - _ => intravisit::walk_argument_source(self, s), - } - } } impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index e68ad6a7c3b..319adea6b86 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2018,8 +2018,9 @@ fn clean(&self, cx: &DocContext<'_>) -> Arguments { Arguments { values: self.0.iter().enumerate().map(|(i, ty)| { + let original_pat = cx.tcx.hir().original_pat_of_argument(&body.arguments[i]); Argument { - name: name_from_pat(&body.arguments[i].original_pat()), + name: name_from_pat(original_pat), type_: ty.clean(cx), } }).collect() diff --git a/src/test/ui/async-await/issues/issue-61187.rs b/src/test/ui/async-await/issues/issue-61187.rs new file mode 100644 index 00000000000..8b939b43b8b --- /dev/null +++ b/src/test/ui/async-await/issues/issue-61187.rs @@ -0,0 +1,9 @@ +// edition:2018 +#![feature(async_await)] + +fn main() { +} + +async fn response(data: Vec) { + data.reverse(); //~ ERROR E0596 +} diff --git a/src/test/ui/async-await/issues/issue-61187.stderr b/src/test/ui/async-await/issues/issue-61187.stderr new file mode 100644 index 00000000000..a0314226320 --- /dev/null +++ b/src/test/ui/async-await/issues/issue-61187.stderr @@ -0,0 +1,11 @@ +error[E0596]: cannot borrow `data` as mutable, as it is not declared as mutable + --> $DIR/issue-61187.rs:8:5 + | +LL | async fn response(data: Vec) { + | ---- help: consider changing this to be mutable: `mut data` +LL | data.reverse(); + | ^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. -- 2.44.0