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:
commit
e5a86d7358
91 changed files with 843 additions and 208 deletions
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue