//! in the HIR, especially for multiple identifiers.
use dep_graph::DepGraph;
-use hir;
+use hir::{self, ParamName};
use hir::HirVec;
use hir::map::{DefKey, DefPathData, Definitions};
use hir::def_id::{DefId, DefIndex, DefIndexAddressSpace, CRATE_DEF_INDEX};
// (i.e. it doesn't appear in the in_scope_lifetimes list), it is added
// to this list. The results of this list are then added to the list of
// lifetime definitions in the corresponding impl or function generics.
- lifetimes_to_define: Vec<(Span, hir::LifetimeName)>,
+ lifetimes_to_define: Vec<(Span, ParamName)>,
// Whether or not in-band lifetimes are being collected. This is used to
// indicate whether or not we're in a place where new lifetimes will result
});
if item_lowered {
- let item_lifetimes = match self.lctx.items.get(&item.id).unwrap().node {
+ let item_generics = match self.lctx.items.get(&item.id).unwrap().node {
hir::Item_::ItemImpl(_, _, _, ref generics, ..)
| hir::Item_::ItemTrait(_, _, ref generics, ..) => {
generics.params.clone()
_ => HirVec::new(),
};
- self.lctx.with_parent_impl_lifetime_defs(&item_lifetimes, |this| {
+ self.lctx.with_parent_impl_lifetime_defs(&item_generics, |this| {
let this = &mut ItemLowerer { lctx: this };
if let ItemKind::Impl(_, _, _, _, ref opt_trait_ref, _, _) = item.node {
this.with_trait_impl_ref(opt_trait_ref, |this| {
}
}
- fn allocate_hir_id_counter<T: Debug>(&mut self, owner: NodeId, debug: &T) {
+ fn allocate_hir_id_counter<T: Debug>(&mut self, owner: NodeId, debug: &T) -> LoweredNodeId {
if self.item_local_id_counters.insert(owner, 0).is_some() {
bug!(
"Tried to allocate item_local_id_counter for {:?} twice",
);
}
// Always allocate the first HirId for the owner itself
- self.lower_node_id_with_owner(owner, owner);
+ self.lower_node_id_with_owner(owner, owner)
}
fn lower_node_id_generic<F>(&mut self, ast_node_id: NodeId, alloc_hir_id: F) -> LoweredNodeId
{
let counter = self.item_local_id_counters
.insert(owner, HIR_ID_COUNTER_LOCKED)
- .unwrap();
+ .unwrap_or_else(|| panic!("No item_local_id_counters entry for {:?}", owner));
let def_index = self.resolver.definitions().opt_def_index(owner).unwrap();
self.current_hir_id_owner.push((def_index, counter));
let ret = f(self);
// that collisions are ok here and this shouldn't
// really show up for end-user.
let str_name = match hir_name {
- hir::LifetimeName::Name(n) => n.as_str(),
- hir::LifetimeName::Fresh(_) => keywords::UnderscoreLifetime.name().as_str(),
- hir::LifetimeName::Implicit
- | hir::LifetimeName::Underscore
- | hir::LifetimeName::Static => {
- span_bug!(span, "unexpected in-band lifetime name: {:?}", hir_name)
- }
+ ParamName::Plain(name) => name.as_str(),
+ ParamName::Fresh(_) => keywords::UnderscoreLifetime.name().as_str(),
};
// Add a definition for the in-band lifetime def
hir::GenericParam {
id: def_node_id,
+ name: hir_name,
+ attrs: hir_vec![],
+ bounds: hir_vec![],
span,
pure_wrt_drop: false,
- kind: hir::GenericParamKind::Lifetime {
- name: hir_name,
- bounds: vec![].into(),
- in_band: true,
- lifetime: hir::Lifetime {
- id: def_node_id,
- span,
- name: hir_name,
- }
- }
+ kind: hir::GenericParamKind::Lifetime { in_band: true }
}
})
.chain(in_band_ty_params.into_iter())
return;
}
- let hir_name = hir::LifetimeName::Name(name);
+ let hir_name = ParamName::Plain(name);
- if self.lifetimes_to_define
- .iter()
- .any(|(_, lt_name)| *lt_name == hir_name)
- {
+ if self.lifetimes_to_define.iter().any(|(_, lt_name)| *lt_name == hir_name) {
return;
}
/// When we have either an elided or `'_` lifetime in an impl
/// header, we convert it to
- fn collect_fresh_in_band_lifetime(&mut self, span: Span) -> hir::LifetimeName {
+ fn collect_fresh_in_band_lifetime(&mut self, span: Span) -> ParamName {
assert!(self.is_collecting_in_band_lifetimes);
let index = self.lifetimes_to_define.len();
- let hir_name = hir::LifetimeName::Fresh(index);
+ let hir_name = ParamName::Fresh(index);
self.lifetimes_to_define.push((span, hir_name));
hir_name
}
{
let old_len = self.in_scope_lifetimes.len();
let lt_def_names = params.iter().filter_map(|param| match param.kind {
- hir::GenericParamKind::Lifetime { .. } => Some(param.name()),
+ hir::GenericParamKind::Lifetime { .. } => Some(param.name.name()),
_ => None,
});
self.in_scope_lifetimes.extend(lt_def_names);
result
}
+ fn make_async_expr(
+ &mut self,
+ capture_clause: CaptureBy,
+ closure_node_id: NodeId,
+ ret_ty: Option<&Ty>,
+ body: impl FnOnce(&mut LoweringContext) -> hir::Expr,
+ ) -> hir::Expr_ {
+ let prev_is_generator = mem::replace(&mut self.is_generator, true);
+ let body_expr = body(self);
+ let span = body_expr.span;
+ let output = match ret_ty {
+ Some(ty) => FunctionRetTy::Ty(P(ty.clone())),
+ None => FunctionRetTy::Default(span),
+ };
+ let decl = FnDecl {
+ inputs: vec![],
+ output,
+ variadic: false
+ };
+ let body_id = self.record_body(body_expr, Some(&decl));
+ self.is_generator = prev_is_generator;
+
+ let capture_clause = self.lower_capture_clause(capture_clause);
+ let closure_hir_id = self.lower_node_id(closure_node_id).hir_id;
+ let decl = self.lower_fn_decl(&decl, None, /* impl trait allowed */ false, false);
+ let generator = hir::Expr {
+ id: closure_node_id,
+ hir_id: closure_hir_id,
+ node: hir::ExprClosure(capture_clause, decl, body_id, span,
+ Some(hir::GeneratorMovability::Static)),
+ span,
+ attrs: ThinVec::new(),
+ };
+
+ let unstable_span = self.allow_internal_unstable(CompilerDesugaringKind::Async, span);
+ let gen_future = self.expr_std_path(
+ unstable_span, &["raw", "future_from_generator"], ThinVec::new());
+ hir::ExprCall(P(gen_future), hir_vec![generator])
+ }
+
fn lower_body<F>(&mut self, decl: Option<&FnDecl>, f: F) -> hir::BodyId
where
F: FnOnce(&mut LoweringContext) -> hir::Expr,
),
unsafety: this.lower_unsafety(f.unsafety),
abi: f.abi,
- decl: this.lower_fn_decl(&f.decl, None, false),
+ decl: this.lower_fn_decl(&f.decl, None, false, false),
arg_names: this.lower_fn_args_to_names(&f.decl),
}))
},
let bounds = bounds
.iter()
.filter_map(|bound| match *bound {
- TraitTyParamBound(ref ty, TraitBoundModifier::None) => {
+ GenericBound::Trait(ref ty, TraitBoundModifier::None) => {
Some(self.lower_poly_trait_ref(ty, itctx))
}
- TraitTyParamBound(_, TraitBoundModifier::Maybe) => None,
- RegionTyParamBound(ref lifetime) => {
+ GenericBound::Trait(_, TraitBoundModifier::Maybe) => None,
+ GenericBound::Outlives(ref lifetime) => {
if lifetime_bound.is_none() {
lifetime_bound = Some(self.lower_lifetime(lifetime));
}
let span = t.span;
match itctx {
ImplTraitContext::Existential(fn_def_id) => {
-
- // We need to manually repeat the code of `next_id` because the lowering
- // needs to happen while the owner_id is pointing to the item itself,
- // because items are their own owners
- let exist_ty_node_id = self.sess.next_node_id();
-
- // Make sure we know that some funky desugaring has been going on here.
- // This is a first: there is code in other places like for loop
- // desugaring that explicitly states that we don't want to track that.
- // Not tracking it makes lints in rustc and clippy very fragile as
- // frequently opened issues show.
- let exist_ty_span = self.allow_internal_unstable(
- CompilerDesugaringKind::ExistentialReturnType,
- t.span,
- );
-
- // Pull a new definition from the ether
- let exist_ty_def_index = self
- .resolver
- .definitions()
- .create_def_with_parent(
- fn_def_id.index,
- exist_ty_node_id,
- DefPathData::ExistentialImplTrait,
- DefIndexAddressSpace::High,
- Mark::root(),
- exist_ty_span,
- );
-
- // the `t` is just for printing debug messages
- self.allocate_hir_id_counter(exist_ty_node_id, t);
-
- let hir_bounds = self.with_hir_id_owner(exist_ty_node_id, |lctx| {
- lctx.lower_bounds(bounds, itctx)
- });
-
- let (lifetimes, lifetime_defs) = self.lifetimes_from_impl_trait_bounds(
- exist_ty_node_id,
- exist_ty_def_index,
- &hir_bounds,
- );
-
- self.with_hir_id_owner(exist_ty_node_id, |lctx| {
- let exist_ty_item_kind = hir::ItemExistential(hir::ExistTy {
- generics: hir::Generics {
- params: lifetime_defs,
- where_clause: hir::WhereClause {
- id: lctx.next_id().node_id,
- predicates: Vec::new().into(),
- },
- span,
- },
- bounds: hir_bounds,
- impl_trait_fn: Some(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);
- // Set the name to `impl Bound1 + Bound2`
- let exist_ty_name = Symbol::intern(&pprust::ty_to_string(t));
-
- trace!("exist ty def index: {:#?}", exist_ty_def_index);
- let exist_ty_item = hir::Item {
- id: exist_ty_id.node_id,
- hir_id: exist_ty_id.hir_id,
- name: exist_ty_name,
- attrs: Default::default(),
- node: exist_ty_item_kind,
- vis: hir::Visibility::Inherited,
- span: exist_ty_span,
- };
-
- // 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.items.insert(exist_ty_id.node_id, exist_ty_item);
-
- // `impl Trait` now just becomes `Foo<'a, 'b, ..>`
- hir::TyImplTraitExistential(
- hir::ItemId {
- id: exist_ty_id.node_id
- },
- DefId::local(exist_ty_def_index),
- lifetimes,
- )
- })
+ // Set the name to `impl Bound1 + Bound2`
+ let exist_ty_name = Symbol::intern(&pprust::ty_to_string(t));
+ self.lower_existential_impl_trait(
+ span, fn_def_id, exist_ty_name, |this| this.lower_bounds(bounds, itctx))
}
ImplTraitContext::Universal(def_id) => {
let def_node_id = self.next_id().node_id;
span,
);
- let hir_bounds = self.lower_bounds(bounds, itctx);
+ let hir_bounds = self.lower_param_bounds(bounds, itctx);
// Set the name to `impl Bound1 + Bound2`
let name = Symbol::intern(&pprust::ty_to_string(t));
self.in_band_ty_params.push(hir::GenericParam {
id: def_node_id,
+ name: ParamName::Plain(name),
span,
pure_wrt_drop: false,
+ attrs: hir_vec![],
+ bounds: hir_bounds,
kind: hir::GenericParamKind::Type {
- name,
- bounds: hir_bounds,
default: None,
synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
- attrs: P::new(),
}
});
})
}
+ fn lower_existential_impl_trait(
+ &mut self,
+ span: Span,
+ fn_def_id: DefId,
+ exist_ty_name: Name,
+ lower_bounds: impl FnOnce(&mut LoweringContext) -> hir::TyParamBounds,
+ ) -> hir::Ty_ {
+ // We need to manually repeat the code of `next_id` because the lowering
+ // needs to happen while the owner_id is pointing to the item itself,
+ // because items are their own owners
+ let exist_ty_node_id = self.sess.next_node_id();
+
+ // Make sure we know that some funky desugaring has been going on here.
+ // This is a first: there is code in other places like for loop
+ // desugaring that explicitly states that we don't want to track that.
+ // Not tracking it makes lints in rustc and clippy very fragile as
+ // frequently opened issues show.
+ let exist_ty_span = self.allow_internal_unstable(
+ CompilerDesugaringKind::ExistentialReturnType,
+ span,
+ );
+
+ // Pull a new definition from the ether
+ let exist_ty_def_index = self
+ .resolver
+ .definitions()
+ .create_def_with_parent(
+ fn_def_id.index,
+ exist_ty_node_id,
+ DefPathData::ExistentialImplTrait,
+ DefIndexAddressSpace::High,
+ Mark::root(),
+ exist_ty_span,
+ );
+
+ self.allocate_hir_id_counter(exist_ty_node_id, &"existential impl trait");
+
+ let hir_bounds = self.with_hir_id_owner(exist_ty_node_id, lower_bounds);
+
+ let (lifetimes, lifetime_defs) = self.lifetimes_from_impl_trait_bounds(
+ exist_ty_node_id,
+ exist_ty_def_index,
+ &hir_bounds,
+ );
+
+ self.with_hir_id_owner(exist_ty_node_id, |lctx| {
+ let exist_ty_item_kind = hir::ItemExistential(hir::ExistTy {
+ generics: hir::Generics {
+ params: lifetime_defs,
+ where_clause: hir::WhereClause {
+ id: lctx.next_id().node_id,
+ predicates: Vec::new().into(),
+ },
+ span,
+ },
+ bounds: hir_bounds,
+ impl_trait_fn: Some(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 {
+ id: exist_ty_id.node_id,
+ hir_id: exist_ty_id.hir_id,
+ name: exist_ty_name,
+ attrs: Default::default(),
+ node: exist_ty_item_kind,
+ vis: hir::Visibility::Inherited,
+ span: exist_ty_span,
+ };
+
+ // 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.items.insert(exist_ty_id.node_id, exist_ty_item);
+
+ // `impl Trait` now just becomes `Foo<'a, 'b, ..>`
+ hir::TyImplTraitExistential(
+ hir::ItemId {
+ id: exist_ty_id.node_id
+ },
+ DefId::local(exist_ty_def_index),
+ lifetimes,
+ )
+ })
+ }
+
fn lifetimes_from_impl_trait_bounds(
&mut self,
exist_ty_id: NodeId,
parent_index: DefIndex,
- bounds: &hir::TyParamBounds,
+ bounds: &hir::GenericBounds,
) -> (HirVec<hir::Lifetime>, HirVec<hir::GenericParam>) {
// This visitor walks over impl trait bounds and creates defs for all lifetimes which
// appear in the bounds, excluding lifetimes that are created within the bounds.
fn visit_generic_param(&mut self, param: &'v hir::GenericParam) {
// Record the introduction of 'a in `for<'a> ...`
- if let hir::GenericParamKind::Lifetime { name, .. } = param.kind {
+ 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>`
- self.currently_bound_lifetimes.push(name);
+ let lt_name = hir::LifetimeName::Param(param.name);
+ self.currently_bound_lifetimes.push(lt_name);
}
hir::intravisit::walk_generic_param(self, param);
return;
}
}
- name @ hir::LifetimeName::Fresh(_) => name,
- name @ hir::LifetimeName::Name(_) => name,
+ hir::LifetimeName::Param(_) => lifetime.name,
hir::LifetimeName::Static => return,
};
if !self.currently_bound_lifetimes.contains(&name)
- && !self.already_defined_lifetimes.contains(&name)
- {
+ && !self.already_defined_lifetimes.contains(&name) {
self.already_defined_lifetimes.insert(name);
self.output_lifetimes.push(hir::Lifetime {
lifetime.span,
);
+ let name = match name {
+ hir::LifetimeName::Underscore => {
+ hir::ParamName::Plain(keywords::UnderscoreLifetime.name())
+ }
+ hir::LifetimeName::Param(param_name) => param_name,
+ _ => bug!("expected LifetimeName::Param or ParamName::Plain"),
+ };
+
self.output_lifetime_params.push(hir::GenericParam {
id: def_node_id,
+ name,
span: lifetime.span,
pure_wrt_drop: false,
+ attrs: hir_vec![],
+ bounds: hir_vec![],
kind: hir::GenericParamKind::Lifetime {
- name,
- bounds: vec![].into(),
in_band: false,
- lifetime: hir::Lifetime {
- id: def_node_id,
- span: lifetime.span,
- name,
- }
}
});
}
};
for bound in bounds {
- hir::intravisit::walk_ty_param_bound(&mut lifetime_collector, &bound);
+ hir::intravisit::walk_param_bound(&mut lifetime_collector, &bound);
}
(
fn lower_parenthesized_parameter_data(
&mut self,
- data: &ParenthesizedParameterData,
- ) -> (hir::PathParameters, bool) {
+ data: &ParenthesisedArgs,
+ ) -> (hir::GenericArgs, bool) {
// Switch to `PassThrough` mode for anonymous lifetimes: this
// means that we permit things like `&Ref<T>`, where `Ref` has
// a hidden lifetime parameter. This is needed for backwards
AnonymousLifetimeMode::PassThrough,
|this| {
const DISALLOWED: ImplTraitContext = ImplTraitContext::Disallowed;
- let &ParenthesizedParameterData { ref inputs, ref output, span } = data;
+ let &ParenthesisedArgs { ref inputs, ref output, span } = data;
let inputs = inputs.iter().map(|ty| this.lower_ty(ty, DISALLOWED)).collect();
let mk_tup = |this: &mut Self, tys, span| {
let LoweredNodeId { node_id, hir_id } = this.next_id();
(
hir::GenericArgs {
- parameters: hir_vec![GenericArg::Type(mk_tup(this, inputs, span))],
+ args: hir_vec![GenericArg::Type(mk_tup(this, inputs, span))],
bindings: hir_vec![
hir::TypeBinding {
id: this.next_id().node_id,
.collect()
}
+ // Lowers a function declaration.
+ //
+ // decl: the unlowered (ast) function declaration.
+ // fn_def_id: if `Some`, impl Trait arguments are lowered into generic parameters on the
+ // given DefId, otherwise impl Trait is disallowed. Must be `Some` if
+ // make_ret_async is true.
+ // impl_trait_return_allow: determines whether impl Trait can be used in return position.
+ // This guards against trait declarations and implementations where impl Trait is
+ // disallowed.
+ // make_ret_async: if enabled, converts `-> T` into `-> impl Future<Output = T>` in the
+ // return type. This is used for `async fn` declarations.
fn lower_fn_decl(
&mut self,
decl: &FnDecl,
fn_def_id: Option<DefId>,
impl_trait_return_allow: bool,
+ make_ret_async: bool,
) -> P<hir::FnDecl> {
- // NOTE: The two last parameters here have to do with impl Trait. If fn_def_id is Some,
- // then impl Trait arguments are lowered into generic parameters on the given
- // fn_def_id, otherwise impl Trait is disallowed. (for now)
- //
- // Furthermore, if impl_trait_return_allow is true, then impl Trait may be used in
- // return positions as well. This guards against trait declarations and their impls
- // where impl Trait is disallowed. (again for now)
- P(hir::FnDecl {
- inputs: decl.inputs
- .iter()
- .map(|arg| {
- if let Some(def_id) = fn_def_id {
- self.lower_ty(&arg.ty, ImplTraitContext::Universal(def_id))
- } else {
- self.lower_ty(&arg.ty, ImplTraitContext::Disallowed)
- }
- })
- .collect(),
- output: match decl.output {
+ let inputs = decl.inputs
+ .iter()
+ .map(|arg| {
+ if let Some(def_id) = fn_def_id {
+ self.lower_ty(&arg.ty, ImplTraitContext::Universal(def_id))
+ } else {
+ self.lower_ty(&arg.ty, ImplTraitContext::Disallowed)
+ }
+ })
+ .collect::<HirVec<_>>();
+
+ let output = if make_ret_async {
+ self.lower_async_fn_ret_ty(
+ &inputs, &decl.output, fn_def_id.expect("make_ret_async but no fn_def_id"))
+ } else {
+ match decl.output {
FunctionRetTy::Ty(ref ty) => match fn_def_id {
Some(def_id) if impl_trait_return_allow => {
hir::Return(self.lower_ty(ty, ImplTraitContext::Existential(def_id)))
_ => hir::Return(self.lower_ty(ty, ImplTraitContext::Disallowed)),
},
FunctionRetTy::Default(span) => hir::DefaultReturn(span),
- },
+ }
+ };
+
+ P(hir::FnDecl {
+ inputs,
+ output,
variadic: decl.variadic,
has_implicit_self: decl.inputs.get(0).map_or(false, |arg| match arg.ty.node {
TyKind::ImplicitSelf => true,
})
}
- fn lower_ty_param_bound(
+ // Transform `-> T` into `-> impl Future<Output = T>` for `async fn`
+ //
+ // 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.
+ fn lower_async_fn_ret_ty(
+ &mut self,
+ inputs: &[P<hir::Ty>],
+ output: &FunctionRetTy,
+ fn_def_id: DefId,
+ ) -> 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.
+
+ struct AsyncFnLifetimeCollector<'r, 'a: 'r> {
+ context: &'r mut LoweringContext<'a>,
+ // Lifetimes bound by HRTB
+ currently_bound_lifetimes: Vec<hir::LifetimeName>,
+ // 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)>,
+ }
+
+ 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
+ }
+
+ fn visit_path_parameters(&mut self, span: Span, parameters: &'v hir::PathParameters) {
+ // 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_path_parameters(self, span, parameters);
+ self.collect_elided_lifetimes = old_collect_elided_lifetimes;
+ } else {
+ hir::intravisit::walk_path_parameters(self, span, parameters);
+ }
+ }
+
+ fn visit_ty(&mut self, t: &'v hir::Ty) {
+ // Don't collect elided lifetimes used inside of `fn()` syntax
+ if let &hir::Ty_::TyBareFn(_) = &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::GenericParam::Lifetime(ref lt_def) = *param {
+ // Introduce lifetimes one at a time so that we can handle
+ // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>`
+ self.currently_bound_lifetimes.push(lt_def.lifetime.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;
+ }
+ }
+ name @ hir::LifetimeName::Fresh(_) => name,
+ name @ hir::LifetimeName::Name(_) => name,
+ 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,
+ current_lt_span.between(lifetime.span),
+ E0703,
+ "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,
+ current_lt_span.between(lifetime.span),
+ E0704,
+ "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 output_ty_name_owned;
+ let (output_ty_name, span) = match output {
+ FunctionRetTy::Ty(ty) => {
+ output_ty_name_owned = pprust::ty_to_string(ty);
+ (&*output_ty_name_owned, ty.span)
+ },
+ FunctionRetTy::Default(span) => ("()", *span),
+ };
+
+ // FIXME(cramertj) add lifetimes (see FIXME below) to the name
+ let exist_ty_name = Symbol::intern(&format!("impl Future<Output = {}>", output_ty_name));
+ let impl_trait_ty = self.lower_existential_impl_trait(
+ span, fn_def_id, exist_ty_name, |this| {
+ let output_ty = match output {
+ FunctionRetTy::Ty(ty) =>
+ this.lower_ty(ty, ImplTraitContext::Existential(fn_def_id)),
+ FunctionRetTy::Default(span) => {
+ let LoweredNodeId { node_id, hir_id } = this.next_id();
+ P(hir::Ty {
+ id: node_id,
+ hir_id: hir_id,
+ node: hir::TyTup(hir_vec![]),
+ span: *span,
+ })
+ }
+ };
+
+ let hir::Path { def, segments, .. } = this.std_path(span, &["future", "Future"], false);
+ let future_path = hir::Path {
+ segments: segments.map_slice(|mut v| {
+ v.last_mut().unwrap().parameters = Some(P(hir::PathParameters {
+ lifetimes: hir_vec![],
+ types: hir_vec![],
+ bindings: hir_vec![hir::TypeBinding {
+ name: Symbol::intern(FN_OUTPUT_NAME),
+ ty: output_ty,
+ id: this.next_id().node_id,
+ span,
+ }],
+ parenthesized: false,
+ }));
+ v
+ }),
+ def, span
+ };
+
+ // FIXME(cramertj) collect input lifetimes to function and add them to
+ // the output `impl Trait` type here.
+ let mut bounds = vec![
+ hir::TyParamBound::TraitTyParamBound(
+ hir::PolyTraitRef {
+ trait_ref: hir::TraitRef {
+ path: future_path,
+ ref_id: this.next_id().node_id,
+ },
+ bound_generic_params: hir_vec![],
+ span,
+ },
+ hir::TraitBoundModifier::None
+ ),
+ ];
+
+ if let Some((name, span)) = bound_lifetime {
+ bounds.push(hir::RegionTyParamBound(
+ hir::Lifetime { id: this.next_id().node_id, name, span }));
+ }
+
+ hir::HirVec::from(bounds)
+ });
+
+ let LoweredNodeId { node_id, hir_id } = self.next_id();
+ let impl_trait_ty = P(hir::Ty {
+ id: node_id,
+ node: impl_trait_ty,
+ span,
+ hir_id,
+ });
+
+ hir::FunctionRetTy::Return(impl_trait_ty)
+ }
+
+ fn lower_param_bound(
&mut self,
- tpb: &TyParamBound,
+ tpb: &GenericBound,
itctx: ImplTraitContext,
- ) -> hir::TyParamBound {
+ ) -> hir::GenericBound {
match *tpb {
- TraitTyParamBound(ref ty, modifier) => hir::TraitTyParamBound(
+ GenericBound::Trait(ref ty, modifier) => hir::GenericBound::Trait(
self.lower_poly_trait_ref(ty, itctx),
self.lower_trait_bound_modifier(modifier),
),
- RegionTyParamBound(ref lifetime) => {
- hir::RegionTyParamBound(self.lower_lifetime(lifetime))
+ GenericBound::Outlives(ref lifetime) => {
+ hir::GenericBound::Outlives(self.lower_lifetime(lifetime))
}
}
}
x if x == "'_" => match self.anonymous_lifetime_mode {
AnonymousLifetimeMode::CreateParameter => {
let fresh_name = self.collect_fresh_in_band_lifetime(span);
- self.new_named_lifetime(l.id, span, fresh_name)
+ self.new_named_lifetime(l.id, span, hir::LifetimeName::Param(fresh_name))
}
AnonymousLifetimeMode::PassThrough => {
},
name => {
self.maybe_collect_in_band_lifetime(span, name);
- self.new_named_lifetime(l.id, span, hir::LifetimeName::Name(name))
+ let param_name = ParamName::Plain(name);
+ self.new_named_lifetime(l.id, span, hir::LifetimeName::Param(param_name))
}
}
}
fn lower_generic_params(
&mut self,
params: &Vec<GenericParam>,
- add_bounds: &NodeMap<Vec<TyParamBound>>,
+ add_bounds: &NodeMap<Vec<GenericBound>>,
itctx: ImplTraitContext,
) -> hir::HirVec<hir::GenericParam> {
params.iter().map(|param| self.lower_generic_param(param, add_bounds, itctx)).collect()
fn lower_generic_param(&mut self,
param: &GenericParam,
- add_bounds: &NodeMap<Vec<TyParamBound>>,
+ add_bounds: &NodeMap<Vec<GenericBound>>,
itctx: ImplTraitContext)
-> hir::GenericParam {
+ let mut bounds = self.lower_param_bounds(¶m.bounds, itctx);
match param.kind {
- GenericParamKind::Lifetime { ref bounds, ref lifetime } => {
+ GenericParamKind::Lifetime => {
let was_collecting_in_band = self.is_collecting_in_band_lifetimes;
self.is_collecting_in_band_lifetimes = false;
- let lifetime = self.lower_lifetime(lifetime);
+ let lt = self.lower_lifetime(&Lifetime { id: param.id, ident: param.ident });
+ let param_name = match lt.name {
+ hir::LifetimeName::Param(param_name) => param_name,
+ _ => hir::ParamName::Plain(lt.name.name()),
+ };
let param = hir::GenericParam {
- id: lifetime.id,
- span: lifetime.span,
+ id: lt.id,
+ name: param_name,
+ span: lt.span,
pure_wrt_drop: attr::contains_name(¶m.attrs, "may_dangle"),
- kind: hir::GenericParamKind::Lifetime {
- name: lifetime.name,
- bounds: bounds.iter().map(|lt| self.lower_lifetime(lt)).collect(),
- in_band: false,
- lifetime,
- }
+ attrs: self.lower_attrs(¶m.attrs),
+ bounds,
+ kind: hir::GenericParamKind::Lifetime { in_band: false }
};
self.is_collecting_in_band_lifetimes = was_collecting_in_band;
param
}
- GenericParamKind::Type { ref bounds, ref default } => {
+ GenericParamKind::Type { ref default, .. } => {
let mut name = self.lower_ident(param.ident);
// Don't expose `Self` (recovered "keyword used as ident" parse error).
name = Symbol::gensym("Self");
}
- let mut bounds = self.lower_bounds(bounds, itctx);
let add_bounds = add_bounds.get(¶m.id).map_or(&[][..], |x| &x);
if !add_bounds.is_empty() {
bounds = bounds.into_iter()
- .chain(self.lower_bounds(add_bounds, itctx).into_iter())
+ .chain(self.lower_param_bounds(add_bounds, itctx).into_iter())
.collect();
}
hir::GenericParam {
id: self.lower_node_id(param.id).node_id,
+ name: hir::ParamName::Plain(name),
span: param.ident.span,
pure_wrt_drop: attr::contains_name(¶m.attrs, "may_dangle"),
+ attrs: self.lower_attrs(¶m.attrs),
+ bounds,
kind: hir::GenericParamKind::Type {
- name,
- bounds,
default: default.as_ref().map(|x| {
self.lower_ty(x, ImplTraitContext::Disallowed)
}),
.filter(|attr| attr.check_name("rustc_synthetic"))
.map(|_| hir::SyntheticTyParamKind::ImplTrait)
.next(),
- attrs: self.lower_attrs(¶m.attrs),
}
}
}
for pred in &generics.where_clause.predicates {
if let WherePredicate::BoundPredicate(ref bound_pred) = *pred {
'next_bound: for bound in &bound_pred.bounds {
- if let TraitTyParamBound(_, TraitBoundModifier::Maybe) = *bound {
+ if let GenericBound::Trait(_, TraitBoundModifier::Maybe) = *bound {
let report_error = |this: &mut Self| {
this.diagnostic().span_err(
bound_pred.bounded_ty.span,
.filter_map(|bound| match *bound {
// Ignore `?Trait` bounds.
// Tthey were copied into type parameters already.
- TraitTyParamBound(_, TraitBoundModifier::Maybe) => None,
- _ => Some(this.lower_ty_param_bound(
+ GenericBound::Trait(_, TraitBoundModifier::Maybe) => None,
+ _ => Some(this.lower_param_bound(
bound,
ImplTraitContext::Disallowed,
)),
}) => hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
span,
lifetime: self.lower_lifetime(lifetime),
- bounds: bounds
- .iter()
- .map(|bound| self.lower_lifetime(bound))
- .collect(),
+ bounds: self.lower_param_bounds(bounds, ImplTraitContext::Disallowed),
}),
WherePredicate::EqPredicate(WhereEqPredicate {
id,
}
}
- fn lower_bounds(
- &mut self,
- bounds: &[TyParamBound],
- itctx: ImplTraitContext,
- ) -> hir::TyParamBounds {
- bounds
- .iter()
- .map(|bound| self.lower_ty_param_bound(bound, itctx))
- .collect()
+ fn lower_param_bounds(&mut self, bounds: &[GenericBound], itctx: ImplTraitContext)
+ -> hir::GenericBounds {
+ bounds.iter().map(|bound| self.lower_param_bound(bound, itctx)).collect()
}
fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P<hir::Block> {
let value = self.lower_body(None, |this| this.lower_expr(e));
hir::ItemConst(self.lower_ty(t, ImplTraitContext::Disallowed), value)
}
- ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
+ 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| {
+ // Note: we can use non-async decl here because lower_body
+ // only cares about the input argument patterns,
+ // not the return types.
let body_id = this.lower_body(Some(decl), |this| {
- let body = this.lower_block(body, false);
- this.expr_block(body, ThinVec::new())
+ if let IsAsync::Async(async_node_id) = header.asyncness {
+ let async_expr = this.make_async_expr(
+ CaptureBy::Value, async_node_id, None,
+ |this| {
+ let body = this.lower_block(body, false);
+ this.expr_block(body, ThinVec::new())
+ });
+ this.expr(body.span, async_expr, ThinVec::new())
+ } else {
+ let body = this.lower_block(body, false);
+ this.expr_block(body, ThinVec::new())
+ }
});
+
let (generics, fn_decl) = this.add_in_band_defs(
generics,
fn_def_id,
AnonymousLifetimeMode::PassThrough,
- |this| this.lower_fn_decl(decl, Some(fn_def_id), true),
+ |this| this.lower_fn_decl(
+ decl, Some(fn_def_id), true, header.asyncness.is_async())
);
hir::ItemFn(
fn_decl,
- this.lower_unsafety(unsafety),
- this.lower_constness(constness),
- abi,
+ this.lower_fn_header(header),
generics,
body_id,
)
)
}
ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref items) => {
- let bounds = self.lower_bounds(bounds, ImplTraitContext::Disallowed);
+ let bounds = self.lower_param_bounds(bounds, ImplTraitContext::Disallowed);
let items = items
.iter()
.map(|item| self.lower_trait_item_ref(item))
}
ItemKind::TraitAlias(ref generics, ref bounds) => hir::ItemTraitAlias(
self.lower_generics(generics, ImplTraitContext::Disallowed),
- self.lower_bounds(bounds, ImplTraitContext::Disallowed),
+ self.lower_param_bounds(bounds, ImplTraitContext::Disallowed),
),
ItemKind::MacroDef(..) | ItemKind::Mac(..) => panic!("Shouldn't still be around"),
}
TraitItemKind::Type(ref bounds, ref default) => (
self.lower_generics(&i.generics, ImplTraitContext::Disallowed),
hir::TraitItemKind::Type(
- self.lower_bounds(bounds, ImplTraitContext::Disallowed),
+ self.lower_param_bounds(bounds, ImplTraitContext::Disallowed),
default
.as_ref()
.map(|x| self.lower_ty(x, ImplTraitContext::Disallowed)),
|this| {
(
// Disallow impl Trait in foreign items
- this.lower_fn_decl(fdec, None, false),
+ this.lower_fn_decl(fdec, None, false, false),
this.lower_fn_args_to_names(fdec),
)
},
impl_trait_return_allow: bool,
) -> hir::MethodSig {
hir::MethodSig {
- abi: sig.abi,
- unsafety: self.lower_unsafety(sig.unsafety),
- constness: self.lower_constness(sig.constness),
- decl: self.lower_fn_decl(&sig.decl, Some(fn_def_id), impl_trait_return_allow),
+ header: self.lower_fn_header(sig.header),
+ decl: self.lower_fn_decl(&sig.decl, Some(fn_def_id), impl_trait_return_allow, false),
}
}
}
}
+ fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
+ hir::FnHeader {
+ unsafety: self.lower_unsafety(h.unsafety),
+ asyncness: self.lower_asyncness(h.asyncness),
+ constness: self.lower_constness(h.constness),
+ abi: h.abi,
+ }
+ }
+
fn lower_unsafety(&mut self, u: Unsafety) -> hir::Unsafety {
match u {
Unsafety::Unsafe => hir::Unsafety::Unsafe,
}
}
+ fn lower_asyncness(&mut self, a: IsAsync) -> hir::IsAsync {
+ match a {
+ IsAsync::Async(_) => hir::IsAsync::Async,
+ IsAsync::NotAsync => hir::IsAsync::NotAsync,
+ }
+ }
+
fn lower_unop(&mut self, u: UnOp) -> hir::UnOp {
match u {
UnOp::Deref => hir::UnDeref,
arms.iter().map(|x| self.lower_arm(x)).collect(),
hir::MatchSource::Normal,
),
- ExprKind::Closure(capture_clause, movability, ref decl, ref body, fn_decl_span) => {
+ ExprKind::Async(capture_clause, closure_node_id, ref block) => {
+ self.make_async_expr(capture_clause, closure_node_id, None, |this| {
+ this.with_new_scopes(|this| {
+ let block = this.lower_block(block, false);
+ this.expr_block(block, ThinVec::new())
+ })
+ })
+ },
+ ExprKind::Closure(
+ capture_clause, asyncness, movability, ref decl, ref body, fn_decl_span) =>
+ {
self.with_new_scopes(|this| {
- let mut is_generator = false;
- let body_id = this.lower_body(Some(decl), |this| {
- let e = this.lower_expr(body);
- is_generator = this.is_generator;
- e
- });
- let generator_option = if is_generator {
- if !decl.inputs.is_empty() {
- span_err!(
+ if let IsAsync::Async(async_closure_node_id) = asyncness {
+ // FIXME(cramertj) allow `async` non-`move` closures with
+ if capture_clause == CaptureBy::Ref &&
+ !decl.inputs.is_empty()
+ {
+ struct_span_err!(
this.sess,
fn_decl_span,
- E0628,
- "generators cannot have explicit arguments"
- );
- this.sess.abort_if_errors();
+ E0705,
+ "`async` non-`move` closures with arguments \
+ are not currently supported",
+ )
+ .help("consider using `let` statements to manually capture \
+ variables by reference before entering an \
+ `async move` closure")
+ .emit();
}
- Some(match movability {
- Movability::Movable => hir::GeneratorMovability::Movable,
- Movability::Static => hir::GeneratorMovability::Static,
- })
+
+ // Transform `async |x: u8| -> X { ... }` into
+ // `|x: u8| future_from_generator(|| -> X { ... })`
+ let outer_decl = FnDecl {
+ inputs: decl.inputs.clone(),
+ output: FunctionRetTy::Default(fn_decl_span),
+ variadic: false,
+ };
+ let body_id = this.lower_body(Some(&outer_decl), |this| {
+ let async_ret_ty = if let FunctionRetTy::Ty(ty) = &decl.output {
+ Some(&**ty)
+ } else { None };
+ let async_body = this.make_async_expr(
+ capture_clause, async_closure_node_id, async_ret_ty,
+ |this| {
+ this.with_new_scopes(|this| this.lower_expr(body))
+ });
+ this.expr(fn_decl_span, async_body, ThinVec::new())
+ });
+ hir::ExprClosure(
+ this.lower_capture_clause(capture_clause),
+ this.lower_fn_decl(&outer_decl, None, false, false),
+ body_id,
+ fn_decl_span,
+ None,
+ )
} else {
- if movability == Movability::Static {
- span_err!(
- this.sess,
- fn_decl_span,
- E0697,
- "closures cannot be static"
- );
- }
- None
- };
- hir::ExprClosure(
- this.lower_capture_clause(capture_clause),
- this.lower_fn_decl(decl, None, false),
- body_id,
- fn_decl_span,
- generator_option,
- )
+ let mut is_generator = false;
+ let body_id = this.lower_body(Some(decl), |this| {
+ let e = this.lower_expr(body);
+ is_generator = this.is_generator;
+ e
+ });
+ let generator_option = if is_generator {
+ if !decl.inputs.is_empty() {
+ span_err!(
+ this.sess,
+ fn_decl_span,
+ E0628,
+ "generators cannot have explicit arguments"
+ );
+ this.sess.abort_if_errors();
+ }
+ Some(match movability {
+ Movability::Movable => hir::GeneratorMovability::Movable,
+ Movability::Static => hir::GeneratorMovability::Static,
+ })
+ } else {
+ if movability == Movability::Static {
+ span_err!(
+ this.sess,
+ fn_decl_span,
+ E0906,
+ "closures cannot be static"
+ );
+ }
+ None
+ };
+ hir::ExprClosure(
+ this.lower_capture_clause(capture_clause),
+ this.lower_fn_decl(decl, None, false, false),
+ body_id,
+ fn_decl_span,
+ generator_option,
+ )
+ }
})
}
ExprKind::Block(ref blk, opt_label) => {
hir::Lifetime {
id: self.next_id().node_id,
span,
- name: fresh_name,
+ name: hir::LifetimeName::Param(fresh_name),
}
}