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.rs564
1 files changed, 393 insertions, 171 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 04437a59ac..8818096500 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -8,17 +8,20 @@ mod path;
use std::{cell::OnceCell, mem};
+use arrayvec::ArrayVec;
use base_db::FxIndexSet;
use cfg::CfgOptions;
use either::Either;
use hir_expand::{
HirFileId, InFile, MacroDefId,
+ mod_path::ModPath,
name::{AsName, Name},
- span_map::SpanMapRef,
+ span_map::SpanMap,
};
use intern::{Symbol, sym};
+use rustc_abi::ExternAbi;
use rustc_hash::FxHashMap;
-use smallvec::smallvec;
+use smallvec::SmallVec;
use stdx::never;
use syntax::{
AstNode, AstPtr, SyntaxNodePtr,
@@ -37,17 +40,17 @@ use crate::{
attrs::AttrFlags,
db::DefDatabase,
expr_store::{
- Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder,
+ Body, BodySourceMap, ExprPtr, ExprRoot, ExpressionStore, ExpressionStoreBuilder,
ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, LifetimePtr,
- PatPtr, RootExprOrigin, TypePtr,
+ PatPtr, TypePtr,
expander::Expander,
lower::generics::ImplTraitLowerFn,
path::{AssociatedTypeBinding, GenericArg, GenericArgs, GenericArgsParentheses, Path},
},
hir::{
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
- CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability,
- OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement,
+ CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm,
+ Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement,
generics::GenericParams,
},
item_scope::BuiltinShadowMode,
@@ -71,6 +74,7 @@ pub(super) fn lower_body(
parameters: Option<ast::ParamList>,
body: Option<ast::Expr>,
is_async_fn: bool,
+ is_gen_fn: bool,
) -> (Body, BodySourceMap) {
// We cannot leave the root span map empty and let any identifier from it be treated as root,
// because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved
@@ -78,10 +82,10 @@ 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_param = None;
+ let mut self_params = ArrayVec::new();
let mut source_map_self_param = None;
let mut params = vec![];
- let mut collector = ExprCollector::body(db, module, current_file_id);
+ let mut collector = ExprCollector::new(db, module, current_file_id);
let skip_body = AttrFlags::query(
db,
@@ -111,18 +115,17 @@ pub(super) fn lower_body(
BindingAnnotation::new(is_mutable, false),
hygiene,
);
- self_param = Some(binding_id);
+ self_params.push(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();
};
- let body_expr = collector.missing_expr();
- collector.store.inference_roots = Some(smallvec![(body_expr, RootExprOrigin::BodyRoot)]);
+ collector.with_expr_root(|collector| collector.missing_expr());
let (store, source_map) = collector.store.finish();
return (
- Body { store, params: params.into_boxed_slice(), self_param },
+ Body { store, params: params.into_boxed_slice(), self_params },
BodySourceMap { self_param: source_map_self_param, store: source_map },
);
}
@@ -140,7 +143,7 @@ pub(super) fn lower_body(
BindingAnnotation::new(is_mutable, false),
hygiene,
);
- self_param = Some(binding_id);
+ self_params.push(binding_id);
source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
}
@@ -162,25 +165,29 @@ pub(super) fn lower_body(
}
};
- let body_expr = collector.collect(
- &mut params,
- body,
- if is_async_fn {
- Awaitable::Yes
- } else {
- match owner {
- DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
- DefWithBodyId::StaticId(..) => Awaitable::No("static"),
- DefWithBodyId::ConstId(..) => Awaitable::No("constant"),
- DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
- }
- },
- );
- collector.store.inference_roots = Some(smallvec![(body_expr, RootExprOrigin::BodyRoot)]);
+ collector.with_expr_root(|collector| {
+ collector.collect(
+ &mut self_params,
+ &mut params,
+ body,
+ if is_async_fn {
+ Awaitable::Yes
+ } else {
+ match owner {
+ DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
+ DefWithBodyId::StaticId(..) => Awaitable::No("static"),
+ DefWithBodyId::ConstId(..) => Awaitable::No("constant"),
+ DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
+ }
+ },
+ is_async_fn,
+ is_gen_fn,
+ )
+ });
let (store, source_map) = collector.store.finish();
(
- Body { store, params: params.into_boxed_slice(), self_param },
+ Body { store, params: params.into_boxed_slice(), self_params },
BodySourceMap { self_param: source_map_self_param, store: source_map },
)
}
@@ -190,7 +197,7 @@ pub(crate) fn lower_type_ref(
module: ModuleId,
type_ref: InFile<Option<ast::Type>>,
) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId) {
- let mut expr_collector = ExprCollector::signature(db, module, type_ref.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, type_ref.file_id);
let type_ref =
expr_collector.lower_type_ref_opt(type_ref.value, &mut ExprCollector::impl_trait_allocator);
let (store, source_map) = expr_collector.store.finish();
@@ -205,7 +212,7 @@ pub(crate) fn lower_generic_params(
param_list: Option<ast::GenericParamList>,
where_clause: Option<ast::WhereClause>,
) -> (ExpressionStore, GenericParams, ExpressionStoreSourceMap) {
- let mut expr_collector = ExprCollector::signature(db, module, file_id);
+ let mut expr_collector = ExprCollector::new(db, module, file_id);
let mut collector = generics::GenericParamsCollector::new(def);
collector.lower(&mut expr_collector, param_list, where_clause);
let params = collector.finish();
@@ -219,7 +226,7 @@ pub(crate) fn lower_impl(
impl_syntax: InFile<ast::Impl>,
impl_id: ImplId,
) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId, Option<TraitRef>, GenericParams) {
- let mut expr_collector = ExprCollector::signature(db, module, impl_syntax.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, impl_syntax.file_id);
let self_ty =
expr_collector.lower_type_ref_opt_disallow_impl_trait(impl_syntax.value.self_ty());
let trait_ = impl_syntax.value.trait_().and_then(|it| match &it {
@@ -247,7 +254,7 @@ pub(crate) fn lower_trait(
trait_syntax: InFile<ast::Trait>,
trait_id: TraitId,
) -> (ExpressionStore, ExpressionStoreSourceMap, GenericParams) {
- let mut expr_collector = ExprCollector::signature(db, module, trait_syntax.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, trait_syntax.file_id);
let mut collector = generics::GenericParamsCollector::with_self_param(
&mut expr_collector,
trait_id.into(),
@@ -270,7 +277,7 @@ pub(crate) fn lower_type_alias(
type_alias_id: TypeAliasId,
) -> (ExpressionStore, ExpressionStoreSourceMap, GenericParams, Box<[TypeBound]>, Option<TypeRefId>)
{
- let mut expr_collector = ExprCollector::signature(db, module, alias.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, alias.file_id);
let bounds = alias
.value
.type_bound_list()
@@ -312,7 +319,7 @@ pub(crate) fn lower_function(
bool,
bool,
) {
- let mut expr_collector = ExprCollector::signature(db, module, fn_.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, fn_.file_id);
let mut collector = generics::GenericParamsCollector::new(function_id.into());
collector.lower(&mut expr_collector, fn_.value.generic_param_list(), fn_.value.where_clause());
let mut params = vec![];
@@ -375,12 +382,20 @@ pub(crate) fn lower_function(
expr_collector.lower_type_ref_opt(ret_type.ty(), &mut ExprCollector::impl_trait_allocator)
});
- let return_type = if fn_.value.async_token().is_some() {
- let path = hir_expand::mod_path::path![core::future::Future];
+ let return_type = if fn_.value.async_token().is_some() || fn_.value.gen_token().is_some() {
+ let (path, assoc_name) =
+ match (fn_.value.async_token().is_some(), fn_.value.gen_token().is_some()) {
+ (true, true) => {
+ (hir_expand::mod_path::path![core::async_iter::AsyncIterator], sym::Item)
+ }
+ (true, false) => (hir_expand::mod_path::path![core::future::Future], sym::Output),
+ (false, true) => (hir_expand::mod_path::path![core::iter::Iterator], sym::Item),
+ (false, false) => unreachable!(),
+ };
let mut generic_args: Vec<_> =
std::iter::repeat_n(None, path.segments().len() - 1).collect();
let binding = AssociatedTypeBinding {
- name: Name::new_symbol_root(sym::Output),
+ name: Name::new_symbol_root(assoc_name),
args: None,
type_ref: Some(
return_type
@@ -531,20 +546,7 @@ impl BindingList {
}
impl<'db> ExprCollector<'db> {
- /// Creates a collector for a signature store, this will populate `const_expr_origins` to any
- /// top level const arg roots.
- pub fn signature(
- db: &dyn DefDatabase,
- module: ModuleId,
- current_file_id: HirFileId,
- ) -> ExprCollector<'_> {
- let mut this = Self::body(db, module, current_file_id);
- this.store.inference_roots = Some(Default::default());
- this
- }
-
- /// Creates a collector for a bidy store.
- pub fn body(
+ pub fn new(
db: &dyn DefDatabase,
module: ModuleId,
current_file_id: HirFileId,
@@ -552,7 +554,7 @@ impl<'db> ExprCollector<'db> {
let (def_map, local_def_map) = module.local_def_map(db);
let expander = Expander::new(db, current_file_id, def_map);
let krate = module.krate(db);
- ExprCollector {
+ let mut result = ExprCollector {
db,
cfg_options: krate.cfg_options(db),
module,
@@ -570,7 +572,9 @@ impl<'db> ExprCollector<'db> {
outer_impl_trait: false,
krate,
name_generator_index: 0,
- }
+ };
+ result.store.inference_roots = Some(SmallVec::new());
+ result
}
fn generate_new_name(&mut self) -> Name {
@@ -585,7 +589,7 @@ impl<'db> ExprCollector<'db> {
}
#[inline]
- pub(crate) fn span_map(&self) -> SpanMapRef<'_> {
+ pub(crate) fn span_map(&self) -> SpanMap<'_> {
self.expander.span_map()
}
@@ -639,9 +643,6 @@ impl<'db> ExprCollector<'db> {
}
ast::Type::ArrayType(inner) => {
let len = self.lower_const_arg_opt(inner.const_arg());
- if let Some(const_expr_origins) = &mut self.store.inference_roots {
- const_expr_origins.push((len.expr, RootExprOrigin::ArrayLength));
- }
TypeRef::Array(ArrayType {
ty: self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn),
len,
@@ -684,15 +685,13 @@ impl<'db> ExprCollector<'db> {
} else {
Vec::with_capacity(1)
};
- fn lower_abi(abi: ast::Abi) -> Symbol {
- match abi.abi_string() {
- Some(tok) => Symbol::intern(tok.text_without_quotes()),
- // `extern` default to be `extern "C"`.
- _ => sym::C,
- }
+ fn lower_abi(abi: ast::Abi) -> ExternAbi {
+ abi.abi_string()
+ .and_then(|abi| abi.text_without_quotes().parse().ok())
+ .unwrap_or(ExternAbi::FALLBACK)
}
- let abi = inner.abi().map(lower_abi);
+ let abi = inner.abi().map(lower_abi).unwrap_or(ExternAbi::Rust);
params.push((None, ret_ty));
TypeRef::Fn(Box::new(FnType {
is_varargs,
@@ -926,9 +925,6 @@ impl<'db> ExprCollector<'db> {
}
ast::GenericArg::ConstArg(arg) => {
let arg = self.lower_const_arg(arg);
- if let Some(const_expr_origins) = &mut self.store.inference_roots {
- const_expr_origins.push((arg.expr, RootExprOrigin::GenericArgsPath));
- }
args.push(GenericArg::Const(arg))
}
}
@@ -949,46 +945,125 @@ impl<'db> ExprCollector<'db> {
/// into the body. This is to make sure that the future actually owns the
/// arguments that are passed to the function, and to ensure things like
/// drop order are stable.
- fn lower_async_block_with_moved_arguments(
+ fn lower_coroutine_body_with_moved_arguments(
&mut self,
+ self_params: &mut ArrayVec<BindingId, 2>,
params: &mut [PatId],
body: ExprId,
+ kind: CoroutineKind,
coroutine_source: CoroutineSource,
) -> ExprId {
+ // Async function parameters are lowered into the closure body so that they are
+ // captured and so that the drop order matches the equivalent non-async functions.
+ //
+ // from:
+ //
+ // async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) {
+ // <body>
+ // }
+ //
+ // into:
+ //
+ // fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) {
+ // async move {
+ // let __arg2 = __arg2;
+ // let <pattern> = __arg2;
+ // let __arg1 = __arg1;
+ // let <pattern> = __arg1;
+ // let __arg0 = __arg0;
+ // let <pattern> = __arg0;
+ // drop-temps { <body> } // see comments later in fn for details
+ // }
+ // }
+ //
+ // 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();
+
+ if let Some(&self_param) = self_params.first() {
+ let Binding { ref name, mode, hygiene, .. } = self.store.bindings[self_param];
+ let name = name.clone();
+ let child_binding_id = self.alloc_binding(name.clone(), mode, 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);
+ let expr = self.alloc_expr_desugared(Expr::Path(name.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,
+ });
+ self_params.push(child_binding_id);
+ }
+
for param in params {
- let (name, hygiene) = match self.store.pats[*param] {
- Pat::Bind { id, .. }
+ let (name, hygiene, is_simple_parameter) = match self.store.pats[*param] {
+ // 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, .. }
if matches!(
self.store.bindings[id].mode,
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
) =>
{
- // If this is a direct binding, we can leave it as-is, as it'll always be captured anyway.
- continue;
+ (self.store.bindings[id].name.clone(), self.store.bindings[id].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)
+ (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, false)
}
- _ => (self.generate_new_name(), HygieneId::ROOT),
+ _ => (self.generate_new_name(), HygieneId::ROOT, false),
};
- let binding_id = self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
- let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None });
- let expr = self.alloc_expr_desugared(Expr::Path(name.into()));
+ 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: *param,
+ pat: child_pat_id,
type_ref: None,
initializer: Some(expr),
else_branch: None,
});
- *param = pat_id;
+ if !is_simple_parameter {
+ 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,
+ 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 async_ = self.async_block(
+ let coroutine = self.desugared_coroutine_expr(
+ kind,
coroutine_source,
// The default capture mode here is by-ref. Later on during upvar analysis,
// we will force the captured arguments to by-move, but for async closures,
@@ -1000,11 +1075,12 @@ impl<'db> ExprCollector<'db> {
Some(body),
);
// It's important that this comes last, see the lowering of async closures for why.
- self.alloc_expr_desugared(async_)
+ self.alloc_expr_desugared(coroutine)
}
- fn async_block(
+ fn desugared_coroutine_expr(
&mut self,
+ kind: CoroutineKind,
source: CoroutineSource,
capture_by: CaptureBy,
id: Option<BlockId>,
@@ -1017,22 +1093,37 @@ impl<'db> ExprCollector<'db> {
arg_types: Box::default(),
ret_type: None,
body: block,
- closure_kind: ClosureKind::AsyncBlock { source },
+ closure_kind: ClosureKind::Coroutine { kind, source },
capture_by,
}
}
fn collect(
&mut self,
+ self_params: &mut ArrayVec<BindingId, 2>,
params: &mut [PatId],
expr: Option<ast::Expr>,
awaitable: Awaitable,
+ is_async_fn: bool,
+ is_gen_fn: bool,
) -> ExprId {
self.awaitable_context.replace(awaitable);
self.with_label_rib(RibKind::Closure, |this| {
let body = this.collect_expr_opt(expr);
- if awaitable == Awaitable::Yes {
- this.lower_async_block_with_moved_arguments(params, body, CoroutineSource::Fn)
+ if is_async_fn || is_gen_fn {
+ let kind = match (is_async_fn, is_gen_fn) {
+ (true, true) => CoroutineKind::AsyncGen,
+ (true, false) => CoroutineKind::Async,
+ (false, true) => CoroutineKind::Gen,
+ (false, false) => unreachable!(),
+ };
+ this.lower_coroutine_body_with_moved_arguments(
+ self_params,
+ params,
+ body,
+ kind,
+ CoroutineSource::Fn,
+ )
} else {
body
}
@@ -1109,17 +1200,17 @@ impl<'db> ExprCollector<'db> {
}
fn lower_const_arg_opt(&mut self, arg: Option<ast::ConstArg>) -> ConstRef {
- let const_expr_origins = self.store.inference_roots.take();
- let r = ConstRef { expr: self.collect_expr_opt(arg.and_then(|it| it.expr())) };
- self.store.inference_roots = const_expr_origins;
- r
+ ConstRef {
+ expr: self.with_fresh_binding_expr_root(|this| {
+ this.collect_expr_opt(arg.and_then(|arg| arg.expr()))
+ }),
+ }
}
pub fn lower_const_arg(&mut self, arg: ast::ConstArg) -> ConstRef {
- let const_expr_origins = self.store.inference_roots.take();
- let r = ConstRef { expr: self.collect_expr_opt(arg.expr()) };
- self.store.inference_roots = const_expr_origins;
- r
+ ConstRef {
+ expr: self.with_fresh_binding_expr_root(|this| this.collect_expr_opt(arg.expr())),
+ }
}
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -1191,7 +1282,44 @@ impl<'db> ExprCollector<'db> {
self.with_label_rib(RibKind::Closure, |this| {
this.with_awaitable_block(Awaitable::Yes, |this| {
this.collect_block_(e, |this, id, statements, tail| {
- this.async_block(
+ this.desugared_coroutine_expr(
+ CoroutineKind::Async,
+ CoroutineSource::Block,
+ capture_by,
+ id,
+ statements,
+ tail,
+ )
+ })
+ })
+ })
+ }
+ Some(ast::BlockModifier::Gen(_)) => {
+ let capture_by =
+ if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+ self.with_label_rib(RibKind::Closure, |this| {
+ this.with_awaitable_block(Awaitable::No("non-async gen block"), |this| {
+ this.collect_block_(e, |this, id, statements, tail| {
+ this.desugared_coroutine_expr(
+ CoroutineKind::Gen,
+ CoroutineSource::Block,
+ capture_by,
+ id,
+ statements,
+ tail,
+ )
+ })
+ })
+ })
+ }
+ Some(ast::BlockModifier::AsyncGen(_)) => {
+ let capture_by =
+ if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+ self.with_label_rib(RibKind::Closure, |this| {
+ this.with_awaitable_block(Awaitable::Yes, |this| {
+ this.collect_block_(e, |this, id, statements, tail| {
+ this.desugared_coroutine_expr(
+ CoroutineKind::AsyncGen,
CoroutineSource::Block,
capture_by,
id,
@@ -1212,14 +1340,6 @@ impl<'db> ExprCollector<'db> {
})
})
}
- // FIXME
- Some(ast::BlockModifier::AsyncGen(_)) => {
- self.with_awaitable_block(Awaitable::Yes, |this| this.collect_block(e))
- }
- Some(ast::BlockModifier::Gen(_)) => self
- .with_awaitable_block(Awaitable::No("non-async gen block"), |this| {
- this.collect_block(e)
- }),
None => self.collect_block(e),
},
ast::Expr::LoopExpr(e) => {
@@ -1347,8 +1467,10 @@ impl<'db> ExprCollector<'db> {
ast::Expr::RecordExpr(e) => {
let path = e
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return Some(self.missing_expr());
+ };
let record_lit = if let Some(nfl) = e.record_expr_field_list() {
let fields = nfl
.fields()
@@ -1425,11 +1547,11 @@ impl<'db> ExprCollector<'db> {
}
}
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
- this.with_binding_owner_and_return(|this| {
+ let mut is_coroutine_closure = false;
+ let closure = this.with_binding_owner_and_return(|this| {
let mut args = Vec::new();
let mut arg_types = Vec::new();
// For coroutine closures, the body, aka. the coroutine is the bindings owner, and not the closure.
- let mut body_is_bindings_owner = false;
if let Some(pl) = e.param_list() {
let num_params = pl.params().count();
args.reserve_exact(num_params);
@@ -1457,25 +1579,38 @@ impl<'db> ExprCollector<'db> {
};
let mut body = this
.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
-
- let closure_kind = if this.is_lowering_coroutine {
- let movability = if e.static_token().is_some() {
- Movability::Static
+ let kind = {
+ if e.async_token().is_some() && e.gen_token().is_some() {
+ Some(CoroutineKind::AsyncGen)
+ } else if e.async_token().is_some() {
+ Some(CoroutineKind::Async)
+ } else if e.gen_token().is_some() {
+ Some(CoroutineKind::Gen)
} else {
- Movability::Movable
- };
- ClosureKind::Coroutine(movability)
- } else if e.async_token().is_some() {
+ None
+ }
+ };
+
+ 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()`.
- body = this.lower_async_block_with_moved_arguments(
+ body = this.lower_coroutine_body_with_moved_arguments(
+ &mut ArrayVec::new(),
&mut args,
body,
+ kind,
CoroutineSource::Closure,
);
- body_is_bindings_owner = true;
+ is_coroutine_closure = true;
- ClosureKind::AsyncClosure
+ ClosureKind::CoroutineClosure(kind)
+ } else if this.is_lowering_coroutine {
+ let movability = if e.static_token().is_some() {
+ Movability::Static
+ } else {
+ Movability::Movable
+ };
+ ClosureKind::OldCoroutine(movability)
} else {
ClosureKind::Closure
};
@@ -1495,8 +1630,23 @@ impl<'db> ExprCollector<'db> {
syntax_ptr,
);
- (if body_is_bindings_owner { body } else { closure }, closure)
- })
+ (if is_coroutine_closure { body } else { closure }, closure)
+ });
+
+ if is_coroutine_closure {
+ let Expr::Closure { args, .. } = &this.store.exprs[closure] else {
+ unreachable!()
+ };
+ for &arg in args {
+ let Pat::Bind { id, .. } = this.store.pats[arg] else {
+ never!("`lower_coroutine_body_with_moved_arguments()` should make sure the coroutine closure only have simple bind args");
+ continue;
+ };
+ this.store.binding_owners.insert(id, closure);
+ }
+ }
+
+ closure
}),
ast::Expr::BinExpr(e) => {
let op = e.op_kind();
@@ -1611,6 +1761,37 @@ impl<'db> ExprCollector<'db> {
})
}
+ /// Whether this path should be lowered as destructuring assignment, or as a normal assignment.
+ fn path_is_destructuring_assignment(&self, path: &ModPath) -> bool {
+ // rustc has access to a full resolver here, including local variables and generic params, and it checks the following
+ // criteria: a path not lowered as destructuring assignment if it can *fully resolve* to something that is *not*
+ // a const, a unit struct or a variant.
+ // We don't have access to a full resolver here. So we should do the same as rustc, but assuming that local variables
+ // could be resolved to nothing (fortunately, there cannot be a local variable shadowing a unit struct/variant/const,
+ // as that is an error). We don't need to consider const params as it's an error to refer to these in patterns.
+ let (resolution, unresolved_idx, _) = self.def_map.resolve_path_locally(
+ self.local_def_map,
+ self.db,
+ self.module,
+ path,
+ BuiltinShadowMode::Other,
+ );
+ match unresolved_idx {
+ Some(_) => {
+ // If `Some(_)`, path could be resolved to unit struct/variant/const with type information, i.e. an assoc type or const.
+ // If `None`, path could be a local variable.
+ resolution.take_types().is_some()
+ }
+ None => match resolution.take_values() {
+ // We don't need to consider non-unit structs/variants, as those are not value types.
+ Some(ModuleDefId::EnumVariantId(_))
+ | Some(ModuleDefId::AdtId(_))
+ | Some(ModuleDefId::ConstId(_)) => true,
+ _ => false,
+ },
+ }
+ }
+
fn collect_expr_as_pat_opt(&mut self, expr: Option<ast::Expr>) -> PatId {
match expr {
Some(expr) => self.collect_expr_as_pat(expr),
@@ -1670,21 +1851,25 @@ impl<'db> ExprCollector<'db> {
let path = collect_path(self, e.expr()?)?;
let path = path
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return Some(self.missing_pat());
+ };
let (ellipsis, args) = collect_tuple(self, e.arg_list()?.args());
self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr)
}
ast::Expr::PathExpr(e) => {
- let (path, hygiene) = self
- .collect_expr_path(e.clone())
- .map(|(path, hygiene)| (Pat::Path(path), hygiene))
- .unwrap_or((Pat::Missing, HygieneId::ROOT));
- let pat_id = self.alloc_pat_from_expr(path, syntax_ptr);
- if !hygiene.is_root() {
- self.store.ident_hygiene.insert(pat_id.into(), hygiene);
+ let (path, hygiene) = self.collect_expr_path(e.clone())?;
+ let mod_path = path.mod_path().expect("should not lower to lang path");
+ if self.path_is_destructuring_assignment(mod_path) {
+ let pat_id = self.alloc_pat_from_expr(Pat::Path(path), syntax_ptr);
+ if !hygiene.is_root() {
+ self.store.ident_hygiene.insert(pat_id.into(), hygiene);
+ }
+ pat_id
+ } else {
+ return None;
}
- pat_id
}
ast::Expr::MacroExpr(e) => {
let e = e.macro_call()?;
@@ -1699,11 +1884,15 @@ impl<'db> ExprCollector<'db> {
ast::Expr::RecordExpr(e) => {
let path = e
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return Some(self.missing_pat());
+ };
let record_field_list = e.record_expr_field_list()?;
let ellipsis = record_field_list.dotdot_token().is_some();
- // FIXME: Report an error here if `record_field_list.spread().is_some()`.
+ // We wanted to emit an error here if `record_field_list.spread().is_some()`,
+ // but that's already a syntax error in rustc, so we decided not to.
+ // See https://github.com/rust-lang/rust-analyzer/pull/22206#discussion_r3156097370
let args = record_field_list
.fields()
.filter_map(|f| {
@@ -1954,24 +2143,27 @@ impl<'db> ExprCollector<'db> {
/// ```
fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
let lang_items = self.lang_items();
- let into_iter_fn = self.lang_path(lang_items.IntoIterIntoIter);
- let iter_next_fn = self.lang_path(lang_items.IteratorNext);
- let option_some = self.lang_path(lang_items.OptionSome);
- let option_none = self.lang_path(lang_items.OptionNone);
+ let (Some(into_iter_fn), Some(iter_next_fn), Some(option_some), Some(option_none)) = (
+ self.lang_path(lang_items.IntoIterIntoIter),
+ self.lang_path(lang_items.IteratorNext),
+ self.lang_path(lang_items.OptionSome),
+ self.lang_path(lang_items.OptionNone),
+ ) else {
+ return self.missing_expr();
+ };
let head = self.collect_expr_opt(e.iterable());
- let into_iter_fn_expr =
- self.alloc_expr(into_iter_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr);
+ let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr);
let iterator = self.alloc_expr(
Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) },
syntax_ptr,
);
let none_arm = MatchArm {
- pat: self.alloc_pat_desugared(option_none.map_or(Pat::Missing, Pat::Path)),
+ pat: self.alloc_pat_desugared(Pat::Path(option_none)),
guard: None,
expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr),
};
let some_pat = Pat::TupleStruct {
- path: option_some.map(Box::new),
+ path: option_some,
args: Box::new([self.collect_pat_top(e.pat())]),
ellipsis: None,
};
@@ -1991,8 +2183,7 @@ impl<'db> ExprCollector<'db> {
Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
syntax_ptr,
);
- let iter_next_fn_expr =
- self.alloc_expr(iter_next_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr);
+ let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr);
let iter_next_expr = self.alloc_expr(
Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) },
syntax_ptr,
@@ -2040,11 +2231,15 @@ impl<'db> ExprCollector<'db> {
/// ```
fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
let lang_items = self.lang_items();
- let try_branch = self.lang_path(lang_items.TryTraitBranch);
- let cf_continue = self.lang_path(lang_items.ControlFlowContinue);
- let cf_break = self.lang_path(lang_items.ControlFlowBreak);
+ let (Some(try_branch), Some(cf_continue), Some(cf_break)) = (
+ self.lang_path(lang_items.TryTraitBranch),
+ self.lang_path(lang_items.ControlFlowContinue),
+ self.lang_path(lang_items.ControlFlowBreak),
+ ) else {
+ return self.missing_expr();
+ };
let operand = self.collect_expr_opt(e.expr());
- let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr);
+ let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr);
let expr = self
.alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
let continue_name = self.generate_new_name();
@@ -2058,7 +2253,7 @@ impl<'db> ExprCollector<'db> {
self.add_definition_to_binding(continue_binding, continue_bpat);
let continue_arm = MatchArm {
pat: self.alloc_pat_desugared(Pat::TupleStruct {
- path: cf_continue.map(Box::new),
+ path: cf_continue,
args: Box::new([continue_bpat]),
ellipsis: None,
}),
@@ -2072,7 +2267,7 @@ impl<'db> ExprCollector<'db> {
self.add_definition_to_binding(break_binding, break_bpat);
let break_arm = MatchArm {
pat: self.alloc_pat_desugared(Pat::TupleStruct {
- path: cf_break.map(Box::new),
+ path: cf_break,
args: Box::new([break_bpat]),
ellipsis: None,
}),
@@ -2306,7 +2501,7 @@ impl<'db> ExprCollector<'db> {
) -> ExprId {
let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| {
let ast_id = self.expander.in_file(file_local_id);
- self.db.intern_block(BlockLoc { ast_id, module: self.module })
+ BlockId::new(self.db, BlockLoc { ast_id, module: self.module })
});
let (module, def_map) =
@@ -2373,9 +2568,7 @@ impl<'db> ExprCollector<'db> {
let Some(pat) = pat else { return self.missing_pat() };
match &pat {
- ast::Pat::IdentPat(bp) => {
- // FIXME: Emit an error if `!bp.is_simple_ident()`.
-
+ ast::Pat::IdentPat(bp) if bp.is_simple_ident() => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let hygiene = bp
.name()
@@ -2387,8 +2580,12 @@ impl<'db> ExprCollector<'db> {
self.add_definition_to_binding(binding, pat);
pat
}
- // FIXME: Emit an error.
- _ => self.missing_pat(),
+ _ => {
+ self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn {
+ node: self.expander.in_file(AstPtr::new(&pat)),
+ });
+ self.missing_pat()
+ }
}
}
@@ -2465,8 +2662,10 @@ impl<'db> ExprCollector<'db> {
ast::Pat::TupleStructPat(p) => {
let path = p
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return self.missing_pat();
+ };
let (args, ellipsis) = self.collect_tuple_pat(
p.fields(),
comma_follows_token(p.l_paren_token()),
@@ -2531,8 +2730,10 @@ impl<'db> ExprCollector<'db> {
ast::Pat::RecordPat(p) => {
let path = p
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return self.missing_pat();
+ };
let record_pat_field_list =
&p.record_pat_field_list().expect("every struct should have a field list");
let args = record_pat_field_list
@@ -2572,19 +2773,15 @@ impl<'db> ExprCollector<'db> {
let expr_id = self.alloc_expr(expr, expr_ptr);
Pat::Lit(expr_id)
}
- ast::Pat::RestPat(_) => {
- // `RestPat` requires special handling and should not be mapped
- // to a Pat. Here we are using `Pat::Missing` as a fallback for
- // when `RestPat` is mapped to `Pat`, which can easily happen
- // when the source code being analyzed has a malformed pattern
- // which includes `..` in a place where it isn't valid.
-
- Pat::Missing
- }
+ ast::Pat::RestPat(_) => Pat::Rest,
ast::Pat::BoxPat(boxpat) => {
let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
Pat::Box { inner }
}
+ ast::Pat::DerefPat(inner) => {
+ let inner = self.collect_pat_opt(inner.pat(), binding_list);
+ Pat::Deref { inner }
+ }
ast::Pat::ConstBlockPat(const_block_pat) => {
if let Some(block) = const_block_pat.block_expr() {
let expr_id = self.with_label_rib(RibKind::Constant, |this| {
@@ -2712,7 +2909,7 @@ impl<'db> ExprCollector<'db> {
// endregion: patterns
- /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
+ /// Returns `false` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `true` when
/// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool {
let enabled = self.expander.is_cfg_enabled(owner, self.cfg_options);
@@ -2756,7 +2953,7 @@ impl<'db> ExprCollector<'db> {
None
} else {
hygiene_id.syntax_context().outer_expn(self.db).map(|expansion| {
- let expansion = self.db.lookup_intern_macro_call(expansion.into());
+ let expansion = hir_expand::MacroCallId::from(expansion).loc(self.db);
(hygiene_id.syntax_context().parent(self.db), expansion.def)
})
};
@@ -2786,7 +2983,7 @@ impl<'db> ExprCollector<'db> {
hygiene_id = HygieneId::new(parent_ctx.opaque_and_semiopaque(self.db));
hygiene_info = parent_ctx.outer_expn(self.db).map(|expansion| {
- let expansion = self.db.lookup_intern_macro_call(expansion.into());
+ let expansion = hir_expand::MacroCallId::from(expansion).loc(self.db);
(parent_ctx.parent(self.db), expansion.def)
});
}
@@ -2897,6 +3094,31 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)>
}
impl ExprCollector<'_> {
+ fn with_fresh_binding_expr_root(&mut self, f: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
+ self.with_expr_root(|this| this.with_binding_owner(f))
+ }
+
+ fn with_expr_root(&mut self, f: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
+ let inference_roots = self.store.inference_roots.take();
+ let root = f(self);
+ self.store.inference_roots = inference_roots;
+
+ if let Some(inference_roots) = &mut self.store.inference_roots {
+ inference_roots.push(ExprRoot {
+ root,
+ exprs_end: end(&self.store.exprs),
+ pats_end: end(&self.store.pats),
+ bindings_end: end(&self.store.bindings),
+ });
+ }
+
+ return root;
+
+ fn end<T>(arena: &la_arena::Arena<T>) -> la_arena::Idx<T> {
+ la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(arena.len() as u32))
+ }
+ }
+
fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
let src = self.expander.in_file(ptr);
let id = self.store.exprs.alloc(expr);