From 749349fc9f7b12f212bca9ba2297e463328cb701 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 13 Mar 2019 17:42:23 -0700 Subject: [PATCH] Refactor async fn return type lowering async fn now lowers directly to an existential type declaration rather than reusing the `impl Trait` return type lowering. As part of this, it lowers all argument-position elided lifetimes using the in-band-lifetimes machinery, creating fresh parameter names for each of them, using each lifetime parameter as a generic argument to the generated existential type. This doesn't currently successfully allow multiple argument-position elided lifetimes since `existential type` doesn't yet support multiple lifetimes where neither outlive the other. This requires a separate fix. --- src/librustc/hir/intravisit.rs | 6 +- src/librustc/hir/lowering.rs | 667 ++++++++++-------- src/librustc/hir/mod.rs | 12 + src/librustc/infer/opaque_types/mod.rs | 80 ++- src/librustc/middle/resolve_lifetime.rs | 2 +- src/librustc_typeck/collect.rs | 1 + src/test/run-pass/async-await.rs | 20 +- src/test/ui/async-fn-multiple-lifetimes.rs | 5 +- .../ui/async-fn-multiple-lifetimes.stderr | 34 +- src/test/ui/issues/issue-54974.rs | 16 + src/test/ui/issues/issue-55324.rs | 14 + src/test/ui/issues/issue-58885.rs | 21 + src/test/ui/issues/issue-59001.rs | 17 + 13 files changed, 556 insertions(+), 339 deletions(-) create mode 100644 src/test/ui/issues/issue-54974.rs create mode 100644 src/test/ui/issues/issue-55324.rs create mode 100644 src/test/ui/issues/issue-58885.rs create mode 100644 src/test/ui/issues/issue-59001.rs diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 6689b0c26fb..c2265eeb30d 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -490,7 +490,11 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) { visitor.visit_ty(ty); visitor.visit_generics(generics) } - ItemKind::Existential(ExistTy { ref generics, ref bounds, impl_trait_fn: _ }) => { + ItemKind::Existential(ExistTy { + ref generics, + ref bounds, + .. + }) => { visitor.visit_id(item.hir_id); walk_generics(visitor, generics); walk_list!(visitor, visit_param_bound, bounds); diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 7dfb16602a3..2a255523676 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -66,7 +66,7 @@ use syntax::symbol::{keywords, Symbol}; use syntax::tokenstream::{TokenStream, TokenTree}; use syntax::parse::token::Token; use syntax::visit::{self, Visitor}; -use syntax_pos::{Span, MultiSpan}; +use syntax_pos::Span; const HIR_ID_COUNTER_LOCKED: u32 = 0xFFFFFFFF; @@ -318,6 +318,49 @@ enum AnonymousLifetimeMode { /// Pass responsibility to `resolve_lifetime` code for all cases. PassThrough, + + /// Used in the return types of `async fn` where there exists + /// exactly one argument-position elided lifetime. + /// + /// In `async fn`, we lower the arguments types using the `CreateParameter` + /// mode, meaning that non-`dyn` elided lifetimes are assigned a fresh name. + /// If any corresponding elided lifetimes appear in the output, we need to + /// replace them with references to the fresh name assigned to the corresponding + /// elided lifetime in the arguments. + /// + /// For **Modern cases**, replace the anonymous parameter with a + /// reference to a specific freshly-named lifetime that was + /// introduced in argument + /// + /// For **Dyn Bound** cases, pass responsibility to + /// `resole_lifetime` code. + Replace(LtReplacement), +} + +/// The type of elided lifetime replacement to perform on `async fn` return types. +#[derive(Copy, Clone)] +enum LtReplacement { + /// Fresh name introduced by the single non-dyn elided lifetime + /// in the arguments of the async fn. + Some(ParamName), + + /// There is no single non-dyn elided lifetime because no lifetimes + /// appeared in the arguments. + NoLifetimes, + + /// There is no single non-dyn elided lifetime because multiple + /// lifetimes appeared in the arguments. + MultipleLifetimes, +} + +/// Calculates the `LtReplacement` to use for elided lifetimes in the return +/// type based on the fresh elided lifetimes introduced in argument position. +fn get_elided_lt_replacement(arg_position_lifetimes: &[(Span, ParamName)]) -> LtReplacement { + match arg_position_lifetimes { + [] => LtReplacement::NoLifetimes, + [(_span, param)] => LtReplacement::Some(*param), + _ => LtReplacement::MultipleLifetimes, + } } struct ImplTraitTypeIdVisitor<'a> { ids: &'a mut SmallVec<[NodeId; 1]> } @@ -778,53 +821,63 @@ impl<'a> LoweringContext<'a> { let params = lifetimes_to_define .into_iter() - .map(|(span, hir_name)| { - let LoweredNodeId { node_id, hir_id } = self.next_id(); - - // Get the name we'll use to make the def-path. Note - // that collisions are ok here and this shouldn't - // really show up for end-user. - let (str_name, kind) = match hir_name { - ParamName::Plain(ident) => ( - ident.as_interned_str(), - hir::LifetimeParamKind::InBand, - ), - ParamName::Fresh(_) => ( - keywords::UnderscoreLifetime.name().as_interned_str(), - hir::LifetimeParamKind::Elided, - ), - ParamName::Error => ( - keywords::UnderscoreLifetime.name().as_interned_str(), - hir::LifetimeParamKind::Error, - ), - }; - - // Add a definition for the in-band lifetime def. - self.resolver.definitions().create_def_with_parent( - parent_id.index, - node_id, - DefPathData::LifetimeParam(str_name), - DefIndexAddressSpace::High, - Mark::root(), - span, - ); - - hir::GenericParam { - hir_id, - name: hir_name, - attrs: hir_vec![], - bounds: hir_vec![], - span, - pure_wrt_drop: false, - kind: hir::GenericParamKind::Lifetime { kind } - } - }) + .map(|(span, hir_name)| self.lifetime_to_generic_param( + span, hir_name, parent_id.index, + )) .chain(in_band_ty_params.into_iter()) .collect(); (params, res) } + /// Converts a lifetime into a new generic parameter. + fn lifetime_to_generic_param( + &mut self, + span: Span, + hir_name: ParamName, + parent_index: DefIndex, + ) -> hir::GenericParam { + let LoweredNodeId { node_id, hir_id } = self.next_id(); + + // Get the name we'll use to make the def-path. Note + // that collisions are ok here and this shouldn't + // really show up for end-user. + let (str_name, kind) = match hir_name { + ParamName::Plain(ident) => ( + ident.as_interned_str(), + hir::LifetimeParamKind::InBand, + ), + ParamName::Fresh(_) => ( + keywords::UnderscoreLifetime.name().as_interned_str(), + hir::LifetimeParamKind::Elided, + ), + ParamName::Error => ( + keywords::UnderscoreLifetime.name().as_interned_str(), + hir::LifetimeParamKind::Error, + ), + }; + + // Add a definition for the in-band lifetime def. + self.resolver.definitions().create_def_with_parent( + parent_index, + node_id, + DefPathData::LifetimeParam(str_name), + DefIndexAddressSpace::High, + Mark::root(), + span, + ); + + hir::GenericParam { + hir_id, + name: hir_name, + attrs: hir_vec![], + bounds: hir_vec![], + span, + pure_wrt_drop: false, + kind: hir::GenericParamKind::Lifetime { kind } + } + } + /// When there is a reference to some lifetime `'a`, and in-band /// lifetimes are enabled, then we want to push that lifetime into /// the vector of names to define later. In that case, it will get @@ -928,6 +981,13 @@ impl<'a> LoweringContext<'a> { |this| { this.collect_in_band_defs(parent_id, anonymous_lifetime_mode, |this| { let mut params = Vec::new(); + // Note: it is necessary to lower generics *before* calling `f`. + // When lowering `async fn`, there's a final step when lowering + // the return type that assumes that all in-scope lifetimes have + // already been added to either `in_scope_lifetimes` or + // `lifetimes_to_define`. If we swapped the order of these two, + // in-band-lifetimes introduced by generics or where-clauses + // wouldn't have been added yet. let generics = this.lower_generics( generics, ImplTraitContext::Universal(&mut params), @@ -1426,42 +1486,62 @@ impl<'a> LoweringContext<'a> { self.with_hir_id_owner(exist_ty_node_id, |lctx| { let LoweredNodeId { node_id: _, hir_id } = lctx.next_id(); - let exist_ty_item_kind = hir::ItemKind::Existential(hir::ExistTy { + let exist_ty_item = hir::ExistTy { generics: hir::Generics { params: lifetime_defs, where_clause: hir::WhereClause { hir_id, - predicates: Vec::new().into(), + predicates: hir_vec![], }, span, }, bounds: hir_bounds, impl_trait_fn: fn_def_id, - }); - let exist_ty_id = lctx.lower_node_id(exist_ty_node_id); - // Generate an `existential type Foo: Trait;` declaration. - trace!("creating existential type with id {:#?}", exist_ty_id); - - trace!("exist ty def index: {:#?}", exist_ty_def_index); - let exist_ty_item = hir::Item { - hir_id: exist_ty_id.hir_id, - ident: keywords::Invalid.ident(), - attrs: Default::default(), - node: exist_ty_item_kind, - vis: respan(span.shrink_to_lo(), hir::VisibilityKind::Inherited), - span: exist_ty_span, + origin: hir::ExistTyOrigin::ReturnImplTrait, }; - // Insert the item into the global list. This usually happens - // automatically for all AST items. But this existential type item - // does not actually exist in the AST. - lctx.insert_item(exist_ty_item); + trace!("exist ty from impl trait def index: {:#?}", exist_ty_def_index); + let exist_ty_id = lctx.generate_existential_type( + exist_ty_node_id, + exist_ty_item, + span, + exist_ty_span, + ); // `impl Trait` now just becomes `Foo<'a, 'b, ..>`. hir::TyKind::Def(hir::ItemId { id: exist_ty_id.hir_id }, lifetimes) }) } + /// Registers a new existential type with the proper NodeIds and + /// returns the lowered node ID for the existential type. + fn generate_existential_type( + &mut self, + exist_ty_node_id: NodeId, + exist_ty_item: hir::ExistTy, + span: Span, + exist_ty_span: Span, + ) -> LoweredNodeId { + let exist_ty_item_kind = hir::ItemKind::Existential(exist_ty_item); + let exist_ty_id = self.lower_node_id(exist_ty_node_id); + // Generate an `existential type Foo: Trait;` declaration. + trace!("registering existential type with id {:#?}", exist_ty_id); + let exist_ty_item = hir::Item { + hir_id: exist_ty_id.hir_id, + ident: keywords::Invalid.ident(), + attrs: Default::default(), + node: exist_ty_item_kind, + vis: respan(span.shrink_to_lo(), hir::VisibilityKind::Inherited), + span: exist_ty_span, + }; + + // Insert the item into the global item list. This usually happens + // automatically for all AST items. But this existential type item + // does not actually exist in the AST. + self.insert_item(exist_ty_item); + exist_ty_id + } + fn lifetimes_from_impl_trait_bounds( &mut self, exist_ty_id: NodeId, @@ -1569,9 +1649,6 @@ impl<'a> LoweringContext<'a> { name, })); - // We need to manually create the ids here, because the - // definitions will go into the explicit `existential type` - // declaration and thus need to have their owner set to that item let def_node_id = self.context.sess.next_node_id(); let LoweredNodeId { node_id: _, hir_id } = self.context.lower_node_id_with_owner(def_node_id, self.exist_ty_id); @@ -2108,23 +2185,42 @@ impl<'a> LoweringContext<'a> { impl_trait_return_allow: bool, make_ret_async: Option, ) -> P { - let inputs = decl.inputs - .iter() - .map(|arg| { - if let Some((_, ref mut ibty)) = in_band_ty_params { - self.lower_ty_direct(&arg.ty, ImplTraitContext::Universal(ibty)) - } else { - self.lower_ty_direct(&arg.ty, ImplTraitContext::disallowed()) - } - }) - .collect::>(); + let lt_mode = if make_ret_async.is_some() { + // In `async fn`, argument-position elided lifetimes + // must be transformed into fresh generic parameters so that + // they can be applied to the existential return type. + AnonymousLifetimeMode::CreateParameter + } else { + self.anonymous_lifetime_mode + }; + + // Remember how many lifetimes were already around so that we can + // only look at the lifetime parameters introduced by the arguments. + let lifetime_count_before_args = self.lifetimes_to_define.len(); + let inputs = self.with_anonymous_lifetime_mode(lt_mode, |this| { + decl.inputs + .iter() + .map(|arg| { + if let Some((_, ibty)) = &mut in_band_ty_params { + this.lower_ty_direct(&arg.ty, ImplTraitContext::Universal(ibty)) + } else { + this.lower_ty_direct(&arg.ty, ImplTraitContext::disallowed()) + } + }) + .collect::>() + }); let output = if let Some(ret_id) = make_ret_async { + // Calculate the `LtReplacement` to use for any return-position elided + // lifetimes based on the elided lifetime parameters introduced in the args. + let lt_replacement = get_elided_lt_replacement( + &self.lifetimes_to_define[lifetime_count_before_args..] + ); self.lower_async_fn_ret_ty( - &inputs, &decl.output, in_band_ty_params.expect("make_ret_async but no fn_def_id").0, ret_id, + lt_replacement, ) } else { match decl.output { @@ -2173,233 +2269,171 @@ impl<'a> LoweringContext<'a> { }) } - // Transform `-> T` into `-> impl Future` for `async fn` + // Transform `-> T` for `async fn` into -> ExistTy { .. } + // combined with the following definition of `ExistTy`: + // + // existential type ExistTy: Future; // - // fn_span: the span of the async function declaration. Used for error reporting. // inputs: lowered types of arguments to the function. Used to collect lifetimes. // output: unlowered output type (`T` in `-> T`) // fn_def_id: DefId of the parent function. Used to create child impl trait definition. + // exist_ty_node_id: NodeId of the existential type that should be created. + // elided_lt_replacement: replacement for elided lifetimes in the return type fn lower_async_fn_ret_ty( &mut self, - inputs: &[hir::Ty], output: &FunctionRetTy, fn_def_id: DefId, - return_impl_trait_id: NodeId, + exist_ty_node_id: NodeId, + elided_lt_replacement: LtReplacement, ) -> hir::FunctionRetTy { - // Get lifetimes used in the input arguments to the function. Our output type must also - // have the same lifetime. - // FIXME(cramertj): multiple different lifetimes are not allowed because - // `impl Trait + 'a + 'b` doesn't allow for capture `'a` and `'b` where neither is a subset - // of the other. We really want some new lifetime that is a subset of all input lifetimes, - // but that doesn't exist at the moment. + let span = output.span(); - struct AsyncFnLifetimeCollector<'r, 'a: 'r> { - context: &'r mut LoweringContext<'a>, - // Lifetimes bound by HRTB. - currently_bound_lifetimes: Vec, - // Whether to count elided lifetimes. - // Disabled inside of `Fn` or `fn` syntax. - collect_elided_lifetimes: bool, - // The lifetime found. - // Multiple different or elided lifetimes cannot appear in async fn for now. - output_lifetime: Option<(hir::LifetimeName, Span)>, - } + let exist_ty_span = self.mark_span_with_reason( + CompilerDesugaringKind::Async, + span, + None, + ); - impl<'r, 'a: 'r, 'v> hir::intravisit::Visitor<'v> for AsyncFnLifetimeCollector<'r, 'a> { - fn nested_visit_map<'this>( - &'this mut self, - ) -> hir::intravisit::NestedVisitorMap<'this, 'v> { - hir::intravisit::NestedVisitorMap::None - } + let exist_ty_def_index = self + .resolver + .definitions() + .opt_def_index(exist_ty_node_id) + .unwrap(); - fn visit_generic_args(&mut self, span: Span, parameters: &'v hir::GenericArgs) { - // Don't collect elided lifetimes used inside of `Fn()` syntax. - if parameters.parenthesized { - let old_collect_elided_lifetimes = self.collect_elided_lifetimes; - self.collect_elided_lifetimes = false; - hir::intravisit::walk_generic_args(self, span, parameters); - self.collect_elided_lifetimes = old_collect_elided_lifetimes; - } else { - hir::intravisit::walk_generic_args(self, span, parameters); - } - } + self.allocate_hir_id_counter(exist_ty_node_id); - fn visit_ty(&mut self, t: &'v hir::Ty) { - // Don't collect elided lifetimes used inside of `fn()` syntax. - if let &hir::TyKind::BareFn(_) = &t.node { - let old_collect_elided_lifetimes = self.collect_elided_lifetimes; - self.collect_elided_lifetimes = false; - - // Record the "stack height" of `for<'a>` lifetime bindings - // to be able to later fully undo their introduction. - let old_len = self.currently_bound_lifetimes.len(); - hir::intravisit::walk_ty(self, t); - self.currently_bound_lifetimes.truncate(old_len); - - self.collect_elided_lifetimes = old_collect_elided_lifetimes; - } else { - hir::intravisit::walk_ty(self, t); - } - } - - fn visit_poly_trait_ref( - &mut self, - trait_ref: &'v hir::PolyTraitRef, - modifier: hir::TraitBoundModifier, - ) { - // Record the "stack height" of `for<'a>` lifetime bindings - // to be able to later fully undo their introduction. - let old_len = self.currently_bound_lifetimes.len(); - hir::intravisit::walk_poly_trait_ref(self, trait_ref, modifier); - self.currently_bound_lifetimes.truncate(old_len); - } - - fn visit_generic_param(&mut self, param: &'v hir::GenericParam) { - // Record the introduction of 'a in `for<'a> ...` - if let hir::GenericParamKind::Lifetime { .. } = param.kind { - // Introduce lifetimes one at a time so that we can handle - // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>` - let lt_name = hir::LifetimeName::Param(param.name); - self.currently_bound_lifetimes.push(lt_name); - } - - hir::intravisit::walk_generic_param(self, param); - } - - fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) { - let name = match lifetime.name { - hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => { - if self.collect_elided_lifetimes { - // Use `'_` for both implicit and underscore lifetimes in - // `abstract type Foo<'_>: SomeTrait<'_>;` - hir::LifetimeName::Underscore - } else { - return; - } - } - hir::LifetimeName::Param(_) => lifetime.name, - hir::LifetimeName::Error | hir::LifetimeName::Static => return, - }; - - if !self.currently_bound_lifetimes.contains(&name) { - if let Some((current_lt_name, current_lt_span)) = self.output_lifetime { - // We don't currently have a reliable way to desugar `async fn` with - // multiple potentially unrelated input lifetimes into - // `-> impl Trait + 'lt`, so we report an error in this case. - if current_lt_name != name { - struct_span_err!( - self.context.sess, - MultiSpan::from_spans(vec![current_lt_span, lifetime.span]), - E0709, - "multiple different lifetimes used in arguments of `async fn`", - ) - .span_label(current_lt_span, "first lifetime here") - .span_label(lifetime.span, "different lifetime here") - .help("`async fn` can only accept borrowed values \ - with identical lifetimes") - .emit() - } else if current_lt_name.is_elided() && name.is_elided() { - struct_span_err!( - self.context.sess, - MultiSpan::from_spans(vec![current_lt_span, lifetime.span]), - E0707, - "multiple elided lifetimes used in arguments of `async fn`", - ) - .span_label(current_lt_span, "first lifetime here") - .span_label(lifetime.span, "different lifetime here") - .help("consider giving these arguments named lifetimes") - .emit() - } - } else { - self.output_lifetime = Some((name, lifetime.span)); - } - } - } - } - - let bound_lifetime = { - let mut lifetime_collector = AsyncFnLifetimeCollector { - context: self, - currently_bound_lifetimes: Vec::new(), - collect_elided_lifetimes: true, - output_lifetime: None, - }; - - for arg in inputs { - hir::intravisit::walk_ty(&mut lifetime_collector, arg); - } - lifetime_collector.output_lifetime - }; - - let span = match output { - FunctionRetTy::Ty(ty) => ty.span, - FunctionRetTy::Default(span) => *span, - }; - - let impl_trait_ty = self.lower_existential_impl_trait( - span, Some(fn_def_id), return_impl_trait_id, |this| { - let output_ty = match output { - FunctionRetTy::Ty(ty) => { - this.lower_ty(ty, ImplTraitContext::Existential(Some(fn_def_id))) - } - FunctionRetTy::Default(span) => { - let LoweredNodeId { node_id: _, hir_id } = this.next_id(); - P(hir::Ty { - hir_id, - node: hir::TyKind::Tup(hir_vec![]), - span: *span, - }) - } - }; - - // "" - let LoweredNodeId { node_id: _, hir_id } = this.next_id(); - let future_params = P(hir::GenericArgs { - args: hir_vec![], - bindings: hir_vec![hir::TypeBinding { - ident: Ident::from_str(FN_OUTPUT_NAME), - ty: output_ty, - hir_id, + let (exist_ty_node_id, lifetime_params) = self.with_hir_id_owner(exist_ty_node_id, |this| { + let future_bound = this.with_anonymous_lifetime_mode( + AnonymousLifetimeMode::Replace(elided_lt_replacement), + |this| this.lower_async_fn_output_type_to_future_bound( + output, + fn_def_id, span, - }], - parenthesized: false, - }); + ), + ); - let future_path = - this.std_path(span, &["future", "Future"], Some(future_params), false); + // Calculate all the lifetimes that should be captured + // by the existential type. This should include all in-scope + // lifetime parameters, including those defined in-band. + // + // Note: this must be done after lowering the output type, + // as the output type may introduce new in-band lifetimes. + let lifetime_params: Vec<(Span, ParamName)> = + this.in_scope_lifetimes + .iter().cloned() + .map(|ident| (ident.span, ParamName::Plain(ident))) + .chain(this.lifetimes_to_define.iter().cloned()) + .collect(); + + let generic_params = + lifetime_params + .iter().cloned() + .map(|(span, hir_name)| { + this.lifetime_to_generic_param(span, hir_name, exist_ty_def_index) + }) + .collect(); let LoweredNodeId { node_id: _, hir_id } = this.next_id(); - let mut bounds = vec![ - hir::GenericBound::Trait( - hir::PolyTraitRef { - trait_ref: hir::TraitRef { - path: future_path, - hir_ref_id: hir_id, - }, - bound_generic_params: hir_vec![], - span, + let exist_ty_item = hir::ExistTy { + generics: hir::Generics { + params: generic_params, + where_clause: hir::WhereClause { + hir_id, + predicates: hir_vec![], }, - hir::TraitBoundModifier::None - ), - ]; + span, + }, + bounds: hir_vec![future_bound], + impl_trait_fn: Some(fn_def_id), + origin: hir::ExistTyOrigin::AsyncFn, + }; - if let Some((name, span)) = bound_lifetime { - let LoweredNodeId { node_id: _, hir_id } = this.next_id(); - bounds.push(hir::GenericBound::Outlives( - hir::Lifetime { hir_id, name, span })); - } + trace!("exist ty from async fn def index: {:#?}", exist_ty_def_index); + let exist_ty_id = this.generate_existential_type( + exist_ty_node_id, + exist_ty_item, + span, + exist_ty_span, + ); - hir::HirVec::from(bounds) + (exist_ty_id.node_id, lifetime_params) }); + let generic_args = + lifetime_params + .iter().cloned() + .map(|(span, hir_name)| { + let LoweredNodeId { node_id: _, hir_id } = self.next_id(); + GenericArg::Lifetime(hir::Lifetime { + hir_id, + span, + name: hir::LifetimeName::Param(hir_name), + }) + }) + .collect(); + + let exist_ty_hir_id = self.lower_node_id(exist_ty_node_id).hir_id; + let exist_ty_ref = hir::TyKind::Def(hir::ItemId { id: exist_ty_hir_id }, generic_args); + let LoweredNodeId { node_id: _, hir_id } = self.next_id(); - let impl_trait_ty = P(hir::Ty { - node: impl_trait_ty, + hir::FunctionRetTy::Return(P(hir::Ty { + node: exist_ty_ref, span, hir_id, + })) + } + + /// Turns `-> T` into `Future` + fn lower_async_fn_output_type_to_future_bound( + &mut self, + output: &FunctionRetTy, + fn_def_id: DefId, + span: Span, + ) -> hir::GenericBound { + // Compute the `T` in `Future` from the return type. + let output_ty = match output { + FunctionRetTy::Ty(ty) => { + self.lower_ty(ty, ImplTraitContext::Existential(Some(fn_def_id))) + } + FunctionRetTy::Default(ret_ty_span) => { + let LoweredNodeId { node_id: _, hir_id } = self.next_id(); + P(hir::Ty { + hir_id, + node: hir::TyKind::Tup(hir_vec![]), + span: *ret_ty_span, + }) + } + }; + + // "" + let LoweredNodeId { node_id: _, hir_id } = self.next_id(); + let future_params = P(hir::GenericArgs { + args: hir_vec![], + bindings: hir_vec![hir::TypeBinding { + ident: Ident::from_str(FN_OUTPUT_NAME), + ty: output_ty, + hir_id, + span, + }], + parenthesized: false, }); - hir::FunctionRetTy::Return(impl_trait_ty) + // ::std::future::Future + let future_path = + self.std_path(span, &["future", "Future"], Some(future_params), false); + + let LoweredNodeId { node_id: _, hir_id } = self.next_id(); + hir::GenericBound::Trait( + hir::PolyTraitRef { + trait_ref: hir::TraitRef { + path: future_path, + hir_ref_id: hir_id, + }, + bound_generic_params: hir_vec![], + span, + }, + hir::TraitBoundModifier::None, + ) } fn lower_param_bound( @@ -2437,6 +2471,11 @@ impl<'a> LoweringContext<'a> { } AnonymousLifetimeMode::ReportError => self.new_error_lifetime(Some(l.id), span), + + AnonymousLifetimeMode::Replace(replacement) => { + let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(l.id); + self.replace_elided_lifetime(hir_id, span, replacement) + } }, ident => { self.maybe_collect_in_band_lifetime(ident); @@ -2461,6 +2500,39 @@ impl<'a> LoweringContext<'a> { } } + /// Replace a return-position elided lifetime with the elided lifetime + /// from the arguments. + fn replace_elided_lifetime( + &mut self, + hir_id: hir::HirId, + span: Span, + replacement: LtReplacement, + ) -> hir::Lifetime { + let multiple_or_none = match replacement { + LtReplacement::Some(name) => { + return hir::Lifetime { + hir_id, + span, + name: hir::LifetimeName::Param(name), + }; + } + LtReplacement::MultipleLifetimes => "multiple", + LtReplacement::NoLifetimes => "none", + }; + + let mut err = crate::middle::resolve_lifetime::report_missing_lifetime_specifiers( + self.sess, + span, + 1, + ); + err.note(&format!( + "return-position elided lifetimes require exactly one \ + input-position elided lifetime, found {}.", multiple_or_none)); + err.emit(); + + hir::Lifetime { hir_id, span, name: hir::LifetimeName::Error } + } + fn lower_generic_params( &mut self, params: &[GenericParam], @@ -2941,6 +3013,7 @@ impl<'a> LoweringContext<'a> { generics: self.lower_generics(generics, ImplTraitContext::disallowed()), bounds: self.lower_param_bounds(b, ImplTraitContext::disallowed()), impl_trait_fn: None, + origin: hir::ExistTyOrigin::ExistentialType, }), ItemKind::Enum(ref enum_definition, ref generics) => hir::ItemKind::Enum( hir::EnumDef { @@ -5083,7 +5156,8 @@ impl<'a> LoweringContext<'a> { /// with no explicit lifetime. fn elided_ref_lifetime(&mut self, span: Span) -> hir::Lifetime { match self.anonymous_lifetime_mode { - // Intercept when we are in an impl header and introduce an in-band lifetime. + // Intercept when we are in an impl header or async fn and introduce an in-band + // lifetime. // Hence `impl Foo for &u32` becomes `impl<'f> Foo for &'f u32` for some fresh // `'f`. AnonymousLifetimeMode::CreateParameter => { @@ -5099,6 +5173,10 @@ impl<'a> LoweringContext<'a> { AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span), AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span), + + AnonymousLifetimeMode::Replace(replacement) => { + self.new_replacement_lifetime(replacement, span) + } } } @@ -5133,6 +5211,12 @@ impl<'a> LoweringContext<'a> { /// sorts of cases are deprecated. This may therefore report a warning or an /// error, depending on the mode. fn elided_path_lifetimes(&mut self, span: Span, count: usize) -> P<[hir::Lifetime]> { + (0..count) + .map(|_| self.elided_path_lifetime(span)) + .collect() + } + + fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime { match self.anonymous_lifetime_mode { // N.B., We intentionally ignore the create-parameter mode here // and instead "pass through" to resolve-lifetimes, which will then @@ -5140,21 +5224,16 @@ impl<'a> LoweringContext<'a> { // impl elision for deprecated forms like // // impl Foo for std::cell::Ref // note lack of '_ - AnonymousLifetimeMode::CreateParameter => {} + AnonymousLifetimeMode::CreateParameter | + // This is the normal case. + AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span), - AnonymousLifetimeMode::ReportError => { - return (0..count) - .map(|_| self.new_error_lifetime(None, span)) - .collect(); + AnonymousLifetimeMode::Replace(replacement) => { + self.new_replacement_lifetime(replacement, span) } - // This is the normal case. - AnonymousLifetimeMode::PassThrough => {} + AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span), } - - (0..count) - .map(|_| self.new_implicit_lifetime(span)) - .collect() } /// Invoked to create the lifetime argument(s) for an elided trait object @@ -5184,11 +5263,25 @@ impl<'a> LoweringContext<'a> { // This is the normal case. AnonymousLifetimeMode::PassThrough => {} + + // We don't need to do any replacement here as this lifetime + // doesn't refer to an elided lifetime elsewhere in the function + // signature. + AnonymousLifetimeMode::Replace(_) => {} } self.new_implicit_lifetime(span) } + fn new_replacement_lifetime( + &mut self, + replacement: LtReplacement, + span: Span, + ) -> hir::Lifetime { + let LoweredNodeId { node_id: _, hir_id } = self.next_id(); + self.replace_elided_lifetime(hir_id, span, replacement) + } + fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime { let LoweredNodeId { node_id: _, hir_id } = self.next_id(); diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 8509ddaccf7..58a27d3f78e 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1799,6 +1799,18 @@ pub struct ExistTy { pub generics: Generics, pub bounds: GenericBounds, pub impl_trait_fn: Option, + pub origin: ExistTyOrigin, +} + +/// Where the existential type came from +#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable)] +pub enum ExistTyOrigin { + /// `existential type Foo: Trait;` + ExistentialType, + /// `-> impl Trait` + ReturnImplTrait, + /// `async fn` + AsyncFn, } /// The various kinds of types recognized by the compiler. diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 8bd20843163..be9460ad86f 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -67,6 +67,9 @@ pub struct OpaqueTypeDecl<'tcx> { /// the fn body). (Ultimately, writeback is responsible for this /// check.) pub has_required_region_bounds: bool, + + /// The origin of the existential type + pub origin: hir::ExistTyOrigin, } impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { @@ -326,14 +329,39 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // There are two regions (`lr` and // `subst_arg`) which are not relatable. We can't // find a best choice. - self.tcx + let context_name = match opaque_defn.origin { + hir::ExistTyOrigin::ExistentialType => "existential type", + hir::ExistTyOrigin::ReturnImplTrait => "impl Trait", + hir::ExistTyOrigin::AsyncFn => "async fn", + }; + let msg = format!("ambiguous lifetime bound in `{}`", context_name); + let mut err = self.tcx .sess - .struct_span_err(span, "ambiguous lifetime bound in `impl Trait`") - .span_label( - span, - format!("neither `{}` nor `{}` outlives the other", lr, subst_arg), - ) - .emit(); + .struct_span_err(span, &msg); + + let lr_name = lr.to_string(); + let subst_arg_name = subst_arg.to_string(); + let label_owned; + let label = match (&*lr_name, &*subst_arg_name) { + ("'_", "'_") => "the elided lifetimes here do not outlive one another", + _ => { + label_owned = format!( + "neither `{}` nor `{}` outlives the other", + lr_name, + subst_arg_name, + ); + &label_owned + } + }; + err.span_label(span, label); + + if let hir::ExistTyOrigin::AsyncFn = opaque_defn.origin { + err.note("multiple unrelated lifetimes are not allowed in \ + `async fn`."); + err.note("if you're using argument-position elided lifetimes, consider \ + switching to a single named lifetime."); + } + err.emit(); least_region = Some(self.tcx.mk_region(ty::ReEmpty)); break; @@ -692,31 +720,41 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { parent_def_id == tcx.hir() .local_def_id_from_hir_id(opaque_parent_hir_id) }; - let in_definition_scope = match tcx.hir().find_by_hir_id(opaque_hir_id) { + let (in_definition_scope, origin) = + match tcx.hir().find_by_hir_id(opaque_hir_id) + { Some(Node::Item(item)) => match item.node { // impl trait hir::ItemKind::Existential(hir::ExistTy { impl_trait_fn: Some(parent), + origin, .. - }) => parent == self.parent_def_id, + }) => (parent == self.parent_def_id, origin), // named existential types hir::ItemKind::Existential(hir::ExistTy { impl_trait_fn: None, + origin, .. - }) => may_define_existential_type( - tcx, - self.parent_def_id, - opaque_hir_id, + }) => ( + may_define_existential_type( + tcx, + self.parent_def_id, + opaque_hir_id, + ), + origin, ), - _ => def_scope_default(), + _ => (def_scope_default(), hir::ExistTyOrigin::ExistentialType), }, Some(Node::ImplItem(item)) => match item.node { - hir::ImplItemKind::Existential(_) => may_define_existential_type( - tcx, - self.parent_def_id, - opaque_hir_id, + hir::ImplItemKind::Existential(_) => ( + may_define_existential_type( + tcx, + self.parent_def_id, + opaque_hir_id, + ), + hir::ExistTyOrigin::ExistentialType, ), - _ => def_scope_default(), + _ => (def_scope_default(), hir::ExistTyOrigin::ExistentialType), }, _ => bug!( "expected (impl) item, found {}", @@ -724,7 +762,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { ), }; if in_definition_scope { - return self.fold_opaque_ty(ty, def_id, substs); + return self.fold_opaque_ty(ty, def_id, substs, origin); } debug!( @@ -746,6 +784,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { ty: Ty<'tcx>, def_id: DefId, substs: SubstsRef<'tcx>, + origin: hir::ExistTyOrigin, ) -> Ty<'tcx> { let infcx = self.infcx; let tcx = infcx.tcx; @@ -795,6 +834,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { substs, concrete_ty: ty_var, has_required_region_bounds: !required_region_bounds.is_empty(), + origin, }, ); debug!("instantiate_opaque_types: ty_var={:?}", ty_var); diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 73e232a6a4f..3306bcae212 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -2891,7 +2891,7 @@ fn insert_late_bound_lifetimes( } } -fn report_missing_lifetime_specifiers( +pub fn report_missing_lifetime_specifiers( sess: &Session, span: Span, count: usize, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 5edb8c92a3b..f92fa2e9799 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1979,6 +1979,7 @@ fn explicit_predicates_of<'a, 'tcx>( ref bounds, impl_trait_fn, ref generics, + origin: _, }) => { let substs = InternalSubsts::identity_for_item(tcx, def_id); let opaque_ty = tcx.mk_opaque(def_id, substs); diff --git a/src/test/run-pass/async-await.rs b/src/test/run-pass/async-await.rs index 1843feed927..72af5162992 100644 --- a/src/test/run-pass/async-await.rs +++ b/src/test/run-pass/async-await.rs @@ -79,6 +79,11 @@ async fn async_fn(x: u8) -> u8 { x } +async fn generic_async_fn(x: T) -> T { + await!(wake_and_yield_once()); + x +} + async fn async_fn_with_borrow(x: &u8) -> u8 { await!(wake_and_yield_once()); *x @@ -96,14 +101,21 @@ fn async_fn_with_impl_future_named_lifetime<'a>(x: &'a u8) -> impl Future(x: &'a u8, _y: &'a u8) -> u8 { +/* FIXME(cramertj) support when `existential type T<'a, 'b>:;` works +async fn async_fn_multiple_args(x: &u8, _y: &u8) -> u8 { + await!(wake_and_yield_once()); + *x +} +*/ + +async fn async_fn_multiple_args_named_lifetime<'a>(x: &'a u8, _y: &'a u8) -> u8 { await!(wake_and_yield_once()); *x } fn async_fn_with_internal_borrow(y: u8) -> impl Future { async move { - await!(async_fn_with_borrow(&y)) + await!(async_fn_with_borrow_named_lifetime(&y)) } } @@ -162,6 +174,7 @@ fn main() { async_nonmove_block, async_closure, async_fn, + generic_async_fn, async_fn_with_internal_borrow, Foo::async_method, |x| { @@ -170,7 +183,6 @@ fn main() { } }, } - test_with_borrow! { async_block_with_borrow_named_lifetime, async_fn_with_borrow, @@ -178,7 +190,7 @@ fn main() { async_fn_with_impl_future_named_lifetime, |x| { async move { - await!(async_fn_with_named_lifetime_multiple_args(x, x)) + await!(async_fn_multiple_args_named_lifetime(x, x)) } }, } diff --git a/src/test/ui/async-fn-multiple-lifetimes.rs b/src/test/ui/async-fn-multiple-lifetimes.rs index 6156617c4da..fccc4fdb917 100644 --- a/src/test/ui/async-fn-multiple-lifetimes.rs +++ b/src/test/ui/async-fn-multiple-lifetimes.rs @@ -5,7 +5,7 @@ use std::ops::Add; async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} -//~^ ERROR multiple different lifetimes used in arguments of `async fn` +//~^ ERROR ambiguous lifetime bound in `async fn` async fn multiple_hrtb_and_single_named_lifetime_ok<'c>( _: impl for<'a> Add<&'a u8>, @@ -14,7 +14,6 @@ async fn multiple_hrtb_and_single_named_lifetime_ok<'c>( ) {} async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} -//~^ ERROR multiple elided lifetimes used -//~^^ ERROR missing lifetime specifier +//~^ ambiguous lifetime bound in `async fn` fn main() {} diff --git a/src/test/ui/async-fn-multiple-lifetimes.stderr b/src/test/ui/async-fn-multiple-lifetimes.stderr index 071349b23fa..8c3ee2bed83 100644 --- a/src/test/ui/async-fn-multiple-lifetimes.stderr +++ b/src/test/ui/async-fn-multiple-lifetimes.stderr @@ -1,32 +1,20 @@ -error[E0709]: multiple different lifetimes used in arguments of `async fn` - --> $DIR/async-fn-multiple-lifetimes.rs:7:47 +error: ambiguous lifetime bound in `async fn` + --> $DIR/async-fn-multiple-lifetimes.rs:7:65 | LL | async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} - | ^^ ^^ different lifetime here - | | - | first lifetime here + | ^ neither `'a` nor `'b` outlives the other | - = help: `async fn` can only accept borrowed values with identical lifetimes + = note: multiple unrelated lifetimes are not allowed in `async fn`. + = note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime. -error[E0707]: multiple elided lifetimes used in arguments of `async fn` - --> $DIR/async-fn-multiple-lifetimes.rs:16:39 +error: ambiguous lifetime bound in `async fn` + --> $DIR/async-fn-multiple-lifetimes.rs:16:52 | LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} - | ^ ^ different lifetime here - | | - | first lifetime here + | ^ the elided lifetimes here do not outlive one another | - = help: consider giving these arguments named lifetimes + = note: multiple unrelated lifetimes are not allowed in `async fn`. + = note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime. -error[E0106]: missing lifetime specifier - --> $DIR/async-fn-multiple-lifetimes.rs:16:39 - | -LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} - | ^ expected lifetime parameter - | - = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `_` or `_` +error: aborting due to 2 previous errors -error: aborting due to 3 previous errors - -Some errors occurred: E0106, E0707, E0709. -For more information about an error, try `rustc --explain E0106`. diff --git a/src/test/ui/issues/issue-54974.rs b/src/test/ui/issues/issue-54974.rs new file mode 100644 index 00000000000..b2624ec92a1 --- /dev/null +++ b/src/test/ui/issues/issue-54974.rs @@ -0,0 +1,16 @@ +// compile-pass +// edition:2018 + +#![feature(async_await, await_macro, futures_api)] + +use std::sync::Arc; + +trait SomeTrait: Send + Sync + 'static { + fn do_something(&self); +} + +async fn my_task(obj: Arc) { + unimplemented!() +} + +fn main() {} diff --git a/src/test/ui/issues/issue-55324.rs b/src/test/ui/issues/issue-55324.rs new file mode 100644 index 00000000000..6160fbabd96 --- /dev/null +++ b/src/test/ui/issues/issue-55324.rs @@ -0,0 +1,14 @@ +// compile-pass +// edition:2018 + +#![feature(async_await, await_macro, futures_api)] + +use std::future::Future; + +#[allow(unused)] +async fn foo>(x: &i32, future: F) -> i32 { + let y = await!(future); + *x + y +} + +fn main() {} diff --git a/src/test/ui/issues/issue-58885.rs b/src/test/ui/issues/issue-58885.rs new file mode 100644 index 00000000000..559899194fb --- /dev/null +++ b/src/test/ui/issues/issue-58885.rs @@ -0,0 +1,21 @@ +// compile-pass +// edition:2018 + +#![feature(async_await, await_macro, futures_api)] + +struct Xyz { + a: u64, +} + +trait Foo {} + +impl Xyz { + async fn do_sth<'a>( + &'a self, foo: &'a dyn Foo + ) -> bool + { + true + } +} + +fn main() {} diff --git a/src/test/ui/issues/issue-59001.rs b/src/test/ui/issues/issue-59001.rs new file mode 100644 index 00000000000..a310653fbce --- /dev/null +++ b/src/test/ui/issues/issue-59001.rs @@ -0,0 +1,17 @@ +// compile-pass +// edition:2018 + +#![feature(async_await, await_macro, futures_api)] + +use std::future::Future; + +#[allow(unused)] +async fn enter<'a, F, R>(mut callback: F) +where + F: FnMut(&'a mut i32) -> R, + R: Future + 'a, +{ + unimplemented!() +} + +fn main() {}