Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/lower.rs')
-rw-r--r--crates/hir-def/src/expr_store/lower.rs194
1 files changed, 103 insertions, 91 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index a69755baf6..7a89f9fa77 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -8,7 +8,6 @@ mod path;
use std::{cell::OnceCell, mem};
-use arrayvec::ArrayVec;
use base_db::FxIndexSet;
use cfg::CfgOptions;
use either::Either;
@@ -19,6 +18,7 @@ use hir_expand::{
span_map::SpanMap,
};
use intern::{Symbol, sym};
+use itertools::Itertools;
use rustc_abi::ExternAbi;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
@@ -43,6 +43,7 @@ use crate::{
Body, BodySourceMap, ExprPtr, ExprRoot, ExpressionStore, ExpressionStoreBuilder,
ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, LifetimePtr,
PatPtr, TypePtr,
+ body::Param,
expander::Expander,
lower::generics::ImplTraitLowerFn,
path::{AssociatedTypeBinding, GenericArg, GenericArgs, GenericArgsParentheses, Path},
@@ -82,7 +83,7 @@ pub(super) fn lower_body(
// even though they should be the same. Also, when the body comes from multiple expansions, their
// hygiene is different.
- let mut self_params = ArrayVec::new();
+ let mut self_param = None;
let mut source_map_self_param = None;
let mut params = vec![];
let mut collector = ExprCollector::new(db, module, current_file_id);
@@ -100,9 +101,43 @@ pub(super) fn lower_body(
// If #[rust_analyzer::skip] annotated, only construct enough information for the signature
// and skip the body.
if skip_body {
+ collector.with_expr_root(|collector| {
+ if let Some(param_list) = parameters {
+ if let Some(self_param_syn) =
+ param_list.self_param().filter(|self_param| collector.check_cfg(self_param))
+ {
+ let is_mutable = self_param_syn.mut_token().is_some()
+ && self_param_syn.amp_token().is_none();
+ let hygiene = self_param_syn
+ .name()
+ .map(|name| collector.hygiene_id_for(name.syntax().text_range()))
+ .unwrap_or(HygieneId::ROOT);
+ let binding_id: la_arena::Idx<Binding> = collector.alloc_binding(
+ Name::new_symbol_root(sym::self_),
+ BindingAnnotation::new(is_mutable, false),
+ hygiene,
+ );
+ self_param = Some(Param::new(binding_id));
+ source_map_self_param =
+ Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
+ }
+ let count = param_list.params().filter(|it| collector.check_cfg(it)).count();
+ params = (0..count).map(|_| Param::new(collector.missing_pat())).collect();
+ };
+
+ collector.missing_expr()
+ });
+ let (store, source_map) = collector.store.finish();
+ return (
+ Body { store, params: params.into_boxed_slice(), self_param },
+ BodySourceMap { self_param: source_map_self_param, store: source_map },
+ );
+ }
+
+ collector.with_expr_root(|collector| {
if let Some(param_list) = parameters {
if let Some(self_param_syn) =
- param_list.self_param().filter(|self_param| collector.check_cfg(self_param))
+ param_list.self_param().filter(|it| collector.check_cfg(it))
{
let is_mutable =
self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
@@ -115,59 +150,31 @@ pub(super) fn lower_body(
BindingAnnotation::new(is_mutable, false),
hygiene,
);
- self_params.push(binding_id);
+ self_param = Some(Param::new(binding_id));
source_map_self_param =
Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
}
- let count = param_list.params().filter(|it| collector.check_cfg(it)).count();
- params = (0..count).map(|_| collector.missing_pat()).collect();
- };
- collector.with_expr_root(|collector| collector.missing_expr());
- let (store, source_map) = collector.store.finish();
- return (
- Body { store, params: params.into_boxed_slice(), self_params },
- BodySourceMap { self_param: source_map_self_param, store: source_map },
- );
- }
- if let Some(param_list) = parameters {
- if let Some(self_param_syn) = param_list.self_param().filter(|it| collector.check_cfg(it)) {
- let is_mutable =
- self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
- let hygiene = self_param_syn
- .name()
- .map(|name| collector.hygiene_id_for(name.syntax().text_range()))
- .unwrap_or(HygieneId::ROOT);
- let binding_id: la_arena::Idx<Binding> = collector.alloc_binding(
- Name::new_symbol_root(sym::self_),
- BindingAnnotation::new(is_mutable, false),
- hygiene,
+ let is_extern = matches!(
+ owner,
+ DefWithBodyId::FunctionId(id)
+ if matches!(id.loc(db).container, ItemContainerId::ExternBlockId(_)),
);
- self_params.push(binding_id);
- source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
- }
- let is_extern = matches!(
- owner,
- DefWithBodyId::FunctionId(id)
- if matches!(id.loc(db).container, ItemContainerId::ExternBlockId(_)),
- );
-
- for param in param_list.params() {
- if collector.check_cfg(&param) {
- let param_pat = if is_extern {
- collector.collect_extern_fn_param(param.pat())
- } else {
- collector.collect_pat_top(param.pat())
- };
- params.push(param_pat);
+ for param in param_list.params() {
+ if collector.check_cfg(&param) {
+ let param_pat = if is_extern {
+ collector.collect_extern_fn_param(param.pat())
+ } else {
+ collector.collect_pat_top(param.pat())
+ };
+ params.push(Param::new(param_pat));
+ }
}
- }
- };
+ };
- collector.with_expr_root(|collector| {
collector.collect(
- &mut self_params,
+ &mut self_param,
&mut params,
body,
if is_async_fn {
@@ -187,7 +194,7 @@ pub(super) fn lower_body(
let (store, source_map) = collector.store.finish();
(
- Body { store, params: params.into_boxed_slice(), self_params },
+ Body { store, params: params.into_boxed_slice(), self_param },
BodySourceMap { self_param: source_map_self_param, store: source_map },
)
}
@@ -952,8 +959,8 @@ impl<'db> ExprCollector<'db> {
/// drop order are stable.
fn lower_coroutine_body_with_moved_arguments(
&mut self,
- self_params: &mut ArrayVec<BindingId, 2>,
- params: &mut [PatId],
+ self_param: &mut Option<Param<BindingId>>,
+ params: &mut [Param<PatId>],
body: ExprId,
kind: CoroutineKind,
coroutine_source: CoroutineSource,
@@ -984,10 +991,11 @@ impl<'db> ExprCollector<'db> {
// If `<pattern>` is a simple ident, then it is lowered to a single
// `let <pattern> = <pattern>;` statement as an optimization.
- let mut statements = Vec::new();
+ let mut statements =
+ Vec::with_capacity(usize::from(self_param.is_some()) + (params.len() * 2));
- if let Some(&self_param) = self_params.first() {
- let Binding { ref name, mode, hygiene, .. } = self.store.bindings[self_param];
+ if let Some(self_param) = self_param {
+ let Binding { ref name, mode, hygiene, .. } = self.store.bindings[self_param.formal];
let name = name.clone();
let child_binding_id = self.alloc_binding(name.clone(), mode, hygiene);
let child_pat_id =
@@ -1003,11 +1011,11 @@ impl<'db> ExprCollector<'db> {
initializer: Some(expr),
else_branch: None,
});
- self_params.push(child_binding_id);
+ self_param.user_written = child_binding_id;
}
for param in params {
- let (name, hygiene, is_simple_parameter) = match self.store.pats[*param] {
+ let (name, hygiene, is_simple_parameter) = match self.store.pats[param.formal] {
// Check if this is a binding pattern, if so, we can optimize and avoid adding a
// `let <pat> = __argN;` statement. In this case, we do not rename the parameter.
Pat::Bind { id, subpat: None, .. }
@@ -1016,55 +1024,57 @@ impl<'db> ExprCollector<'db> {
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
) =>
{
- (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, true)
+ let binding = &self.store.bindings[id];
+ (binding.name.clone(), binding.hygiene, true)
}
Pat::Bind { id, .. } => {
// If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display.
- (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, false)
+ let binding = &self.store.bindings[id];
+ (binding.name.clone(), binding.hygiene, false)
}
_ => (self.generate_new_name(), HygieneId::ROOT, false),
};
- let pat_syntax = self.store.pat_map_back.get(*param).copied();
- let child_binding_id =
- self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
- let child_pat_id =
- self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None });
- self.add_definition_to_binding(child_binding_id, child_pat_id);
- if let Some(pat_syntax) = pat_syntax {
- self.store.pat_map_back.insert(child_pat_id, pat_syntax);
- }
- let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into()));
- if !hygiene.is_root() {
- self.store.ident_hygiene.insert(expr.into(), hygiene);
- }
- statements.push(Statement::Let {
- pat: child_pat_id,
- type_ref: None,
- initializer: Some(expr),
- else_branch: None,
- });
+ let pat_syntax = self.store.pat_map_back.get(param.formal).copied();
if !is_simple_parameter {
+ // It needs to be mutable so the inner patterns can borrow it mutably (`ref mut`).
+ let child_binding_id =
+ self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
+ let child_pat_id =
+ self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None });
+ self.add_definition_to_binding(child_binding_id, child_pat_id);
+ if let Some(pat_syntax) = pat_syntax {
+ self.store.pat_map_back.insert(child_pat_id, pat_syntax);
+ }
let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into()));
if !hygiene.is_root() {
self.store.ident_hygiene.insert(expr.into(), hygiene);
}
statements.push(Statement::Let {
- pat: *param,
+ pat: child_pat_id,
type_ref: None,
initializer: Some(expr),
else_branch: None,
});
+ }
+ let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into()));
+ if !hygiene.is_root() {
+ self.store.ident_hygiene.insert(expr.into(), hygiene);
+ }
+ statements.push(Statement::Let {
+ pat: param.formal,
+ type_ref: None,
+ initializer: Some(expr),
+ else_branch: None,
+ });
- let parent_binding_id =
- self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
- let parent_pat_id =
- self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None });
- self.add_definition_to_binding(parent_binding_id, parent_pat_id);
- if let Some(pat_syntax) = pat_syntax {
- self.store.pat_map_back.insert(parent_pat_id, pat_syntax);
- }
- *param = parent_pat_id;
+ let parent_binding_id = self.alloc_binding(name, BindingAnnotation::Mutable, hygiene);
+ let parent_pat_id =
+ self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None });
+ self.add_definition_to_binding(parent_binding_id, parent_pat_id);
+ if let Some(pat_syntax) = pat_syntax {
+ self.store.pat_map_back.insert(parent_pat_id, pat_syntax);
}
+ *param = Param { formal: parent_pat_id, user_written: param.formal };
}
let coroutine = self.desugared_coroutine_expr(
@@ -1105,8 +1115,8 @@ impl<'db> ExprCollector<'db> {
fn collect(
&mut self,
- self_params: &mut ArrayVec<BindingId, 2>,
- params: &mut [PatId],
+ self_param: &mut Option<Param<BindingId>>,
+ params: &mut [Param<PatId>],
expr: Option<ast::Expr>,
awaitable: Awaitable,
is_async_fn: bool,
@@ -1123,7 +1133,7 @@ impl<'db> ExprCollector<'db> {
(false, false) => unreachable!(),
};
this.lower_coroutine_body_with_moved_arguments(
- self_params,
+ self_param,
params,
body,
kind,
@@ -1599,13 +1609,15 @@ impl<'db> ExprCollector<'db> {
let closure_kind = if let Some(kind) = kind {
// It's important that this expr is allocated immediately before the closure.
// We rely on it for `coroutine_for_closure()`.
+ let mut args_with_user_written = args.iter().copied().map(Param::new).collect::<Vec<_>>();
body = this.lower_coroutine_body_with_moved_arguments(
- &mut ArrayVec::new(),
- &mut args,
+ &mut None,
+ &mut args_with_user_written,
body,
kind,
CoroutineSource::Closure,
);
+ args.iter_mut().set_from(args_with_user_written.iter().map(|arg| arg.formal));
is_coroutine_closure = true;
ClosureKind::CoroutineClosure(kind)