Skip to content

Commit

Permalink
Auto merge of #98279 - cjgillot:all-fresh-nofn, r=petrochenkov
Browse files Browse the repository at this point in the history
Create elided lifetime parameters for function-like types

Split from #97720

This PR refactor lifetime generic parameters in bare function types and parenthesized traits to introduce the additional required lifetimes as fresh parameters in a `for<>` bound.

This PR does the same to lifetimes appearing in closure signatures, and as-if introducing `for<>` bounds on closures (without the associated change in semantics).

r? `@petrochenkov`
  • Loading branch information
bors committed Jun 22, 2022
2 parents 89a0783 + 576661c commit 10f4ce3
Show file tree
Hide file tree
Showing 28 changed files with 808 additions and 599 deletions.
57 changes: 34 additions & 23 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
if let Async::Yes { closure_id, .. } = asyncness {
self.lower_expr_async_closure(
capture_clause,
e.id,
closure_id,
decl,
body,
Expand All @@ -173,6 +174,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
} else {
self.lower_expr_closure(
capture_clause,
e.id,
movability,
decl,
body,
Expand Down Expand Up @@ -604,6 +606,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `static |_task_context| -> <ret_ty> { body }`:
let generator_kind = hir::ExprKind::Closure {
capture_clause,
bound_generic_params: &[],
fn_decl,
body,
fn_decl_span: self.lower_span(span),
Expand Down Expand Up @@ -828,6 +831,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_expr_closure(
&mut self,
capture_clause: CaptureBy,
closure_id: NodeId,
movability: Movability,
decl: &FnDecl,
body: &Expr,
Expand All @@ -848,16 +852,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
(body_id, generator_option)
});

// Lower outside new scope to preserve `is_in_loop_condition`.
let fn_decl = self.lower_fn_decl(decl, None, FnDeclKind::Closure, None);

hir::ExprKind::Closure {
capture_clause,
fn_decl,
body,
fn_decl_span: self.lower_span(fn_decl_span),
movability: generator_option,
}
self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| {
// Lower outside new scope to preserve `is_in_loop_condition`.
let fn_decl = this.lower_fn_decl(decl, None, FnDeclKind::Closure, None);

hir::ExprKind::Closure {
capture_clause,
bound_generic_params,
fn_decl,
body,
fn_decl_span: this.lower_span(fn_decl_span),
movability: generator_option,
}
})
}

fn generator_movability_for_fn(
Expand Down Expand Up @@ -897,6 +904,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
capture_clause: CaptureBy,
closure_id: NodeId,
inner_closure_id: NodeId,
decl: &FnDecl,
body: &Expr,
fn_decl_span: Span,
Expand Down Expand Up @@ -927,7 +935,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
if let FnRetTy::Ty(ty) = &decl.output { Some(ty.clone()) } else { None };
let async_body = this.make_async_expr(
capture_clause,
closure_id,
inner_closure_id,
async_ret_ty,
body.span,
hir::AsyncGeneratorKind::Closure,
Expand All @@ -938,18 +946,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
body_id
});

// We need to lower the declaration outside the new scope, because we
// have to conserve the state of being inside a loop condition for the
// closure argument types.
let fn_decl = self.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None);

hir::ExprKind::Closure {
capture_clause,
fn_decl,
body,
fn_decl_span: self.lower_span(fn_decl_span),
movability: None,
}
self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| {
// We need to lower the declaration outside the new scope, because we
// have to conserve the state of being inside a loop condition for the
// closure argument types.
let fn_decl = this.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None);

hir::ExprKind::Closure {
capture_clause,
bound_generic_params,
fn_decl,
body,
fn_decl_span: this.lower_span(fn_decl_span),
movability: None,
}
})
}

/// Destructure the LHS of complex assignments.
Expand Down
11 changes: 7 additions & 4 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1386,16 +1386,19 @@ impl<'hir> LoweringContext<'_, 'hir> {

let mut params: SmallVec<[hir::GenericParam<'hir>; 4]> =
self.lower_generic_params_mut(&generics.params).collect();

// Introduce extra lifetimes if late resolution tells us to.
let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id);
params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| {
self.lifetime_res_to_generic_param(ident, node_id, res)
}));

let has_where_clause_predicates = !generics.where_clause.predicates.is_empty();
let where_clause_span = self.lower_span(generics.where_clause.span);
let span = self.lower_span(generics.span);
let res = f(self);

let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id);
let impl_trait_defs = std::mem::take(&mut self.impl_trait_defs);
params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| {
self.lifetime_res_to_generic_param(ident, node_id, res)
}));
params.extend(impl_trait_defs.into_iter());

let impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
Expand Down
75 changes: 55 additions & 20 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ impl ResolverAstLoweringExt for ResolverAstLowering {
}

/// Obtain the list of lifetimes parameters to add to an item.
///
/// Extra lifetime parameters should only be added in places that can appear
/// as a `binder` in `LifetimeRes`.
///
/// The extra lifetimes that appear from the parenthesized `Fn`-trait desugaring
/// should appear at the enclosing `PolyTraitRef`.
fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> {
self.extra_lifetime_params_map.remove(&id).unwrap_or_default()
}
Expand Down Expand Up @@ -721,6 +727,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}

/// Converts a lifetime into a new generic parameter.
#[tracing::instrument(level = "debug", skip(self))]
fn lifetime_res_to_generic_param(
&mut self,
ident: Ident,
Expand All @@ -731,7 +738,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
LifetimeRes::Param { .. } => {
(hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
}
LifetimeRes::Fresh { .. } => (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided),
LifetimeRes::Fresh { param, .. } => {
// Late resolution delegates to us the creation of the `LocalDefId`.
let _def_id = self.create_def(
self.current_hir_id_owner,
param,
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
);
debug!(?_def_id);

(hir::ParamName::Fresh, hir::LifetimeParamKind::Elided)
}
LifetimeRes::Static | LifetimeRes::Error => return None,
res => panic!(
"Unexpected lifetime resolution {:?} for {:?} at {:?}",
Expand Down Expand Up @@ -777,11 +794,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// Register a binder to be ignored for lifetime capture.
#[tracing::instrument(level = "debug", skip(self, f))]
#[inline]
fn with_lifetime_binder<T>(&mut self, binder: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
fn with_lifetime_binder<T>(
&mut self,
binder: NodeId,
generic_params: &[GenericParam],
f: impl FnOnce(&mut Self, &'hir [hir::GenericParam<'hir>]) -> T,
) -> T {
let mut generic_params: Vec<_> = self.lower_generic_params_mut(generic_params).collect();
let extra_lifetimes = self.resolver.take_extra_lifetime_params(binder);
debug!(?extra_lifetimes);
generic_params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| {
self.lifetime_res_to_generic_param(ident, node_id, res)
}));
let generic_params = self.arena.alloc_from_iter(generic_params);
debug!(?generic_params);

if let Some(ctxt) = &mut self.captured_lifetimes {
ctxt.binders_to_ignore.insert(binder);
}
let ret = f(self);
let ret = f(self, generic_params);
if let Some(ctxt) = &mut self.captured_lifetimes {
ctxt.binders_to_ignore.remove(&binder);
}
Expand Down Expand Up @@ -1178,15 +1209,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let lifetime = self.lower_lifetime(&region);
hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx))
}
TyKind::BareFn(ref f) => self.with_lifetime_binder(t.id, |this| {
hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
generic_params: this.lower_generic_params(&f.generic_params),
unsafety: this.lower_unsafety(f.unsafety),
abi: this.lower_extern(f.ext),
decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
param_names: this.lower_fn_params_to_names(&f.decl),
}))
}),
TyKind::BareFn(ref f) => {
self.with_lifetime_binder(t.id, &f.generic_params, |this, generic_params| {
hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
generic_params,
unsafety: this.lower_unsafety(f.unsafety),
abi: this.lower_extern(f.ext),
decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
param_names: this.lower_fn_params_to_names(&f.decl),
}))
})
}
TyKind::Never => hir::TyKind::Never,
TyKind::Tup(ref tys) => hir::TyKind::Tup(
self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty_direct(ty, itctx))),
Expand Down Expand Up @@ -1814,8 +1847,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
hir::LifetimeName::Param(param, p_name)
}
LifetimeRes::Fresh { mut param, binder } => {
LifetimeRes::Fresh { param, binder } => {
debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
let mut param = self.local_def_id(param);
if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() {
if !captured_lifetimes.binders_to_ignore.contains(&binder) {
match captured_lifetimes.captures.entry(param) {
Expand Down Expand Up @@ -1952,13 +1986,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
p: &PolyTraitRef,
itctx: ImplTraitContext,
) -> hir::PolyTraitRef<'hir> {
let bound_generic_params = self.lower_generic_params(&p.bound_generic_params);

let trait_ref = self.with_lifetime_binder(p.trait_ref.ref_id, |this| {
this.lower_trait_ref(&p.trait_ref, itctx)
});

hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
self.with_lifetime_binder(
p.trait_ref.ref_id,
&p.bound_generic_params,
|this, bound_generic_params| {
let trait_ref = this.lower_trait_ref(&p.trait_ref, itctx);
hir::PolyTraitRef { bound_generic_params, trait_ref, span: this.lower_span(p.span) }
},
)
}

fn lower_mt(&mut self, mt: &MutTy, itctx: ImplTraitContext) -> hir::MutTy<'hir> {
Expand Down
51 changes: 22 additions & 29 deletions compiler/rustc_ast_lowering/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
}
GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args {
ParenthesizedGenericArgs::Ok => {
self.lower_parenthesized_parameter_data(segment.id, data)
}
ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
ParenthesizedGenericArgs::Err => {
let mut err = struct_span_err!(self.sess, data.span, E0214, "{}", msg);
err.span_label(data.span, "only `Fn` traits may use parentheses");
Expand Down Expand Up @@ -351,39 +349,34 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

fn lower_parenthesized_parameter_data(
&mut self,
id: NodeId,
data: &ParenthesizedArgs,
) -> (GenericArgsCtor<'hir>, 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
// compatibility, even in contexts like an impl header where
// we generally don't permit such things (see #51008).
self.with_lifetime_binder(id, |this| {
let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
let inputs = this.arena.alloc_from_iter(inputs.iter().map(|ty| {
this.lower_ty_direct(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam),
)
}));
let output_ty = match output {
FnRetTy::Ty(ty) => this
.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn)),
FnRetTy::Default(_) => this.arena.alloc(this.ty_tup(*span, &[])),
};
let args = smallvec![GenericArg::Type(this.ty_tup(*inputs_span, inputs))];
let binding = this.output_ty_binding(output_ty.span, output_ty);
(
GenericArgsCtor {
args,
bindings: arena_vec![this; binding],
parenthesized: true,
span: data.inputs_span,
},
false,
)
})
let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
let inputs = self.arena.alloc_from_iter(inputs.iter().map(|ty| {
self.lower_ty_direct(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam))
}));
let output_ty = match output {
FnRetTy::Ty(ty) => {
self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn))
}
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
};
let args = smallvec![GenericArg::Type(self.ty_tup(*inputs_span, inputs))];
let binding = self.output_ty_binding(output_ty.span, output_ty);
(
GenericArgsCtor {
args,
bindings: arena_vec![self; binding],
parenthesized: true,
span: data.inputs_span,
},
false,
)
}

/// An associated type binding `Output = $ty`.
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_borrowck/src/diagnostics/region_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
/// *user* has a name for. In that case, we'll be able to map
/// `fr` to a `Region<'tcx>`, and that region will be one of
/// named variants.
#[tracing::instrument(level = "trace", skip(self))]
fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> {
let error_region = self.to_error_region(fr)?;

Expand Down Expand Up @@ -290,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
if free_region.bound_region.is_named() {
// A named region that is actually named.
Some(RegionName { name, source: RegionNameSource::NamedFreeRegion(span) })
} else {
} else if let hir::IsAsync::Async = tcx.asyncness(self.mir_hir_id().owner) {
// If we spuriously thought that the region is named, we should let the
// system generate a true name for error messages. Currently this can
// happen if we have an elided name in an async fn for example: the
Expand All @@ -301,6 +302,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
name,
source: RegionNameSource::AnonRegionFromAsyncFn(span),
})
} else {
None
}
}

Expand Down Expand Up @@ -362,6 +365,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
/// | fn foo(x: &u32) { .. }
/// ------- fully elaborated type of `x` is `&'1 u32`
/// ```
#[tracing::instrument(level = "trace", skip(self))]
fn give_name_if_anonymous_region_appears_in_arguments(
&self,
fr: RegionVid,
Expand Down Expand Up @@ -651,6 +655,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
/// | let x = Some(&22);
/// - fully elaborated type of `x` is `Option<&'1 u32>`
/// ```
#[tracing::instrument(level = "trace", skip(self))]
fn give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Option<RegionName> {
let upvar_index = self.regioncx.get_upvar_index_for_region(self.infcx.tcx, fr)?;
let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region(
Expand All @@ -670,6 +675,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
/// must be a closure since, in a free fn, such an argument would
/// have to either also appear in an argument (if using elision)
/// or be early bound (named, not in argument).
#[tracing::instrument(level = "trace", skip(self))]
fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> {
let tcx = self.infcx.tcx;
let hir = tcx.hir();
Expand Down Expand Up @@ -801,6 +807,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
}
}

#[tracing::instrument(level = "trace", skip(self))]
fn give_name_if_anonymous_region_appears_in_yield_ty(
&self,
fr: RegionVid,
Expand Down
Loading

0 comments on commit 10f4ce3

Please sign in to comment.