This commit is contained in:
bQUARKz 2026-02-04 21:16:21 +00:00
parent acd74d57ff
commit 8c02f505d0
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
3 changed files with 317 additions and 29 deletions

View File

@ -245,7 +245,13 @@ pub fn compile_project(
for (module_path, parsed) in &parsed_files {
let ms = module_symbols_map.get(module_path).unwrap();
// Ensure primitive names are interned before creating resolver/bootstrap
{
let primitives = ["int", "bool", "float", "string", "bounded", "void"];
for p in primitives { interner.intern(p); }
}
let mut resolver = Resolver::new(ms, &module_provider, &interner);
resolver.bootstrap_types(&interner);
resolver.resolve(&parsed.arena, parsed.root)?;
// Capture imported symbols

View File

@ -60,8 +60,14 @@ impl Frontend for PbsFrontend {
let mut resolver = Resolver::new(&module_symbols, &EmptyProvider, &interner);
// Step 3: Resolve and type expressions
let mut interner_mut = file_manager.interner_mut();
resolver.bootstrap_types(&mut interner_mut);
// Ensure primitives are interned in the shared FileManager interner
{
let inter = file_manager.interner_mut();
let primitives = ["int", "bool", "float", "string", "bounded", "void"];
for p in primitives { inter.intern(p); }
}
let inter_ro = file_manager.interner();
resolver.bootstrap_types(inter_ro);
resolver.resolve(&parsed.arena, parsed.root)?;
let imported_symbols = resolver.imported_symbols;

View File

@ -46,10 +46,10 @@ impl<'a> Resolver<'a> {
}
}
pub fn bootstrap_types(&mut self, interner: &mut NameInterner) {
pub fn bootstrap_types(&mut self, interner: &NameInterner) {
let primitive_names = ["int", "bool", "float", "string", "bounded", "void"];
for name in primitive_names {
let name_id = interner.intern(name);
let name_id = interner.get(name).expect("primitive name not interned");
let type_id = self.type_arena.intern_type(TypeKind::Primitive { name: name_id });
self.primitives.insert(name.to_string(), type_id);
}
@ -66,6 +66,40 @@ impl<'a> Resolver<'a> {
}
};
// Step 0: Populate symbol_arena with top-level symbols for global lookup
for (name, sym) in &self.current_module.type_symbols.symbols {
self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name,
kind: match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
},
exported: sym.visibility == Visibility::Pub,
module: 0,
decl_span: sym.span,
});
}
for (name, sym) in &self.current_module.value_symbols.symbols {
self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name,
kind: match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
},
exported: sym.visibility == Visibility::Pub,
module: 0,
decl_span: sym.span,
});
}
// Step 1: Process imports to populate imported_symbols
for imp in &file.imports {
if let NodeKind::Import(imp_node) = arena.kind(*imp) {
@ -73,6 +107,40 @@ impl<'a> Resolver<'a> {
}
}
// Add imported symbols to symbol_arena too
for (name, sym) in &self.imported_symbols.type_symbols.symbols {
self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name,
kind: match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
},
exported: sym.visibility == Visibility::Pub,
module: 0, // Should be target module
decl_span: sym.span,
});
}
for (name, sym) in &self.imported_symbols.value_symbols.symbols {
self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name,
kind: match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
},
exported: sym.visibility == Visibility::Pub,
module: 0, // Should be target module
decl_span: sym.span,
});
}
// Step 2: Resolve all top-level declarations
for decl in &file.decls {
self.resolve_node(arena, *decl);
@ -260,14 +328,8 @@ impl<'a> Resolver<'a> {
_ => {}
}
}
NodeKind::TypeName(n) => {
self.resolve_identifier(n.name, arena.span(node), Namespace::Type);
}
NodeKind::TypeApp(n) => {
self.resolve_identifier(n.base, arena.span(node), Namespace::Type);
for arg in &n.args {
self.resolve_type_ref(arena, *arg);
}
NodeKind::TypeName(_) | NodeKind::TypeApp(_) => {
self.resolve_type_ref(arena, node);
}
NodeKind::ConstructorDecl(n) => self.resolve_constructor_decl(arena, node, n),
NodeKind::ConstantDecl(n) => self.resolve_node(arena, n.value),
@ -469,20 +531,134 @@ impl<'a> Resolver<'a> {
self.resolve_node(arena, n.init);
let sym_id = self.define_local(n.name, arena.span(id), SymbolKind::Local);
if let (Some(tid), Some(sid)) = (ty_id, sym_id) {
self.type_facts.set_symbol_type(sid, tid);
if let Some(sid) = sym_id {
self.node_to_symbol.bind_node(id, sid);
if let Some(tid) = ty_id {
self.type_facts.set_symbol_type(sid, tid);
}
}
}
fn resolve_type_ref(&mut self, arena: &AstArena, node: NodeId) -> Option<TypeId> {
self.resolve_node(arena, node);
let type_id = self.lower_type_node(arena, node);
if let Some(tid) = type_id {
self.type_facts.set_node_type(node, tid);
}
type_id
}
fn lower_type_node(&mut self, arena: &AstArena, node: NodeId) -> Option<TypeId> {
match arena.kind(node) {
NodeKind::TypeName(n) => {
let name = self.interner.resolve(n.name);
self.primitives.get(name).copied()
if let Some(prim) = self.primitives.get(name) {
return Some(*prim);
}
// Resolve struct/type symbol
if let Some((_sym, sym_id)) = self.lookup_with_id(n.name, Namespace::Type) {
// Try to find if this symbol is a type symbol that can be converted to TypeId
// For now, we only have TypeKind::Struct
if let Some(sid) = sym_id {
let kind = TypeKind::Struct { sym: sid };
return Some(self.type_arena.intern_type(kind));
} else {
// sym_id is None, but _sym exists. This means it's a built-in or something not in analysis arena yet.
// But for Struct resolution we need a SymbolId.
// If it's a built-in it should have been handled by self.primitives.
}
}
// If not found in current or imports, maybe it's a symbol from another module not yet in SymbolArena?
// Actually, lookup_with_id checks imported_symbols too.
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_UNKNOWN_TYPE".to_string()),
message: format!("Unknown type: {}", name),
span: Some(arena.span(node)),
});
None
}
NodeKind::TypeApp(n) => {
let base_name = self.interner.resolve(n.base);
match base_name {
"optional" => {
if n.args.len() != 1 {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_INVALID_ARGS".to_string()),
message: "optional<T> expects exactly 1 argument".to_string(),
span: Some(arena.span(node)),
});
return None;
}
let inner = self.lower_type_node(arena, n.args[0])?;
Some(self.type_arena.intern_type(TypeKind::Optional { inner }))
}
"result" => {
if n.args.len() != 2 {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_INVALID_ARGS".to_string()),
message: "result<T, E> expects exactly 2 arguments".to_string(),
span: Some(arena.span(node)),
});
return None;
}
let ok = self.lower_type_node(arena, n.args[0])?;
let err = self.lower_type_node(arena, n.args[1])?;
Some(self.type_arena.intern_type(TypeKind::Result { ok, err }))
}
"array" => {
if n.args.len() < 1 || n.args.len() > 2 {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_INVALID_ARGS".to_string()),
message: "array<T> or array<T>[N] expects 1 or 2 arguments".to_string(),
span: Some(arena.span(node)),
});
return None;
}
let inner = self.lower_type_node(arena, n.args[0])?;
let len = if n.args.len() == 2 {
match arena.kind(n.args[1]) {
NodeKind::IntLit(lit) => Some(lit.value as u32),
_ => {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_INVALID_ARGS".to_string()),
message: "Array length must be an integer literal".to_string(),
span: Some(arena.span(n.args[1])),
});
None
}
}
} else {
None
};
Some(self.type_arena.intern_type(TypeKind::Array { inner, len }))
}
_ => {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_UNKNOWN_TYPE".to_string()),
message: format!("Unknown generic type: {}", base_name),
span: Some(arena.span(node)),
});
None
}
}
}
_ => {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_NOT_A_TYPE".to_string()),
message: "Expected a type node".to_string(),
span: Some(arena.span(node)),
});
None
}
_ => None,
}
}
@ -545,7 +721,19 @@ impl<'a> Resolver<'a> {
// 2 & 3. file-private and module symbols
if let Some(sym) = table.get(name) {
return Some((sym.clone(), None));
// Se encontrarmos no módulo atual, tentamos achar o SymbolId correspondente na symbol_arena
let sym_id = self.symbol_arena.symbols.iter().enumerate().find(|(_, s)| {
s.name == sym.name && s.kind == match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
}
}).map(|(i, _)| SymbolId(i as u32));
return Some((sym.clone(), sym_id));
}
// 4. imported symbols
@ -555,7 +743,18 @@ impl<'a> Resolver<'a> {
&self.imported_symbols.value_symbols
};
if let Some(sym) = imp_table.get(name) {
return Some((sym.clone(), None));
let sym_id = self.symbol_arena.symbols.iter().enumerate().find(|(_, s)| {
s.name == sym.name && s.kind == match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
}
}).map(|(i, _)| SymbolId(i as u32));
return Some((sym.clone(), sym_id));
}
// 5. Fallback for constructor calls: check Type namespace if looking for a Value
@ -896,7 +1095,7 @@ mod tests {
let c = -1;
}
";
let (parsed, _, interner) = setup_test(source);
let (parsed, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -906,9 +1105,11 @@ mod tests {
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
}
let mut interner_for_bootstrap = interner.clone();
// Ensure primitives are interned for bootstrap
for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); }
let interner_for_bootstrap = interner.clone();
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
resolver.bootstrap_types(&mut interner_for_bootstrap);
resolver.bootstrap_types(&interner);
resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed");
// Verify types in TypeFacts using format_type for easier assertion
@ -955,7 +1156,7 @@ mod tests {
let y = x;
}
";
let (parsed, _, interner) = setup_test(source);
let (parsed, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -965,9 +1166,10 @@ mod tests {
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
}
let mut interner_for_bootstrap = interner.clone();
for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); }
let interner_for_bootstrap = interner.clone();
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
resolver.bootstrap_types(&mut interner_for_bootstrap);
resolver.bootstrap_types(&interner);
resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed");
use crate::analysis::types::format_type;
@ -1002,7 +1204,7 @@ mod tests {
let b = a;
}
";
let (parsed, _, interner) = setup_test(source);
let (parsed, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -1012,9 +1214,10 @@ mod tests {
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
}
let mut interner_for_bootstrap = interner.clone();
for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); }
let interner_for_bootstrap = interner.clone();
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
resolver.bootstrap_types(&mut interner_for_bootstrap);
resolver.bootstrap_types(&interner);
resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed");
use crate::analysis::types::format_type;
@ -1041,4 +1244,77 @@ mod tests {
assert!(found_a_ref, "Reference to a in let b = a not found or not typed");
}
#[test]
fn test_lower_type_node_complex() {
let source = "
declare struct MyType {}
fn main() {
let x: optional<int> = 1;
let y: result<int, string> = 2;
let z: array<float>[10] = 3;
let w: MyType = 4;
}
";
let (parsed, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
struct EmptyProvider;
impl ModuleProvider for EmptyProvider {
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
}
for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); }
let interner_for_bootstrap = interner.clone();
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
resolver.bootstrap_types(&interner);
resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed");
use crate::analysis::types::format_type;
let mut found_x = false;
let mut found_y = false;
let mut found_z = false;
let mut found_w = false;
for i in 0..parsed.arena.nodes.len() {
let id = NodeId(i as u32);
let kind = parsed.arena.kind(id);
if let NodeKind::LetStmt(n) = kind {
let name = interner.resolve(n.name);
let sym_id = resolver.node_to_symbol.get(id).expect(&format!("Node {:?} should be bound to a symbol", kind));
let ty_id = resolver.type_facts.get_symbol_type(sym_id).expect("Symbol should have type");
match name {
"x" => {
assert_eq!(format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None), "optional<int>");
found_x = true;
}
"y" => {
assert_eq!(format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None), "result<int, string>");
found_y = true;
}
"z" => {
assert_eq!(format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None), "array<float>[10]");
found_z = true;
}
"w" => {
// MyType is a struct. Since we don't pass symbols to format_type in this test, it should be struct#ID
let formatted = format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None);
assert!(formatted.starts_with("struct#"), "Formatted type should be struct#ID, got {}", formatted);
found_w = true;
}
_ => {}
}
}
}
assert!(found_x);
assert!(found_y);
assert!(found_z);
assert!(found_w);
}
}