pr 05.f
This commit is contained in:
parent
acd74d57ff
commit
8c02f505d0
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user