Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/body/lower.rs')
-rw-r--r--crates/hir-def/src/body/lower.rs641
1 files changed, 373 insertions, 268 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 5362737583..688c9e86bb 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -7,77 +7,38 @@ use base_db::CrateId;
use either::Either;
use hir_expand::{
ast_id_map::AstIdMap,
- hygiene::Hygiene,
name::{name, AsName, Name},
- AstId, ExpandError, HirFileId, InFile,
+ AstId, ExpandError, InFile,
};
use intern::Interned;
use la_arena::Arena;
-use once_cell::unsync::OnceCell;
use profile::Count;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use syntax::{
ast::{
- self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind,
+ self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName,
SlicePatComponents,
},
AstNode, AstPtr, SyntaxNodePtr,
};
use crate::{
- adt::StructKind,
- body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr},
- body::{BodyDiagnostic, ExprSource, PatSource},
- builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
+ body::{Body, BodyDiagnostic, BodySourceMap, Expander, ExprPtr, LabelPtr, LowerCtx, PatPtr},
+ data::adt::StructKind,
db::DefDatabase,
- expr::{
- dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
- FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
- RecordFieldPat, RecordLitField, Statement,
+ hir::{
+ dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr,
+ ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat,
+ RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
- item_tree::ItemTree,
lang_item::LangItem,
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
};
-pub struct LowerCtx<'a> {
- pub db: &'a dyn DefDatabase,
- hygiene: Hygiene,
- ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
-}
-
-impl<'a> LowerCtx<'a> {
- pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
- LowerCtx {
- db,
- hygiene: Hygiene::new(db.upcast(), file_id),
- ast_id_map: Some((file_id, OnceCell::new())),
- }
- }
-
- pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
- LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
- }
-
- pub(crate) fn hygiene(&self) -> &Hygiene {
- &self.hygiene
- }
-
- pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
- Path::from_src(ast, self)
- }
-
- pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<AstId<N>> {
- let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
- let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
- Some(InFile::new(file_id, ast_id_map.ast_id(item)))
- }
-}
-
pub(super) fn lower(
db: &dyn DefDatabase,
expander: Expander,
@@ -102,9 +63,11 @@ pub(super) fn lower(
_c: Count::new(),
},
expander,
- current_try_block: None,
+ current_try_block_label: None,
is_lowering_assignee_expr: false,
is_lowering_generator: false,
+ label_ribs: Vec::new(),
+ current_binding_owner: None,
}
.collect(params, body, is_async_fn)
}
@@ -113,12 +76,57 @@ struct ExprCollector<'a> {
db: &'a dyn DefDatabase,
expander: Expander,
ast_id_map: Arc<AstIdMap>,
- body: Body,
krate: CrateId,
+ body: Body,
source_map: BodySourceMap,
- current_try_block: Option<LabelId>,
+
is_lowering_assignee_expr: bool,
is_lowering_generator: bool,
+
+ current_try_block_label: Option<LabelId>,
+ // points to the expression that a try expression will target (replaces current_try_block_label)
+ // catch_scope: Option<ExprId>,
+ // points to the expression that an unlabeled control flow will target
+ // loop_scope: Option<ExprId>,
+ // needed to diagnose non label control flow in while conditions
+ // is_in_loop_condition: bool,
+
+ // resolution
+ label_ribs: Vec<LabelRib>,
+ current_binding_owner: Option<ExprId>,
+}
+
+#[derive(Clone, Debug)]
+struct LabelRib {
+ kind: RibKind,
+ // Once we handle macro hygiene this will need to be a map
+ label: Option<(Name, LabelId)>,
+}
+
+impl LabelRib {
+ fn new(kind: RibKind) -> Self {
+ LabelRib { kind, label: None }
+ }
+ fn new_normal(label: (Name, LabelId)) -> Self {
+ LabelRib { kind: RibKind::Normal, label: Some(label) }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum RibKind {
+ Normal,
+ Closure,
+ Constant,
+}
+
+impl RibKind {
+ /// This rib forbids referring to labels defined in upwards ribs.
+ fn is_label_barrier(self) -> bool {
+ match self {
+ RibKind::Normal => false,
+ RibKind::Closure | RibKind::Constant => true,
+ }
+ }
}
#[derive(Debug, Default)]
@@ -162,97 +170,35 @@ impl ExprCollector<'_> {
self.body.params.push(param_pat);
}
- for pat in param_list
- .params()
- .zip(attr_enabled)
- .filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
+ for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
{
- let param_pat = self.collect_pat(pat);
+ let param_pat = self.collect_pat_top(param.pat());
self.body.params.push(param_pat);
}
};
+ self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
+ if is_async_fn {
+ match body {
+ Some(e) => {
+ let expr = this.collect_expr(e);
+ this.alloc_expr_desugared(Expr::Async {
+ id: None,
+ statements: Box::new([]),
+ tail: Some(expr),
+ })
+ }
+ None => this.missing_expr(),
+ }
+ } else {
+ this.collect_expr_opt(body)
+ }
+ });
- self.body.body_expr = if is_async_fn {
- self.current_try_block =
- Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
- let expr = self.collect_expr_opt(body);
- let expr = self.alloc_expr_desugared(Expr::Block {
- id: None,
- statements: Box::new([]),
- tail: Some(expr),
- label: self.current_try_block,
- });
- let expr = self.alloc_expr_desugared(Expr::Async {
- id: None,
- statements: Box::new([]),
- tail: Some(expr),
- });
- expr
- } else {
- self.collect_expr_opt(body)
- };
(self.body, self.source_map)
}
fn ctx(&self) -> LowerCtx<'_> {
- LowerCtx::new(self.db, self.expander.current_file_id)
- }
-
- fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
- let src = self.expander.to_source(ptr);
- let id = self.make_expr(expr, src.clone());
- self.source_map.expr_map.insert(src, id);
- id
- }
- // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
- fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
- self.body.exprs.alloc(expr)
- }
- fn missing_expr(&mut self) -> ExprId {
- self.alloc_expr_desugared(Expr::Missing)
- }
- fn make_expr(&mut self, expr: Expr, src: ExprSource) -> ExprId {
- let id = self.body.exprs.alloc(expr);
- self.source_map.expr_map_back.insert(id, src);
- id
- }
-
- fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
- self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
- }
- fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
- let src = self.expander.to_source(ptr);
- let id = self.make_pat(pat, src.clone());
- self.source_map.pat_map.insert(src, id);
- id
- }
- // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
- fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
- self.body.pats.alloc(pat)
- }
- fn missing_pat(&mut self) -> PatId {
- self.body.pats.alloc(Pat::Missing)
- }
- fn make_pat(&mut self, pat: Pat, src: PatSource) -> PatId {
- let id = self.body.pats.alloc(pat);
- self.source_map.pat_map_back.insert(id, src);
- id
- }
-
- fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
- let src = self.expander.to_source(ptr);
- let id = self.make_label(label, src.clone());
- self.source_map.label_map.insert(src, id);
- id
- }
- // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
- fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
- self.body.labels.alloc(label)
- }
- fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
- let id = self.body.labels.alloc(label);
- self.source_map.label_map_back.insert(id, src);
- id
+ self.expander.ctx(self.db)
}
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -264,6 +210,7 @@ impl ExprCollector<'_> {
let syntax_ptr = AstPtr::new(&expr);
self.check_cfg(&expr)?;
+ // FIXME: Move some of these arms out into separate methods for clarity
Some(match expr {
ast::Expr::IfExpr(e) => {
let then_branch = self.collect_block_opt(e.then_branch());
@@ -281,12 +228,12 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
}
ast::Expr::LetExpr(e) => {
- let pat = self.collect_pat_opt(e.pat());
+ let pat = self.collect_pat_top(e.pat());
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
}
ast::Expr::BlockExpr(e) => match e.modifier() {
- Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e),
+ Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
Some(ast::BlockModifier::Unsafe(_)) => {
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
id,
@@ -296,28 +243,48 @@ impl ExprCollector<'_> {
}
Some(ast::BlockModifier::Label(label)) => {
let label = self.collect_label(label);
- self.collect_block_(e, |id, statements, tail| Expr::Block {
- id,
- statements,
- tail,
- label: Some(label),
+ self.with_labeled_rib(label, |this| {
+ this.collect_block_(e, |id, statements, tail| Expr::Block {
+ id,
+ statements,
+ tail,
+ label: Some(label),
+ })
+ })
+ }
+ Some(ast::BlockModifier::Async(_)) => {
+ self.with_label_rib(RibKind::Closure, |this| {
+ this.collect_block_(e, |id, statements, tail| Expr::Async {
+ id,
+ statements,
+ tail,
+ })
+ })
+ }
+ Some(ast::BlockModifier::Const(_)) => {
+ self.with_label_rib(RibKind::Constant, |this| {
+ this.collect_as_a_binding_owner_bad(
+ |this| {
+ this.collect_block_(e, |id, statements, tail| Expr::Const {
+ id,
+ statements,
+ tail,
+ })
+ },
+ syntax_ptr,
+ )
})
}
- Some(ast::BlockModifier::Async(_)) => self
- .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
- Some(ast::BlockModifier::Const(_)) => self
- .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
None => self.collect_block(e),
},
ast::Expr::LoopExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
- let body = self.collect_block_opt(e.loop_body());
+ let body = self.collect_labelled_block_opt(label, e.loop_body());
self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
}
ast::Expr::WhileExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
- let body = self.collect_block_opt(e.loop_body());
-
+ let body = self.collect_labelled_block_opt(label, e.loop_body());
let condition = self.collect_expr_opt(e.condition());
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
@@ -325,8 +292,8 @@ impl ExprCollector<'_> {
ast::Expr::ForExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
let iterable = self.collect_expr_opt(e.iterable());
- let pat = self.collect_pat_opt(e.pat());
- let body = self.collect_block_opt(e.loop_body());
+ let pat = self.collect_pat_top(e.pat());
+ let body = self.collect_labelled_block_opt(label, e.loop_body());
self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
}
ast::Expr::CallExpr(e) => {
@@ -365,7 +332,7 @@ impl ExprCollector<'_> {
.arms()
.filter_map(|arm| {
self.check_cfg(&arm).map(|()| MatchArm {
- pat: self.collect_pat_opt(arm.pat()),
+ pat: self.collect_pat_top(arm.pat()),
expr: self.collect_expr_opt(arm.expr()),
guard: arm
.guard()
@@ -386,16 +353,20 @@ impl ExprCollector<'_> {
.unwrap_or(Expr::Missing);
self.alloc_expr(path, syntax_ptr)
}
- ast::Expr::ContinueExpr(e) => self.alloc_expr(
- Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
- syntax_ptr,
- ),
+ ast::Expr::ContinueExpr(e) => {
+ let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+ self.source_map.diagnostics.push(e);
+ None
+ });
+ self.alloc_expr(Expr::Continue { label }, syntax_ptr)
+ }
ast::Expr::BreakExpr(e) => {
+ let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+ self.source_map.diagnostics.push(e);
+ None
+ });
let expr = e.expr().map(|e| self.collect_expr(e));
- self.alloc_expr(
- Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
- syntax_ptr,
- )
+ self.alloc_expr(Expr::Break { expr, label }, syntax_ptr)
}
ast::Expr::ParenExpr(e) => {
let inner = self.collect_expr_opt(e.expr());
@@ -496,14 +467,16 @@ impl ExprCollector<'_> {
None => self.alloc_expr(Expr::Missing, syntax_ptr),
}
}
- ast::Expr::ClosureExpr(e) => {
+ ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
+ let (result_expr_id, prev_binding_owner) =
+ this.initialize_binding_owner(syntax_ptr);
let mut args = Vec::new();
let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() {
for param in pl.params() {
- let pat = self.collect_pat_opt(param.pat());
+ let pat = this.collect_pat_top(param.pat());
let type_ref =
- param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+ param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
args.push(pat);
arg_types.push(type_ref);
}
@@ -511,14 +484,13 @@ impl ExprCollector<'_> {
let ret_type = e
.ret_type()
.and_then(|r| r.ty())
- .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+ .map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
- let prev_is_lowering_generator = self.is_lowering_generator;
- self.is_lowering_generator = false;
+ let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator);
- let body = self.collect_expr_opt(e.body());
+ let body = this.collect_expr_opt(e.body());
- let closure_kind = if self.is_lowering_generator {
+ let closure_kind = if this.is_lowering_generator {
let movability = if e.static_token().is_some() {
Movability::Static
} else {
@@ -530,19 +502,21 @@ impl ExprCollector<'_> {
} else {
ClosureKind::Closure
};
- self.is_lowering_generator = prev_is_lowering_generator;
-
- self.alloc_expr(
- Expr::Closure {
- args: args.into(),
- arg_types: arg_types.into(),
- ret_type,
- body,
- closure_kind,
- },
- syntax_ptr,
- )
- }
+ this.is_lowering_generator = prev_is_lowering_generator;
+ let capture_by =
+ if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+ this.is_lowering_generator = prev_is_lowering_generator;
+ this.current_binding_owner = prev_binding_owner;
+ this.body.exprs[result_expr_id] = Expr::Closure {
+ args: args.into(),
+ arg_types: arg_types.into(),
+ ret_type,
+ body,
+ closure_kind,
+ capture_by,
+ };
+ result_expr_id
+ }),
ast::Expr::BinExpr(e) => {
let op = e.op_kind();
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
@@ -581,7 +555,17 @@ impl ExprCollector<'_> {
}
ArrayExprKind::Repeat { initializer, repeat } => {
let initializer = self.collect_expr_opt(initializer);
- let repeat = self.collect_expr_opt(repeat);
+ let repeat = self.with_label_rib(RibKind::Constant, |this| {
+ if let Some(repeat) = repeat {
+ let syntax_ptr = AstPtr::new(&repeat);
+ this.collect_as_a_binding_owner_bad(
+ |this| this.collect_expr(repeat),
+ syntax_ptr,
+ )
+ } else {
+ this.missing_expr()
+ }
+ });
self.alloc_expr(
Expr::Array(Array::Repeat { initializer, repeat }),
syntax_ptr,
@@ -627,23 +611,53 @@ impl ExprCollector<'_> {
})
}
+ fn initialize_binding_owner(
+ &mut self,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> (ExprId, Option<ExprId>) {
+ let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
+ let prev_binding_owner = self.current_binding_owner.take();
+ self.current_binding_owner = Some(result_expr_id);
+ (result_expr_id, prev_binding_owner)
+ }
+
+ /// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
+ /// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
+ /// their own body. Don't add more usage for this function so that we can remove this function after
+ /// separating those bodies.
+ fn collect_as_a_binding_owner_bad(
+ &mut self,
+ job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> ExprId {
+ let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
+ let tmp = job(self);
+ self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
+ self.current_binding_owner = prev_owner;
+ id
+ }
+
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
/// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
/// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
- fn collect_try_block(&mut self, e: BlockExpr) -> ExprId {
+ fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
- return self.alloc_expr_desugared(Expr::Missing);
+ return self.collect_block(e);
};
- let prev_try_block = self.current_try_block.take();
- self.current_try_block =
- Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
- let expr_id = self.collect_block(e);
+ let label = self.alloc_label_desugared(Label { name: Name::generate_new_name() });
+ let old_label = self.current_try_block_label.replace(label);
+
+ let (btail, expr_id) = self.with_labeled_rib(label, |this| {
+ let mut btail = None;
+ let block = this.collect_block_(e, |id, statements, tail| {
+ btail = tail;
+ Expr::Block { id, statements, tail, label: Some(label) }
+ });
+ (btail, block)
+ });
+
let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
- let Expr::Block { label, tail, .. } = &mut self.body.exprs[expr_id] else {
- unreachable!("It is the output of collect block");
- };
- *label = self.current_try_block;
- let next_tail = match *tail {
+ let next_tail = match btail {
Some(tail) => self.alloc_expr_desugared(Expr::Call {
callee,
args: Box::new([tail]),
@@ -662,10 +676,10 @@ impl ExprCollector<'_> {
}
};
let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
- unreachable!("It is the output of collect block");
+ unreachable!("block was lowered to non-block");
};
*tail = Some(next_tail);
- self.current_try_block = prev_try_block;
+ self.current_try_block_label = old_label;
expr_id
}
@@ -735,12 +749,13 @@ impl ExprCollector<'_> {
Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
syntax_ptr.clone(),
);
- if let Some(label) = self.current_try_block {
- let label = Some(self.body.labels[label].name.clone());
- self.alloc_expr(Expr::Break { expr: Some(result), label }, syntax_ptr.clone())
- } else {
- self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone())
- }
+ self.alloc_expr(
+ match self.current_try_block_label {
+ Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
+ None => Expr::Return { expr: Some(result) },
+ },
+ syntax_ptr.clone(),
+ )
},
};
let arms = Box::new([continue_arm, break_arm]);
@@ -785,7 +800,7 @@ impl ExprCollector<'_> {
krate: *krate,
});
}
- Some(ExpandError::RecursionOverflowPosioned) => {
+ Some(ExpandError::RecursionOverflowPoisoned) => {
// Recursion limit has been reached in the macro expansion tree, but not in
// this very macro call. Don't add diagnostics to avoid duplication.
}
@@ -866,7 +881,7 @@ impl ExprCollector<'_> {
if self.check_cfg(&stmt).is_none() {
return;
}
- let pat = self.collect_pat_opt(stmt.pat());
+ let pat = self.collect_pat_top(stmt.pat());
let type_ref =
stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
@@ -911,10 +926,20 @@ impl ExprCollector<'_> {
block: ast::BlockExpr,
mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
) -> ExprId {
- let file_local_id = self.ast_id_map.ast_id(&block);
- let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
+ let block_has_items = {
+ let statement_has_item = block.statements().any(|stmt| match stmt {
+ ast::Stmt::Item(_) => true,
+ // Macro calls can be both items and expressions. The syntax library always treats
+ // them as expressions here, so we undo that.
+ ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
+ _ => false,
+ });
+ statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
+ };
- let block_id = if ItemTree::block_has_items(self.db, ast_id.file_id, &block) {
+ let block_id = if block_has_items {
+ let file_local_id = self.ast_id_map.ast_id(&block);
+ let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
Some(self.db.intern_block(BlockLoc {
ast_id,
module: self.expander.def_map.module_id(self.expander.module),
@@ -966,32 +991,34 @@ impl ExprCollector<'_> {
}
}
- fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
- let label = Label {
- name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
- };
- self.alloc_label(label, AstPtr::new(&ast_label))
+ fn collect_labelled_block_opt(
+ &mut self,
+ label: Option<LabelId>,
+ expr: Option<ast::BlockExpr>,
+ ) -> ExprId {
+ match label {
+ Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
+ None => self.collect_block_opt(expr),
+ }
}
- fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
- self.collect_pat_(pat, &mut BindingList::default())
- }
+ // region: patterns
- fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
+ fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {
match pat {
- Some(pat) => self.collect_pat(pat),
+ Some(pat) => self.collect_pat(pat, &mut BindingList::default()),
None => self.missing_pat(),
}
}
- fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
+ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
let pattern = match &pat {
ast::Pat::IdentPat(bp) => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let annotation =
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
- let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
+ let subpat = bp.pat().map(|subpat| self.collect_pat(subpat, binding_list));
let is_simple_ident_pat =
annotation == BindingAnnotation::Unannotated && subpat.is_none();
@@ -1045,7 +1072,7 @@ impl ExprCollector<'_> {
Pat::TupleStruct { path, args, ellipsis }
}
ast::Pat::RefPat(p) => {
- let pat = self.collect_pat_opt_(p.pat(), binding_list);
+ let pat = self.collect_pat_opt(p.pat(), binding_list);
let mutability = Mutability::from_mutable(p.mut_token().is_some());
Pat::Ref { pat, mutability }
}
@@ -1055,10 +1082,10 @@ impl ExprCollector<'_> {
path.map(Pat::Path).unwrap_or(Pat::Missing)
}
ast::Pat::OrPat(p) => {
- let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
+ let pats = p.pats().map(|p| self.collect_pat(p, binding_list)).collect();
Pat::Or(pats)
}
- ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
+ ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
ast::Pat::TuplePat(p) => {
let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
Pat::Tuple { args, ellipsis }
@@ -1073,7 +1100,7 @@ impl ExprCollector<'_> {
.fields()
.filter_map(|f| {
let ast_pat = f.pat()?;
- let pat = self.collect_pat_(ast_pat, binding_list);
+ let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
Some(RecordFieldPat { name, pat })
})
@@ -1092,15 +1119,9 @@ impl ExprCollector<'_> {
// FIXME properly handle `RestPat`
Pat::Slice {
- prefix: prefix
- .into_iter()
- .map(|p| self.collect_pat_(p, binding_list))
- .collect(),
- slice: slice.map(|p| self.collect_pat_(p, binding_list)),
- suffix: suffix
- .into_iter()
- .map(|p| self.collect_pat_(p, binding_list))
- .collect(),
+ prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
+ slice: slice.map(|p| self.collect_pat(p, binding_list)),
+ suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
}
}
#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
@@ -1131,12 +1152,18 @@ impl ExprCollector<'_> {
Pat::Missing
}
ast::Pat::BoxPat(boxpat) => {
- let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
+ let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
Pat::Box { inner }
}
ast::Pat::ConstBlockPat(const_block_pat) => {
- if let Some(expr) = const_block_pat.block_expr() {
- let expr_id = self.collect_block(expr);
+ if let Some(block) = const_block_pat.block_expr() {
+ let expr_id = self.with_label_rib(RibKind::Constant, |this| {
+ let syntax_ptr = AstPtr::new(&block.clone().into());
+ this.collect_as_a_binding_owner_bad(
+ |this| this.collect_block(block),
+ syntax_ptr,
+ )
+ });
Pat::ConstBlock(expr_id)
} else {
Pat::Missing
@@ -1148,7 +1175,7 @@ impl ExprCollector<'_> {
let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
let pat =
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
- this.collect_pat_opt_(expanded_pat, binding_list)
+ this.collect_pat_opt(expanded_pat, binding_list)
});
self.source_map.pat_map.insert(src, pat);
return pat;
@@ -1162,9 +1189,9 @@ impl ExprCollector<'_> {
self.alloc_pat(pattern, Either::Left(ptr))
}
- fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
+ fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
match pat {
- Some(pat) => self.collect_pat_(pat, binding_list),
+ Some(pat) => self.collect_pat(pat, binding_list),
None => self.missing_pat(),
}
}
@@ -1180,12 +1207,14 @@ impl ExprCollector<'_> {
// We want to skip the `..` pattern here, since we account for it above.
let args = args
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
- .map(|p| self.collect_pat_(p, binding_list))
+ .map(|p| self.collect_pat(p, binding_list))
.collect();
(args, ellipsis)
}
+ // endregion: patterns
+
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
@@ -1213,42 +1242,118 @@ impl ExprCollector<'_> {
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
self.body.bindings[binding_id].definitions.push(pat_id);
}
-}
-impl From<ast::LiteralKind> for Literal {
- fn from(ast_lit_kind: ast::LiteralKind) -> Self {
- match ast_lit_kind {
- // FIXME: these should have actual values filled in, but unsure on perf impact
- LiteralKind::IntNumber(lit) => {
- if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
- Literal::Float(
- FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
- builtin,
- )
- } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
- Literal::Uint(lit.value().unwrap_or(0), builtin)
- } else {
- let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
- Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
+ // region: labels
+
+ fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
+ let label = Label {
+ name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
+ };
+ self.alloc_label(label, AstPtr::new(&ast_label))
+ }
+
+ fn resolve_label(
+ &self,
+ lifetime: Option<ast::Lifetime>,
+ ) -> Result<Option<LabelId>, BodyDiagnostic> {
+ let Some(lifetime) = lifetime else {
+ return Ok(None)
+ };
+ let name = Name::new_lifetime(&lifetime);
+
+ for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
+ if let Some((label_name, id)) = &rib.label {
+ if *label_name == name {
+ return if self.is_label_valid_from_rib(rib_idx) {
+ Ok(Some(*id))
+ } else {
+ Err(BodyDiagnostic::UnreachableLabel {
+ name,
+ node: InFile::new(
+ self.expander.current_file_id,
+ AstPtr::new(&lifetime),
+ ),
+ })
+ };
}
}
- LiteralKind::FloatNumber(lit) => {
- let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
- Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
- }
- LiteralKind::ByteString(bs) => {
- let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
- Literal::ByteString(text)
- }
- LiteralKind::String(s) => {
- let text = s.value().map(Box::from).unwrap_or_else(Default::default);
- Literal::String(text)
- }
- LiteralKind::Byte(b) => {
- Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
- }
- LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
- LiteralKind::Bool(val) => Literal::Bool(val),
}
+
+ Err(BodyDiagnostic::UndeclaredLabel {
+ name,
+ node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)),
+ })
+ }
+
+ fn is_label_valid_from_rib(&self, rib_index: usize) -> bool {
+ !self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
+ }
+
+ fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
+ self.label_ribs.push(LabelRib::new(kind));
+ let res = f(self);
+ self.label_ribs.pop();
+ res
+ }
+
+ fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
+ self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
+ let res = f(self);
+ self.label_ribs.pop();
+ res
+ }
+ // endregion: labels
+}
+
+impl ExprCollector<'_> {
+ fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.exprs.alloc(expr);
+ self.source_map.expr_map_back.insert(id, src.clone());
+ self.source_map.expr_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
+ self.body.exprs.alloc(expr)
+ }
+ fn missing_expr(&mut self) -> ExprId {
+ self.alloc_expr_desugared(Expr::Missing)
+ }
+
+ fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
+ self.body.bindings.alloc(Binding {
+ name,
+ mode,
+ definitions: SmallVec::new(),
+ owner: self.current_binding_owner,
+ })
+ }
+
+ fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.pats.alloc(pat);
+ self.source_map.pat_map_back.insert(id, src.clone());
+ self.source_map.pat_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
+ self.body.pats.alloc(pat)
+ }
+ fn missing_pat(&mut self) -> PatId {
+ self.body.pats.alloc(Pat::Missing)
+ }
+
+ fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.labels.alloc(label);
+ self.source_map.label_map_back.insert(id, src.clone());
+ self.source_map.label_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
+ self.body.labels.alloc(label)
}
}