Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 72 additions & 35 deletions ndc_analyser/src/analyser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::name_resolution::{NameResolutionError, NameResolver};
use crate::scope::{CallKind, ResolvedCall, ScopeTree, TypeBinding, TypeDef};
use crate::symbols::SymbolId;
use itertools::{Itertools, izip};
use ndc_core::static_type::StaticTypeConstructionError;
use ndc_core::r#struct::StructRegistry;
Expand All @@ -8,7 +9,7 @@ use ndc_lexer::{NumericLiteral, Span};
use ndc_parser::{
AssignmentTarget, AssignmentTargetLocation, AugmentedAssignmentPlan, Binding, BindingPattern,
BindingPatternLocation, Candidate, Expression, ExpressionLocation, ForBody, ForIteration,
FunctionParameter, NodeId, TypeExpr,
FunctionParameter, NodeId, ResolvedVar, TypeExpr,
};
use std::cell::RefCell;
use std::collections::HashMap;
Expand Down Expand Up @@ -61,8 +62,18 @@ impl Analyser {
scope_tree: ScopeTree,
struct_registry: Rc<RefCell<StructRegistry>>,
) -> Self {
let resolver = NameResolver::new(scope_tree.native_names());
let mut scope_tree = scope_tree;
let natives = resolver.native_symbols();
debug_assert!(
scope_tree
.native_names()
.zip(&natives)
.all(|(name, &symbol)| resolver.symbol_name(symbol) == name)
);
scope_tree.attach_native_symbols(natives);
Self {
resolver: NameResolver::new(scope_tree.native_names()),
resolver,
scope_tree,
struct_registry,
return_type_stack: Vec::new(),
Expand Down Expand Up @@ -95,6 +106,17 @@ impl Analyser {
result
}

/// Binds `name` in the current scope as the resolver's `symbol` for it.
fn create_local_binding(
&mut self,
name: String,
binding: TypeBinding,
symbol: SymbolId,
) -> ResolvedVar {
debug_assert_eq!(self.resolver.symbol_name(symbol), name);
self.scope_tree.create_local_binding(name, binding, symbol)
}

/// Record a non-fatal analysis error. The analyser continues with a
/// fallback type (usually `Any`) so that subsequent code is still checked.
fn emit(&mut self, err: AnalysisError) {
Expand Down Expand Up @@ -475,32 +497,34 @@ impl Analyser {

// Pre-register the function before analyzing its body so recursive calls can
// resolve the name. The return type is unknown at this point so we use Any.
let pre_slot =
if let Some(name) = name {
let arity = type_signature.types().map(|t| t.len());
if self.scope_tree.has_function_in_current_scope(name, arity) {
self.emit(AnalysisError::function_redefinition(name, arity, *span));
// Skip re-registering but still analyse the body below.
None
} else {
let placeholder = StaticType::Function {
parameters: type_signature.types(),
return_type: Box::new(
return_type_slot.clone().unwrap_or(StaticType::Any),
),
};
Some(self.scope_tree.create_local_binding(
name.clone(),
TypeBinding::Inferred(placeholder),
))
}
} else {
let pre_slot = if let Some(name) = name {
let arity = type_signature.types().map(|t| t.len());
if self.scope_tree.has_function_in_current_scope(name, arity) {
self.emit(AnalysisError::function_redefinition(name, arity, *span));
// Skip re-registering but still analyse the body below.
None
};
} else {
let placeholder = StaticType::Function {
parameters: type_signature.types(),
return_type: Box::new(
return_type_slot.clone().unwrap_or(StaticType::Any),
),
};
Some(self.create_local_binding(
name.clone(),
TypeBinding::Inferred(placeholder),
self.resolver.declared_symbol(*id),
))
}
} else {
None
};

self.scope_tree.new_function_scope();
self.return_type_stack.push(None);
let param_types = self.resolve_parameters_declarative(&type_signature, *span);
let parameter_nodes = parameters.iter().map(|p| p.lvalue.id).collect_vec();
let param_types =
self.resolve_parameters_declarative(&type_signature, &parameter_nodes, *span);

// Fill inferred_type on parameter binding patterns for LSP hints.
for (p, typ) in parameters.iter_mut().zip(&param_types) {
Expand Down Expand Up @@ -735,20 +759,26 @@ impl Analyser {
self.scope_tree
.declare_type(name.clone(), TypeDef::Struct(Rc::clone(&info)));

*resolved_name = Some(self.scope_tree.create_local_binding(
let symbols = self.resolver.struct_symbols(*id).clone();
*resolved_name = Some(self.create_local_binding(
name.clone(),
TypeBinding::Annotated(info.constructor_type()),
symbols.constructor,
));

for (index, field) in fields.iter_mut().enumerate() {
field.resolved_getter = Some(self.scope_tree.create_local_binding(
for (index, (field, &(getter, setter))) in
fields.iter_mut().zip(&symbols.accessors).enumerate()
{
field.resolved_getter = Some(self.create_local_binding(
field.identifier.clone(),
TypeBinding::Annotated(info.getter_type(index)),
getter,
));

field.resolved_setter = Some(self.scope_tree.create_local_binding(
field.resolved_setter = Some(self.create_local_binding(
format!("{}=", field.identifier),
TypeBinding::Annotated(info.setter_type(index)),
setter,
));
}

Expand Down Expand Up @@ -1146,6 +1176,7 @@ impl Analyser {
fn resolve_parameters_declarative(
&mut self,
type_signature: &TypeSignature,
parameter_nodes: &[NodeId],
span: Span,
) -> Vec<StaticType> {
let TypeSignature::Exact(parameters) = type_signature else {
Expand All @@ -1156,16 +1187,20 @@ impl Analyser {
self.emit(AnalysisError::parameter_redefined(&param.name, span));
}

for param in parameters.iter().unique_by(|param| &param.name) {
for (param, &node) in parameters
.iter()
.zip(parameter_nodes)
.unique_by(|(param, _)| &param.name)
{
let has_annotation = param.type_name != StaticType::Any;
let binding = if has_annotation {
TypeBinding::Annotated(param.type_name.clone())
} else {
TypeBinding::Inferred(StaticType::Any)
};

self.scope_tree
.create_local_binding(param.name.clone(), binding);
let symbol = self.resolver.declared_symbol(node);
self.create_local_binding(param.name.clone(), binding, symbol);
}

parameters
Expand Down Expand Up @@ -1203,10 +1238,12 @@ impl Analyser {
None => TypeBinding::Inferred(found_type),
};

*resolved = Some(
self.scope_tree
.create_local_binding(identifier.clone(), type_binding.clone()),
);
let symbol = self.resolver.declared_symbol(lvalue.id);
*resolved = Some(self.create_local_binding(
identifier.clone(),
type_binding.clone(),
symbol,
));

*inferred_type = Some(type_binding.typ().clone())
}
Expand Down
33 changes: 24 additions & 9 deletions ndc_analyser/src/name_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,6 @@ pub(crate) struct NameResolver {
structs: AHashMap<NodeId, StructSymbols>,
/// Each native's position in the function registry, which is also its
/// global slot in the VM. With `new(["len", "+"])`, `len` is 0 and `+` is 1.
#[cfg_attr(
not(test),
expect(
dead_code,
reason = "read by the layout pass, which is not written yet"
)
)]
native_slots: AHashMap<SymbolId, usize>,
/// Errors from statements that failed to resolve. Resolution carries on with
/// the next statement, so `a; b;` reports both unknown names.
Expand Down Expand Up @@ -147,9 +140,9 @@ pub(crate) struct StructSymbols {
/// `Point` in annotations such as `let p: Point`.
type_symbol: SymbolId,
/// `Point` in calls such as `Point(1)`.
constructor: SymbolId,
pub(crate) constructor: SymbolId,
/// The getter and setter of each field, in declaration order.
accessors: Vec<(SymbolId, SymbolId)>,
pub(crate) accessors: Vec<(SymbolId, SymbolId)>,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -365,6 +358,28 @@ impl NameResolver {
}
}

/// The native symbols in registry order: the first is the native in slot 0.
pub(crate) fn native_symbols(&self) -> Vec<SymbolId> {
let mut natives = self.native_slots.iter().collect::<Vec<_>>();
natives.sort_by_key(|&(_, &slot)| slot);
natives.into_iter().map(|(&symbol, _)| symbol).collect()
}

/// The symbol a resolved declaration introduced: a binding pattern identifier's
/// or a named function's, keyed by its node.
pub(crate) fn declared_symbol(&self, node: NodeId) -> SymbolId {
self.declarations[&node]
}

pub(crate) fn symbol_name(&self, symbol: SymbolId) -> &str {
self.symbols.name(symbol)
}

/// What the struct declared at `node` bound.
pub(crate) fn struct_symbols(&self, node: NodeId) -> &StructSymbols {
&self.structs[&node]
}

/// The errors recorded so far, leaving none behind.
pub(crate) fn take_errors(&mut self) -> Vec<NameResolutionError> {
std::mem::take(&mut self.errors)
Expand Down
Loading
Loading