Refactor visit_fn.

This commit is contained in:
Camille GILLOT 2022-06-05 18:24:22 +02:00
parent 80c6a1f275
commit 237e267b80
2 changed files with 97 additions and 66 deletions

View File

@ -746,86 +746,117 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
self.diagnostic_metadata.current_function = Some((fn_kind, sp)); self.diagnostic_metadata.current_function = Some((fn_kind, sp));
} }
debug!("(resolving function) entering function"); debug!("(resolving function) entering function");
let declaration = fn_kind.decl();
// Create a value rib for the function. // Create a value rib for the function.
self.with_rib(ValueNS, rib_kind, |this| { self.with_rib(ValueNS, rib_kind, |this| {
// Create a label rib for the function. // Create a label rib for the function.
this.with_label_rib(FnItemRibKind, |this| { this.with_label_rib(FnItemRibKind, |this| {
let async_node_id = fn_kind.header().and_then(|h| h.asyncness.opt_return_id()); match fn_kind {
FnKind::Fn(_, _, sig, _, generics, body) => {
this.visit_generics(generics);
if let FnKind::Fn(_, _, _, _, generics, _) = fn_kind { let declaration = &sig.decl;
this.visit_generics(generics); let async_node_id = sig.header.asyncness.opt_return_id();
}
if let Some(async_node_id) = async_node_id { // Argument-position elided lifetimes must be transformed into fresh
// In `async fn`, argument-position elided lifetimes // generic parameters. This is especially useful for `async fn`, where
// must be transformed into fresh generic parameters so that // these fresh generic parameters can be applied to the opaque `impl Trait`
// they can be applied to the opaque `impl Trait` return type. // return type.
this.with_lifetime_rib( this.with_lifetime_rib(
LifetimeRibKind::AnonymousCreateParameter(fn_id), if async_node_id.is_some() {
|this| { LifetimeRibKind::AnonymousCreateParameter(fn_id)
} else {
LifetimeRibKind::AnonymousPassThrough(fn_id, false)
},
// Add each argument to the rib. // Add each argument to the rib.
this.resolve_params(&declaration.inputs) |this| this.resolve_params(&declaration.inputs),
},
);
// Construct the list of in-scope lifetime parameters for async lowering.
// We include all lifetime parameters, either named or "Fresh".
// The order of those parameters does not matter, as long as it is
// deterministic.
let mut extra_lifetime_params =
this.r.extra_lifetime_params_map.get(&fn_id).cloned().unwrap_or_default();
for rib in this.lifetime_ribs.iter().rev() {
extra_lifetime_params.extend(
rib.bindings
.iter()
.map(|(&ident, &(node_id, res))| (ident, node_id, res)),
); );
match rib.kind {
LifetimeRibKind::Item => break, // Construct the list of in-scope lifetime parameters for async lowering.
LifetimeRibKind::AnonymousCreateParameter(id) => { // We include all lifetime parameters, either named or "Fresh".
if let Some(earlier_fresh) = // The order of those parameters does not matter, as long as it is
this.r.extra_lifetime_params_map.get(&id) // deterministic.
{ if let Some(async_node_id) = async_node_id {
extra_lifetime_params.extend(earlier_fresh); let mut extra_lifetime_params = this
.r
.extra_lifetime_params_map
.get(&fn_id)
.cloned()
.unwrap_or_default();
for rib in this.lifetime_ribs.iter().rev() {
extra_lifetime_params.extend(
rib.bindings
.iter()
.map(|(&ident, &(node_id, res))| (ident, node_id, res)),
);
match rib.kind {
LifetimeRibKind::Item => break,
LifetimeRibKind::AnonymousCreateParameter(binder) => {
if let Some(earlier_fresh) =
this.r.extra_lifetime_params_map.get(&binder)
{
extra_lifetime_params.extend(earlier_fresh);
}
}
_ => {}
} }
} }
_ => {} this.r
.extra_lifetime_params_map
.insert(async_node_id, extra_lifetime_params);
}
this.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(
// For async fn, the return type appears inside a custom
// `impl Future` RPIT, so we override the binder's id.
async_node_id.unwrap_or(fn_id),
true,
),
|this| visit::walk_fn_ret_ty(this, &declaration.output),
);
if let Some(body) = body {
// Ignore errors in function bodies if this is rustdoc
// Be sure not to set this until the function signature has been resolved.
let previous_state = replace(&mut this.in_func_body, true);
// Resolve the function body, potentially inside the body of an async closure
this.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(fn_id, false),
|this| this.visit_block(body),
);
debug!("(resolving function) leaving function");
this.in_func_body = previous_state;
} }
} }
this.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params); FnKind::Closure(declaration, body) => {
// Do not attempt to create generic lifetime parameters.
// FIXME: Revisit this decision once `for<>` bounds on closures become a
// thing.
this.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(fn_id, false),
// Add each argument to the rib.
|this| this.resolve_params(&declaration.inputs),
);
this.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(fn_id, true),
|this| visit::walk_fn_ret_ty(this, &declaration.output),
);
this.with_lifetime_rib( // Ignore errors in function bodies if this is rustdoc
LifetimeRibKind::AnonymousPassThrough(async_node_id, true), // Be sure not to set this until the function signature has been resolved.
|this| visit::walk_fn_ret_ty(this, &declaration.output), let previous_state = replace(&mut this.in_func_body, true);
); // Resolve the function body, potentially inside the body of an async closure
} else { this.with_lifetime_rib(
// Add each argument to the rib. LifetimeRibKind::AnonymousPassThrough(fn_id, false),
this.with_lifetime_rib( |this| this.visit_expr(body),
LifetimeRibKind::AnonymousPassThrough(fn_id, false), );
|this| this.resolve_params(&declaration.inputs),
);
this.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(fn_id, true),
|this| visit::walk_fn_ret_ty(this, &declaration.output),
);
};
// Ignore errors in function bodies if this is rustdoc debug!("(resolving function) leaving function");
// Be sure not to set this until the function signature has been resolved. this.in_func_body = previous_state;
let previous_state = replace(&mut this.in_func_body, true); }
// Resolve the function body, potentially inside the body of an async closure }
this.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(fn_id, false),
|this| match fn_kind {
FnKind::Fn(.., body) => walk_list!(this, visit_block, body),
FnKind::Closure(_, body) => this.visit_expr(body),
},
);
debug!("(resolving function) leaving function");
this.in_func_body = previous_state;
}) })
}); });
self.diagnostic_metadata.current_function = previous_value; self.diagnostic_metadata.current_function = previous_value;

View File

@ -11,7 +11,7 @@ LL | | }
| |
= note: expected fn pointer `fn(&mut RepeatMut<'a, T>) -> Option<_>` = note: expected fn pointer `fn(&mut RepeatMut<'a, T>) -> Option<_>`
found fn pointer `fn(&'a mut RepeatMut<'a, T>) -> Option<_>` found fn pointer `fn(&'a mut RepeatMut<'a, T>) -> Option<_>`
note: the anonymous lifetime #1 defined here... note: the anonymous lifetime as defined here...
--> $DIR/issue-37884.rs:6:5 --> $DIR/issue-37884.rs:6:5
| |
LL | fn next(&'a mut self) -> Option<Self::Item> LL | fn next(&'a mut self) -> Option<Self::Item>