1
Fork 0

Rollup merge of #98705 - WaffleLapkin:closure_binder, r=cjgillot

Implement `for<>` lifetime binder for closures

This PR implements RFC 3216 ([TI](https://github.com/rust-lang/rust/issues/97362)) and allows code like the following:

```rust
let _f = for<'a, 'b> |a: &'a A, b: &'b B| -> &'b C { b.c(a) };
//       ^^^^^^^^^^^--- new!
```

cc ``@Aaron1011`` ``@cjgillot``
This commit is contained in:
Dylan DPC 2022-07-14 14:14:21 +05:30 committed by GitHub
commit e5a86d7358
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
91 changed files with 843 additions and 208 deletions

View file

@ -259,7 +259,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> {
fn visit_expr(&mut self, expr: &'a Expr) {
let parent_def = match expr.kind {
ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id),
ExprKind::Closure(_, asyncness, ..) => {
ExprKind::Closure(_, _, asyncness, ..) => {
// Async closures desugar to closures inside of closures, so
// we must create two defs.
let closure_def = self.create_def(expr.id, DefPathData::ClosureExpr, expr.span);

View file

@ -268,6 +268,7 @@ enum LifetimeBinderKind {
WhereBound,
Item,
Function,
Closure,
ImplBlock,
}
@ -281,6 +282,7 @@ impl LifetimeBinderKind {
Item => "item",
ImplBlock => "impl block",
Function => "function",
Closure => "closure",
}
}
}
@ -866,19 +868,30 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
this.in_func_body = previous_state;
}
}
FnKind::Closure(declaration, body) => {
// We do not have any explicit generic lifetime parameter.
// FIXME(rfc3216): Change when implementing `for<>` bounds on closures.
FnKind::Closure(binder, declaration, body) => {
this.visit_closure_binder(binder);
this.with_lifetime_rib(
LifetimeRibKind::AnonymousCreateParameter {
binder: fn_id,
report_in_path: false,
match binder {
// We do not have any explicit generic lifetime parameter.
ClosureBinder::NotPresent => {
LifetimeRibKind::AnonymousCreateParameter {
binder: fn_id,
report_in_path: false,
}
}
ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError,
},
// Add each argument to the rib.
|this| this.resolve_params(&declaration.inputs),
);
this.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(fn_id, true),
match binder {
ClosureBinder::NotPresent => {
LifetimeRibKind::AnonymousPassThrough(fn_id, true)
}
ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError,
},
|this| visit::walk_fn_ret_ty(this, &declaration.output),
);
@ -913,6 +926,18 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
}
}
fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) {
match b {
ClosureBinder::NotPresent => {}
ClosureBinder::For { generic_params, .. } => {
self.visit_generic_params(
&generic_params,
self.diagnostic_metadata.current_self_item.is_some(),
);
}
}
}
fn visit_generic_arg(&mut self, arg: &'ast GenericArg) {
debug!("visit_generic_arg({:?})", arg);
let prev = replace(&mut self.diagnostic_metadata.currently_processing_generics, true);
@ -3517,7 +3542,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
// `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to
// resolve the arguments within the proper scopes so that usages of them inside the
// closure are detected as upvars rather than normal closure arg usages.
ExprKind::Closure(_, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => {
ExprKind::Closure(_, _, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => {
self.with_rib(ValueNS, NormalRibKind, |this| {
this.with_label_rib(ClosureOrAsyncRibKind, |this| {
// Resolve arguments:
@ -3537,6 +3562,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
});
}
// For closures, ClosureOrAsyncRibKind is added in visit_fn
ExprKind::Closure(ClosureBinder::For { ref generic_params, span }, ..) => {
self.with_generic_param_rib(
&generic_params,
NormalRibKind,
LifetimeRibKind::Generics {
binder: expr.id,
kind: LifetimeBinderKind::Closure,
span,
},
|this| visit::walk_expr(this, expr),
);
}
ExprKind::Closure(..) => visit::walk_expr(self, expr),
ExprKind::Async(..) => {
self.with_label_rib(ClosureOrAsyncRibKind, |this| visit::walk_expr(this, expr));

View file

@ -70,6 +70,8 @@ pub(crate) enum ForLifetimeSpanType {
BoundTail,
TypeEmpty,
TypeTail,
ClosureEmpty,
ClosureTail,
}
impl ForLifetimeSpanType {
@ -77,13 +79,15 @@ impl ForLifetimeSpanType {
match self {
Self::BoundEmpty | Self::BoundTail => "bound",
Self::TypeEmpty | Self::TypeTail => "type",
Self::ClosureEmpty | Self::ClosureTail => "closure",
}
}
pub(crate) fn suggestion(&self, sugg: &str) -> String {
match self {
Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg),
Self::BoundTail | Self::TypeTail => format!(", {}", sugg),
Self::ClosureEmpty => format!("for<{}>", sugg),
Self::BoundTail | Self::TypeTail | Self::ClosureTail => format!(", {}", sugg),
}
}
}

View file

@ -15,7 +15,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefIdMap, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node};
use rustc_hir::{GenericParamKind, HirIdMap};
use rustc_hir::{GenericParamKind, HirIdMap, LifetimeParamKind};
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_lifetime::*;
@ -571,7 +571,54 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
}
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
if let hir::ExprKind::Closure { bound_generic_params, .. } = e.kind {
if let hir::ExprKind::Closure(hir::Closure {
binder, bound_generic_params, fn_decl, ..
}) = e.kind
{
if let &hir::ClosureBinder::For { span: for_sp, .. } = binder {
fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> {
struct V(Option<Span>);
impl<'v> Visitor<'v> for V {
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
match t.kind {
_ if self.0.is_some() => (),
hir::TyKind::Infer => {
self.0 = Some(t.span);
}
_ => intravisit::walk_ty(self, t),
}
}
}
let mut v = V(None);
v.visit_ty(ty);
v.0
}
let infer_in_rt_sp = match fn_decl.output {
hir::FnRetTy::DefaultReturn(sp) => Some(sp),
hir::FnRetTy::Return(ty) => span_of_infer(ty),
};
let infer_spans = fn_decl
.inputs
.into_iter()
.filter_map(span_of_infer)
.chain(infer_in_rt_sp)
.collect::<Vec<_>>();
if !infer_spans.is_empty() {
self.tcx.sess
.struct_span_err(
infer_spans,
"implicit types in closure signatures are forbidden when `for<...>` is present",
)
.span_label(for_sp, "`for<...>` is here")
.emit();
}
}
let next_early_index = self.next_early_index();
let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) =
bound_generic_params
@ -584,6 +631,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
(pair, r)
})
.unzip();
self.map.late_bound_vars.insert(e.hir_id, binders);
let scope = Scope::Binder {
hir_id: e.hir_id,
@ -595,11 +643,41 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
allow_late_bound: true,
where_bound_origin: None,
};
if let &hir::ClosureBinder::For { span, .. } = binder {
let last_lt = bound_generic_params
.iter()
.filter(|p| {
matches!(
p,
GenericParam {
kind: GenericParamKind::Lifetime {
kind: LifetimeParamKind::Explicit
},
..
}
)
})
.last();
let (span, span_type) = match last_lt {
Some(GenericParam { span: last_sp, .. }) => {
(last_sp.shrink_to_hi(), ForLifetimeSpanType::ClosureTail)
}
None => (span, ForLifetimeSpanType::ClosureEmpty),
};
self.missing_named_lifetime_spots
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
}
self.with(scope, |this| {
// a closure has no bounds, so everything
// contained within is scoped within its binder.
intravisit::walk_expr(this, e)
});
if let hir::ClosureBinder::For { .. } = binder {
self.missing_named_lifetime_spots.pop();
}
} else {
intravisit::walk_expr(self, e)
}