Skip to content

Commit 574aadf

Browse files
committed
Simpler fixup of deferred definitions
1 parent 7d7d85c commit 574aadf

5 files changed

Lines changed: 175 additions & 228 deletions

File tree

crates/oak_index/src/builder.rs

Lines changed: 5 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use aether_syntax::AnyRValue;
44
use aether_syntax::RArgumentList;
55
use aether_syntax::RBinaryExpression;
66
use aether_syntax::RExpressionList;
7-
use aether_syntax::RForStatement;
87
use aether_syntax::RFunctionDefinition;
98
use aether_syntax::RParameter;
109
use aether_syntax::RParameters;
@@ -28,7 +27,6 @@ use crate::semantic_index::ScopeId;
2827
use crate::semantic_index::ScopeKind;
2928
use crate::semantic_index::SemanticIndex;
3029
use crate::semantic_index::SymbolFlags;
31-
use crate::semantic_index::SymbolId;
3230
use crate::semantic_index::SymbolTableBuilder;
3331
use crate::semantic_index::Use;
3432
use crate::semantic_index::UseId;
@@ -182,7 +180,7 @@ impl SemanticIndexBuilder {
182180

183181
let builder = self.use_def_builder_mut(target_scope);
184182
builder.ensure_symbol(target_symbol);
185-
builder.append_definition(target_symbol, target_def_id);
183+
builder.record_deferred_definition(target_symbol, target_def_id);
186184
}
187185

188186
// Walk up from the parent scope looking for a scope where `name` already
@@ -353,9 +351,8 @@ impl SemanticIndexBuilder {
353351

354352
if let Ok(body) = stmt.body() {
355353
let first_use = self.uses[self.current_scope].next_id();
356-
let loop_header = self.build_loop_header(body.syntax());
357354
self.collect_expression(&body);
358-
self.finish_loop_header(&loop_header, first_use);
355+
self.current_use_def.finish_loop_defs(&pre_loop, first_use);
359356
}
360357

361358
self.current_use_def.merge(pre_loop);
@@ -398,9 +395,8 @@ impl SemanticIndexBuilder {
398395

399396
if let Ok(body) = stmt.body() {
400397
let first_use = self.uses[self.current_scope].next_id();
401-
let loop_header = self.build_loop_header(body.syntax());
402398
self.collect_expression(&body);
403-
self.finish_loop_header(&loop_header, first_use);
399+
self.current_use_def.finish_loop_defs(&pre_loop, first_use);
404400
}
405401

406402
// Body may not execute
@@ -410,10 +406,10 @@ impl SemanticIndexBuilder {
410406
AnyRExpression::RRepeatStatement(stmt) => {
411407
// Body always executes at least once, no snapshot needed
412408
if let Ok(body) = stmt.body() {
409+
let pre_loop = self.current_use_def.snapshot();
413410
let first_use = self.uses[self.current_scope].next_id();
414-
let loop_header = self.build_loop_header(body.syntax());
415411
self.collect_expression(&body);
416-
self.finish_loop_header(&loop_header, first_use);
412+
self.current_use_def.finish_loop_defs(&pre_loop, first_use);
417413
}
418414
},
419415

@@ -553,87 +549,6 @@ impl SemanticIndexBuilder {
553549
}
554550
}
555551

556-
// Pre-walk a loop body to find all symbols that will be bound, then
557-
// create `LoopHeader` placeholder definitions for each. These are
558-
// recorded as additional (non-shadowing) bindings so that uses at the
559-
// top of the body can see definitions from a previous iteration.
560-
// After the body is visited, `finish_loop_header` resolves each
561-
// placeholder to the real definitions that are live at the end of
562-
// the body.
563-
//
564-
// Skips function bodies (different scope) and super-assignments (don't
565-
// affect local flow).
566-
fn build_loop_header(&mut self, body: &RSyntaxNode) -> Vec<(SymbolId, DefinitionId)> {
567-
let names = Self::collect_loop_bound_names(body);
568-
let mut loop_header = Vec::new();
569-
570-
for name in names {
571-
let symbol_id =
572-
self.symbol_tables[self.current_scope].intern(&name, SymbolFlags::IS_BOUND);
573-
let def_id = self.definitions[self.current_scope].push(Definition {
574-
symbol: symbol_id,
575-
kind: DefinitionKind::LoopHeader,
576-
range: body.text_trimmed_range(),
577-
});
578-
579-
self.current_use_def.ensure_symbol(symbol_id);
580-
self.current_use_def.append_definition(symbol_id, def_id);
581-
loop_header.push((symbol_id, def_id));
582-
}
583-
584-
loop_header
585-
}
586-
587-
fn finish_loop_header(&mut self, loop_header: &[(SymbolId, DefinitionId)], first_use: UseId) {
588-
for &(symbol_id, placeholder_id) in loop_header {
589-
self.current_use_def
590-
.resolve_placeholder(symbol_id, placeholder_id, first_use);
591-
}
592-
}
593-
594-
// Keep in sync with `collect_expression`: Every construct that creates
595-
// a definition there must be matched here so that loop headers account
596-
// for all bindings in the body.
597-
fn collect_loop_bound_names(body: &RSyntaxNode) -> Vec<String> {
598-
let mut names = Vec::new();
599-
let mut preorder = body.preorder();
600-
601-
while let Some(event) = preorder.next() {
602-
let WalkEvent::Enter(node) = event else {
603-
continue;
604-
};
605-
606-
match node.kind() {
607-
// Function bodies are separate scopes. In the future we'll need
608-
// an indirection here to handle other kinds of local scopes, in
609-
// particular from NSE functions like `local()`.
610-
RSyntaxKind::R_FUNCTION_DEFINITION => {
611-
preorder.skip_subtree();
612-
},
613-
614-
RSyntaxKind::R_BINARY_EXPRESSION => {
615-
let op: RBinaryExpression = node.cast().unwrap();
616-
if let Some((name, _)) = assignment_target(&op) {
617-
names.push(name);
618-
}
619-
},
620-
621-
RSyntaxKind::R_FOR_STATEMENT => {
622-
let for_stmt: RForStatement = node.cast().unwrap();
623-
if let Ok(variable) = for_stmt.variable() {
624-
names.push(identifier_text(&variable));
625-
}
626-
},
627-
628-
_ => {},
629-
}
630-
}
631-
632-
names.sort();
633-
names.dedup();
634-
names
635-
}
636-
637552
fn collect_arguments(&mut self, args: &RArgumentList) {
638553
for item in args.iter() {
639554
let Ok(arg) = item else { continue };
@@ -709,18 +624,6 @@ fn string_value_text(s: &aether_syntax::RStringValue) -> Option<String> {
709624
Some(text[1..text.len() - 1].to_string())
710625
}
711626

712-
/// For a local (non-super) assignment, extract the binding name and range.
713-
/// Returns `None` if the expression is not an assignment, is a
714-
/// super-assignment, or has a complex target (`x$foo`, `x[1]`, etc.).
715-
fn assignment_target(bin: &RBinaryExpression) -> Option<(String, TextRange)> {
716-
if !is_assignment(bin) || is_super_assignment(bin) {
717-
return None;
718-
}
719-
let right = is_right_assignment(bin);
720-
let target = if right { bin.right() } else { bin.left() }.ok()?;
721-
assignment_target_name(&target)
722-
}
723-
724627
/// Extract the binding name and range from an assignment target expression.
725628
/// Returns `None` for complex targets (`x$foo`, `x[1]`, etc.) that don't
726629
/// represent simple name bindings.

crates/oak_index/src/semantic_index.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -390,16 +390,6 @@ pub enum DefinitionKind {
390390
SuperAssignment(RSyntaxNode),
391391
Parameter(RSyntaxNode),
392392
ForVariable(RSyntaxNode),
393-
// The "loop header" is the entry point of a loop in the control flow
394-
// graph. This placeholder definition, created at the loop header before
395-
// visiting the body, represents a value carried from a previous
396-
// iteration so that uses at the top of the body can see definitions
397-
// from the bottom. After the body is visited, each placeholder is
398-
// resolved to the real definitions that are live at the end of the
399-
// body (see `finish_loop_header`). Resolved placeholders no longer
400-
// appear in use-def results but remain in the definition arena to
401-
// preserve `DefinitionId` stability (the arena is append-only).
402-
LoopHeader,
403393
}
404394

405395
impl Definition {

0 commit comments

Comments
 (0)