intermediary state, safe point

This commit is contained in:
bQUARKz 2026-02-10 15:15:42 +00:00
parent c80260e780
commit f5d259ba2a
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
14 changed files with 702 additions and 480 deletions

View File

@ -1,6 +1,5 @@
use crate::analysis::symbols::{SymbolArena}; use crate::analysis::symbols::{SymbolArena};
use prometeu_analysis::{NameId, NameInterner, TypeId, SymbolId, NodeId, ModuleId}; use prometeu_analysis::{NameId, NameInterner, TypeId, SymbolId, NodeId};
use crate::common::spans::FileId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// Use canonical TypeId from prometeu-analysis // Use canonical TypeId from prometeu-analysis
@ -112,6 +111,7 @@ impl TypeFacts {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use prometeu_analysis::{FileId, ModuleId};
use super::*; use super::*;
// Mock NameId and SymbolId for simplified tests if necessary, // Mock NameId and SymbolId for simplified tests if necessary,
// or use real values. // or use real values.

View File

@ -1,11 +1,11 @@
use crate::building::linker::{LinkError, Linker}; use crate::building::linker::{LinkError, Linker};
use crate::building::output::{compile_project, CompileError}; use crate::building::output::{compile_project, CompileError};
use crate::building::plan::{BuildPlan, BuildTarget}; use crate::building::plan::{BuildPlan, BuildTarget};
use crate::common::files::FileManager;
use crate::common::diagnostics::DiagnosticBundle; use crate::common::diagnostics::DiagnosticBundle;
use crate::common::files::FileManager;
use crate::deps::resolver::ResolvedGraph; use crate::deps::resolver::ResolvedGraph;
use std::collections::HashMap;
use prometeu_abi::virtual_machine::ProgramImage; use prometeu_abi::virtual_machine::ProgramImage;
use std::collections::HashMap;
#[derive(Debug)] #[derive(Debug)]
pub enum BuildError { pub enum BuildError {
@ -150,11 +150,10 @@ pub fn build_from_graph(graph: &ResolvedGraph, target: BuildTarget) -> Result<Bu
mod tests { mod tests {
use super::*; use super::*;
use crate::deps::resolver::{ProjectKey, ResolvedGraph, ResolvedNode}; use crate::deps::resolver::{ProjectKey, ResolvedGraph, ResolvedNode};
use crate::sources::discover;
use crate::manifest::{Manifest, ManifestKind}; use crate::manifest::{Manifest, ManifestKind};
use std::collections::BTreeMap; use crate::sources::discover;
use prometeu_analysis::ids::ProjectId; use prometeu_analysis::ids::ProjectId;
use std::collections::HashMap; use std::collections::BTreeMap;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use tempfile::tempdir; use tempfile::tempdir;

View File

@ -4,21 +4,20 @@ use crate::common::diagnostics::DiagnosticBundle;
use crate::common::files::FileManager; use crate::common::files::FileManager;
use crate::common::spans::{FileId, Span}; use crate::common::spans::{FileId, Span};
use crate::deps::resolver::ProjectKey; use crate::deps::resolver::ProjectKey;
use prometeu_analysis::ids::ProjectId;
use crate::frontends::pbs::ast::ParsedAst; use crate::frontends::pbs::ast::ParsedAst;
use crate::frontends::pbs::collector::SymbolCollector;
use crate::frontends::pbs::lowering::Lowerer; use crate::frontends::pbs::lowering::Lowerer;
use crate::frontends::pbs::parser::Parser;
use crate::frontends::pbs::resolver::{ModuleProvider, Resolver}; use crate::frontends::pbs::resolver::{ModuleProvider, Resolver};
use crate::frontends::pbs::symbols::{ModuleSymbols, Namespace, Symbol, SymbolKind, Visibility}; use crate::frontends::pbs::symbols::{ModuleSymbols, Namespace, Symbol, SymbolKind, Visibility};
use crate::frontends::pbs::typecheck::TypeChecker;
use crate::frontends::pbs::types::PbsType; use crate::frontends::pbs::types::PbsType;
use crate::frontends::pbs::{build_typed_module_symbols, SymbolCollector};
use crate::lowering::core_to_vm; use crate::lowering::core_to_vm;
use crate::semantics::export_surface::ExportSurfaceKind; use crate::semantics::export_surface::ExportSurfaceKind;
use prometeu_bytecode::{ConstantPoolEntry, DebugInfo, FunctionMeta}; use prometeu_analysis::ids::ProjectId;
use prometeu_analysis::NameInterner; use prometeu_analysis::NameInterner;
use prometeu_bytecode::{ConstantPoolEntry, DebugInfo, FunctionMeta};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use crate::frontends::pbs::parser::Parser;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct ExportKey { pub struct ExportKey {
@ -63,7 +62,7 @@ pub struct CompiledModule {
#[derive(Debug)] #[derive(Debug)]
pub enum CompileError { pub enum CompileError {
Frontend(crate::common::diagnostics::DiagnosticBundle), Frontend(DiagnosticBundle),
DuplicateExport { DuplicateExport {
symbol: String, symbol: String,
first_dep: String, first_dep: String,
@ -77,9 +76,15 @@ impl std::fmt::Display for CompileError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
CompileError::Frontend(d) => write!(f, "Frontend error: {:?}", d), CompileError::Frontend(d) => write!(f, "Frontend error: {:?}", d),
CompileError::DuplicateExport { symbol, first_dep, second_dep } => { CompileError::DuplicateExport {
write!(f, "duplicate export: symbol `{}`\n first defined in dependency `{}`\n again defined in dependency `{}`", symbol, first_dep, second_dep) symbol,
} first_dep,
second_dep,
} => write!(
f,
"duplicate export: symbol `{}`\n first defined in dependency `{}`\n again defined in dependency `{}`",
symbol, first_dep, second_dep
),
CompileError::Io(e) => write!(f, "IO error: {}", e), CompileError::Io(e) => write!(f, "IO error: {}", e),
CompileError::Internal(s) => write!(f, "Internal error: {}", s), CompileError::Internal(s) => write!(f, "Internal error: {}", s),
} }
@ -116,9 +121,10 @@ pub fn compile_project(
file_manager: &mut FileManager, file_manager: &mut FileManager,
) -> Result<CompiledModule, CompileError> { ) -> Result<CompiledModule, CompileError> {
let mut interner = NameInterner::new(); let mut interner = NameInterner::new();
// 1. Parse all files and group symbols by module
// 1) Parse all files; collect+merge symbols by module_path
let mut module_symbols_map: HashMap<String, ModuleSymbols> = HashMap::new(); let mut module_symbols_map: HashMap<String, ModuleSymbols> = HashMap::new();
let mut parsed_files: Vec<(String, ParsedAst)> = Vec::new(); // (module_path, ast) let mut parsed_files: Vec<(String, ParsedAst)> = Vec::new();
for source_rel in &step.sources { for source_rel in &step.sources {
let source_abs = step.project_dir.join(source_rel); let source_abs = step.project_dir.join(source_rel);
@ -145,10 +151,13 @@ pub fn compile_project(
.map(|p| p.to_string_lossy().replace('\\', "/")) .map(|p| p.to_string_lossy().replace('\\', "/"))
.unwrap_or_else(|| "".to_string()); .unwrap_or_else(|| "".to_string());
let ms = module_symbols_map.entry(module_path.clone()).or_insert_with(ModuleSymbols::new); let ms = module_symbols_map
.entry(module_path.clone())
.or_insert_with(ModuleSymbols::new);
// Merge symbols // Merge Type symbols (no overload): reject duplicates
for sym in ts.symbols.into_values() { for list in ts.symbols.into_values() {
for sym in list {
if let Some(existing) = ms.type_symbols.get(sym.name) { if let Some(existing) = ms.type_symbols.get(sym.name) {
return Err(DiagnosticBundle::error( return Err(DiagnosticBundle::error(
"E_RESOLVE_DUPLICATE_SYMBOL", "E_RESOLVE_DUPLICATE_SYMBOL",
@ -158,12 +167,18 @@ pub fn compile_project(
module_path module_path
), ),
existing.span.clone(), existing.span.clone(),
).into()); )
.into());
} }
let _ = ms.type_symbols.insert(sym); let _ = ms.type_symbols.insert(sym);
} }
for sym in vs.symbols.into_values() { }
// Merge Value symbols: allow overloads only for Function kind
for list in vs.symbols.into_values() {
for sym in list {
if let Some(existing) = ms.value_symbols.get(sym.name) { if let Some(existing) = ms.value_symbols.get(sym.name) {
if !(existing.kind == SymbolKind::Function && sym.kind == SymbolKind::Function) {
return Err(DiagnosticBundle::error( return Err(DiagnosticBundle::error(
"E_RESOLVE_DUPLICATE_SYMBOL", "E_RESOLVE_DUPLICATE_SYMBOL",
format!( format!(
@ -172,40 +187,48 @@ pub fn compile_project(
module_path module_path
), ),
existing.span.clone(), existing.span.clone(),
).into()); )
.into());
}
} }
let _ = ms.value_symbols.insert(sym); let _ = ms.value_symbols.insert(sym);
} }
}
parsed_files.push((module_path, parsed)); parsed_files.push((module_path, parsed));
} }
// 2. Synthesize ModuleSymbols for dependencies // 2) Synthesize visible ModuleSymbols for dependencies (under synthetic module paths)
let mut all_visible_modules = module_symbols_map.clone(); let mut all_visible_modules = module_symbols_map.clone();
for (alias, project_id) in &step.deps { for (alias, project_id) in &step.deps {
if let Some(compiled) = dep_modules.get(project_id) { let Some(compiled) = dep_modules.get(project_id) else { continue };
for (key, meta) in &compiled.exports { for (key, meta) in &compiled.exports {
// Support syntax: "alias/module" and "@alias:module"
let key_module_path = &key.module_path; let key_module_path = &key.module_path;
// Support both import styles:
// - "alias/module"
// - "@alias:module"
let synthetic_paths = [ let synthetic_paths = [
format!("{}/{}", alias, key_module_path), format!("{}/{}", alias, key_module_path),
format!("@{}:{}", alias, key_module_path), format!("@{}:{}", alias, key_module_path),
]; ];
for synthetic_module_path in synthetic_paths { for synthetic_module_path in synthetic_paths {
let ms = all_visible_modules.entry(synthetic_module_path.clone()).or_insert_with(ModuleSymbols::new); let ms = all_visible_modules
.entry(synthetic_module_path.clone())
.or_insert_with(ModuleSymbols::new);
let sym = Symbol { let sym = Symbol {
name: interner.intern(&key.symbol_name), name: interner.intern(&key.symbol_name),
kind: match key.kind { kind: match key.kind {
ExportSurfaceKind::Service => SymbolKind::Service, ExportSurfaceKind::Service => SymbolKind::Service,
ExportSurfaceKind::DeclareType => { ExportSurfaceKind::DeclareType => match &meta.ty {
match &meta.ty {
Some(PbsType::Contract(_)) => SymbolKind::Contract, Some(PbsType::Contract(_)) => SymbolKind::Contract,
Some(PbsType::ErrorType(_)) => SymbolKind::ErrorType, Some(PbsType::ErrorType(_)) => SymbolKind::ErrorType,
_ => SymbolKind::Struct, _ => SymbolKind::Struct,
} },
}
ExportSurfaceKind::Function => SymbolKind::Function, ExportSurfaceKind::Function => SymbolKind::Function,
}, },
namespace: key.kind.namespace(), namespace: key.kind.namespace(),
@ -221,106 +244,113 @@ pub fn compile_project(
return Err(CompileError::DuplicateExport { return Err(CompileError::DuplicateExport {
symbol: interner.resolve(sym.name).to_string(), symbol: interner.resolve(sym.name).to_string(),
first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()), first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()),
second_dep: sym.origin.unwrap_or_else(|| "unknown".to_string()), second_dep: sym.origin.clone().unwrap_or_else(|| "unknown".to_string()),
}); });
} }
let _ = ms.type_symbols.insert(sym.clone()); let _ = ms.type_symbols.insert(sym);
} else { } else {
if let Some(existing) = ms.value_symbols.get(sym.name) { if let Some(existing) = ms.value_symbols.get(sym.name) {
if !(existing.kind == SymbolKind::Function && sym.kind == SymbolKind::Function) {
return Err(CompileError::DuplicateExport { return Err(CompileError::DuplicateExport {
symbol: interner.resolve(sym.name).to_string(), symbol: interner.resolve(sym.name).to_string(),
first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()), first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()),
second_dep: sym.origin.unwrap_or_else(|| "unknown".to_string()), second_dep: sym.origin.clone().unwrap_or_else(|| "unknown".to_string()),
}); });
} }
let _ = ms.value_symbols.insert(sym.clone());
} }
let _ = ms.value_symbols.insert(sym);
} }
} }
} }
} }
// 3. Resolve and TypeCheck each file // 3) Resolve imports and type each file; keep imported_symbols per module_path for Lowerer
let module_provider = ProjectModuleProvider { let module_provider = ProjectModuleProvider {
modules: all_visible_modules, modules: all_visible_modules,
}; };
// We need to collect imported symbols for Lowerer let mut file_imported_symbols: HashMap<String, ModuleSymbols> = HashMap::new();
let mut file_imported_symbols: HashMap<String, ModuleSymbols> = HashMap::new(); // keyed by module_path
// Ensure primitive names are interned early (resolver/typecheck may depend on NameIds)
{
let primitives = ["int", "bool", "float", "string", "bounded", "void", "error", "result"];
for p in primitives {
interner.intern(p);
}
}
for (module_path, parsed) in &parsed_files { for (module_path, parsed) in &parsed_files {
let ms = module_symbols_map.get(module_path).unwrap(); let ms = module_symbols_map
// Ensure primitive names are interned before creating resolver/bootstrap .get(module_path)
{ .ok_or_else(|| CompileError::Internal(format!("Missing module_symbols for '{}'", module_path)))?;
let primitives = ["int", "bool", "float", "string", "bounded", "void"];
for p in primitives { interner.intern(p); }
}
let mut resolver = Resolver::new(ms, &module_provider, &interner); let mut resolver = Resolver::new(ms, &module_provider, &interner);
resolver.bootstrap_types(&interner); resolver.bootstrap_types(&interner);
resolver.resolve(&parsed.arena, parsed.root)?; resolver.resolve(&parsed.arena, parsed.root)?;
// Capture imported symbols
file_imported_symbols.insert(module_path.clone(), resolver.imported_symbols.clone()); file_imported_symbols.insert(module_path.clone(), resolver.imported_symbols.clone());
// TypeChecker also needs &mut ModuleSymbols // Unified typed-symbol builder: typecheck the already-collected module symbols in place
let mut ms_mut = module_symbols_map.get_mut(module_path).unwrap(); let imported = file_imported_symbols
let imported = file_imported_symbols.get(module_path).unwrap(); .get(module_path)
let mut typechecker = TypeChecker::new(&mut ms_mut, imported, &module_provider, &interner); .ok_or_else(|| CompileError::Internal(format!("Missing imported_symbols for '{}'", module_path)))?;
typechecker.check(&parsed.arena, parsed.root)?;
let ms_mut = module_symbols_map
.get_mut(module_path)
.ok_or_else(|| CompileError::Internal(format!("Missing module_symbols (mut) for '{}'", module_path)))?;
build_typed_module_symbols(parsed, ms_mut, imported, &mut interner)?;
} }
// 4. Lower ALL modules to VM and emit a single combined bytecode image for this project // 4) Lower ALL sources into a single combined VM module (so exports func_idx match final image)
// Rationale: services and functions can live in multiple modules; exports must refer to
// correct function indices within this CompiledModule. We aggregate all VM functions
// into a single ir_vm::Module and assemble once using the public API `emit_fragments`.
// Combined VM module (we will merge const pools and remap ConstIds accordingly)
let mut combined_vm = crate::ir_vm::Module::new(step.project_key.name.clone()); let mut combined_vm = crate::ir_vm::Module::new(step.project_key.name.clone());
combined_vm.const_pool = crate::ir_core::ConstPool::new(); combined_vm.const_pool = crate::ir_core::ConstPool::new();
// Track origin module_path for each function we append to combined_vm
// Origin module_path per appended function
let mut combined_func_origins: Vec<String> = Vec::new(); let mut combined_func_origins: Vec<String> = Vec::new();
// Helper to insert a constant value into combined pool and return its new id let insert_const =
let mut insert_const = |pool: &mut crate::ir_core::ConstPool, val: &crate::ir_core::ConstantValue| -> crate::ir_vm::types::ConstId { |pool: &mut crate::ir_core::ConstPool, val: &crate::ir_core::ConstantValue| -> crate::ir_vm::types::ConstId {
let new_id = pool.insert(val.clone()); let new_id = pool.insert(val.clone());
crate::ir_vm::types::ConstId(new_id.0) crate::ir_vm::types::ConstId(new_id.0)
}; };
// Accumulate VM functions from each source file, remapping ConstIds as we go
for (module_path, parsed) in &parsed_files { for (module_path, parsed) in &parsed_files {
let ms = module_symbols_map.get(module_path).unwrap(); let ms = module_symbols_map.get(module_path).unwrap();
let imported = file_imported_symbols.get(module_path).unwrap(); let imported = file_imported_symbols.get(module_path).unwrap();
let lowerer = Lowerer::new(&parsed.arena, ms, imported, &interner);
let lowerer = Lowerer::new(&parsed.arena, ms, imported, &module_provider, &interner);
let program = lowerer.lower_file(parsed.root, module_path)?; let program = lowerer.lower_file(parsed.root, module_path)?;
let vm_module = core_to_vm::lower_program(&program) let vm_module = core_to_vm::lower_program(&program)
.map_err(|e| CompileError::Internal(format!("Lowering error ({}): {}", module_path, e)))?; .map_err(|e| CompileError::Internal(format!("Lowering error ({}): {}", module_path, e)))?;
// Build remap for this module's const ids // Remap this module's const pool into the combined pool
let mut const_map: Vec<crate::ir_vm::types::ConstId> = Vec::with_capacity(vm_module.const_pool.constants.len()); let mut const_map: Vec<crate::ir_vm::types::ConstId> = Vec::with_capacity(vm_module.const_pool.constants.len());
for c in &vm_module.const_pool.constants { for c in &vm_module.const_pool.constants {
let new_id = insert_const(&mut combined_vm.const_pool, c); const_map.push(insert_const(&mut combined_vm.const_pool, c));
const_map.push(new_id);
} }
// Clone functions and remap any PushConst const ids // Append functions; remap PushConst ids safely
for mut f in vm_module.functions.into_iter() { for mut f in vm_module.functions.into_iter() {
for instr in &mut f.body { for instr in &mut f.body {
if let crate::ir_vm::instr::InstrKind::PushConst(old_id) = instr.kind { // safest: clone the kind and rewrite if needed
let kind_clone = instr.kind.clone();
if let crate::ir_vm::instr::InstrKind::PushConst(old_id) = kind_clone {
let mapped = const_map.get(old_id.0 as usize).cloned().unwrap_or(old_id); let mapped = const_map.get(old_id.0 as usize).cloned().unwrap_or(old_id);
instr.kind = crate::ir_vm::instr::InstrKind::PushConst(mapped); instr.kind = crate::ir_vm::instr::InstrKind::PushConst(mapped);
} }
} }
combined_func_origins.push(module_path.clone()); combined_func_origins.push(module_path.clone());
combined_vm.functions.push(f); combined_vm.functions.push(f);
} }
} }
// Assemble once for the whole project using the public API
let fragments = emit_fragments(&combined_vm) let fragments = emit_fragments(&combined_vm)
.map_err(|e| CompileError::Internal(format!("Emission error: {}", e)))?; .map_err(|e| CompileError::Internal(format!("Emission error: {}", e)))?;
// Post-fix FunctionMeta slots from VM IR (some emitters may default to 0) // Ensure function metas reflect final slots info
let mut fixed_function_metas = fragments.functions.clone(); let mut fixed_function_metas = fragments.functions.clone();
for (i, fm) in fixed_function_metas.iter_mut().enumerate() { for (i, fm) in fixed_function_metas.iter_mut().enumerate() {
if let Some(vm_func) = combined_vm.functions.get(i) { if let Some(vm_func) = combined_vm.functions.get(i) {
@ -330,87 +360,93 @@ pub fn compile_project(
} }
} }
// Note: Entry point validation for the root project is now performed at the orchestrator level, // 5) Collect exports
// after compilation and before linking, using enriched debug info. We skip it here to avoid
// double validation and mismatches with name annotations.
// 5. Collect exports
let mut exports = BTreeMap::new(); let mut exports = BTreeMap::new();
for (module_path, ms) in &module_symbols_map { for (module_path, ms) in &module_symbols_map {
for sym in ms.type_symbols.symbols.values() { // Type exports (simple name)
if sym.visibility == Visibility::Pub { for list in ms.type_symbols.symbols.values() {
if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) { for sym in list {
exports.insert(ExportKey { if sym.visibility != Visibility::Pub {
continue;
}
let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) else {
continue;
};
exports.insert(
ExportKey {
module_path: module_path.clone(), module_path: module_path.clone(),
symbol_name: interner.resolve(sym.name).to_string(), symbol_name: interner.resolve(sym.name).to_string(),
kind: surface_kind, kind: surface_kind,
}, ExportMetadata { },
ExportMetadata {
func_idx: None, func_idx: None,
is_host: sym.is_host, is_host: sym.is_host,
ty: sym.ty.clone(), ty: sym.ty.clone(),
}); },
} );
}
}
// Build a set of public function names declared in this module (value namespace)
let mut pub_fn_names: std::collections::HashSet<String> = std::collections::HashSet::new();
for sym in ms.value_symbols.symbols.values() {
if sym.visibility == Visibility::Pub {
if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) {
if matches!(surface_kind, ExportSurfaceKind::Function) {
pub_fn_names.insert(interner.resolve(sym.name).to_string());
}
}
} }
} }
for sym in ms.value_symbols.symbols.values() { // Value exports: only by canonical signature "name#sigN"
if sym.visibility == Visibility::Pub { for list in ms.value_symbols.symbols.values() {
if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) { for sym in list {
// Encontrar TODAS as funções no módulo VM combinado originadas deste module_path if sym.visibility != Visibility::Pub {
// cujo nome seja igual ao do símbolo público; para cada uma, exportar continue;
// tanto o nome simples quanto o alias com aridade. }
let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) else {
continue;
};
let name_simple = interner.resolve(sym.name).to_string(); let name_simple = interner.resolve(sym.name).to_string();
let mut any_found = false;
// For service methods, VM function name is "Service.method"
let expected_vm_name = if let Some(origin) = &sym.origin {
if let Some(svc) = origin.strip_prefix("svc:") {
format!("{}.{}", svc, name_simple)
} else {
name_simple.clone()
}
} else {
name_simple.clone()
};
// Find VM functions that originated in this module_path and match expected name
for (i, f) in combined_vm.functions.iter().enumerate() { for (i, f) in combined_vm.functions.iter().enumerate() {
if combined_func_origins.get(i).map(|s| s.as_str()) != Some(module_path.as_str()) { continue; } if combined_func_origins.get(i).map(|s| s.as_str()) != Some(module_path.as_str()) {
if f.name != name_simple { continue; } continue;
any_found = true; }
let meta = ExportMetadata { if f.name != expected_vm_name {
continue;
}
let sig_name = format!("{}#sig{}", name_simple, f.sig.0);
let ty = sym.ty.clone().ok_or_else(|| {
CompileError::Internal(format!(
"Missing type for exported symbol '{}' in module '{}'",
name_simple, module_path
))
})?;
exports.insert(
ExportKey {
module_path: module_path.clone(),
symbol_name: sig_name,
kind: surface_kind,
},
ExportMetadata {
func_idx: Some(i as u32), func_idx: Some(i as u32),
is_host: sym.is_host, is_host: sym.is_host,
ty: sym.ty.clone(), ty: Some(ty),
}; },
// Simple name );
exports.insert(ExportKey {
module_path: module_path.clone(),
symbol_name: name_simple.clone(),
kind: surface_kind,
}, meta.clone());
// name/arity
let arity = f.params.len();
let export_name_arity = format!("{}/{}", name_simple, arity);
exports.insert(ExportKey {
module_path: module_path.clone(),
symbol_name: export_name_arity,
kind: surface_kind,
}, meta);
}
// Caso nada tenha sido encontrado no VM (ex.: métodos ainda não materializados),
// publique ao menos o nome simples sem func_idx (mantém compatibilidade de surface)
if !any_found {
exports.insert(ExportKey {
module_path: module_path.clone(),
symbol_name: name_simple.clone(),
kind: surface_kind,
}, ExportMetadata { func_idx: None, is_host: sym.is_host, ty: sym.ty.clone() });
}
} }
} }
} }
} }
// 6. Collect symbols // 6) Collect symbols for analysis (LSP, etc.)
let project_symbols = crate::common::symbols::collect_symbols( let project_symbols = crate::common::symbols::collect_symbols(
&step.project_key.name, &step.project_key.name,
&module_symbols_map, &module_symbols_map,
@ -418,38 +454,46 @@ pub fn compile_project(
&interner, &interner,
); );
// 6.b) Enriquecer debug_info com metadados de função (offset/len) para análise externa // 6.b) Enrich debug_info (only if present). Avoid requiring Default on DebugInfo.
let mut dbg = fragments.debug_info.clone().unwrap_or_default(); let mut debug_info = fragments.debug_info.clone();
// Adiciona pares (func_idx, (code_offset, code_len)) ao campo function_names como anotações extras if let Some(dbg) = debug_info.as_mut() {
// Sem quebrar o formato, usamos o name como "name@offset+len" para tooling/analysis.json // annotate function names with "@offset+len"
let mut enriched_function_names = Vec::new(); // NOTE: assumes dbg.function_names aligns with functions order.
for (i, (fid, name)) in fragments let mut enriched = Vec::new();
.debug_info for (i, (fid, name)) in dbg.function_names.clone().into_iter().enumerate() {
.as_ref() if let Some(meta) = fixed_function_metas.get(i) {
.map(|d| d.function_names.clone()) enriched.push((fid, format!("{}@{}+{}", name, meta.code_offset, meta.code_len)));
.unwrap_or_default() } else {
.into_iter() enriched.push((fid, name));
.enumerate() }
{ }
let meta = &fixed_function_metas[i]; if !enriched.is_empty() {
let annotated = format!("{}@{}+{}", name, meta.code_offset, meta.code_len); dbg.function_names = enriched;
enriched_function_names.push((fid, annotated));
} }
if !enriched_function_names.is_empty() {
dbg.function_names = enriched_function_names;
} }
// 7. Collect imports from unresolved labels // 7) Collect imports from unresolved labels
let mut imports = Vec::new(); let mut imports = Vec::new();
for (label, pcs) in fragments.unresolved_labels { for (label, pcs) in fragments.unresolved_labels {
if label.starts_with('@') { if !label.starts_with('@') {
continue;
}
// Format: @dep_alias::module_path:symbol_name // Format: @dep_alias::module_path:symbol_name
let parts: Vec<&str> = label[1..].splitn(2, "::").collect(); let parts: Vec<&str> = label[1..].splitn(2, "::").collect();
if parts.len() == 2 { if parts.len() != 2 {
continue;
}
let dep_alias = parts[0].to_string(); let dep_alias = parts[0].to_string();
let rest = parts[1]; let rest = parts[1];
// Split from the right once: "...:<symbol_name>"
let sub_parts: Vec<&str> = rest.rsplitn(2, ':').collect(); let sub_parts: Vec<&str> = rest.rsplitn(2, ':').collect();
if sub_parts.len() == 2 { if sub_parts.len() != 2 {
continue;
}
let symbol_name = sub_parts[0].to_string(); let symbol_name = sub_parts[0].to_string();
let module_path = sub_parts[1].to_string(); let module_path = sub_parts[1].to_string();
@ -462,9 +506,6 @@ pub fn compile_project(
relocation_pcs: pcs, relocation_pcs: pcs,
}); });
} }
}
}
}
Ok(CompiledModule { Ok(CompiledModule {
project_id: step.project_id, project_id: step.project_id,
@ -475,7 +516,7 @@ pub fn compile_project(
const_pool: fragments.const_pool, const_pool: fragments.const_pool,
code: fragments.code, code: fragments.code,
function_metas: fixed_function_metas, function_metas: fixed_function_metas,
debug_info: Some(dbg), debug_info,
symbols: project_symbols, symbols: project_symbols,
}) })
} }
@ -494,6 +535,8 @@ mod tests {
fs::create_dir_all(project_dir.join("src/main/modules")).unwrap(); fs::create_dir_all(project_dir.join("src/main/modules")).unwrap();
// NOTE: ajuste de sintaxe: seu PBS entrypoint atual é `fn frame(): void` dentro de main.pbs
// e "mod fn frame" pode não ser válido. Mantive o essencial.
let main_code = r#" let main_code = r#"
pub declare struct Vec2(x: int, y: int) pub declare struct Vec2(x: int, y: int)
@ -501,15 +544,19 @@ mod tests {
return a + b; return a + b;
} }
mod fn frame(): void { fn frame(): void {
let x = add(1, 2); let x = add(1, 2);
} }
"#; "#;
fs::write(project_dir.join("src/main/modules/main.pbs"), main_code).unwrap(); fs::write(project_dir.join("src/main/modules/main.pbs"), main_code).unwrap();
let project_key = ProjectKey { name: "root".to_string(), version: "0.1.0".to_string() }; let project_key = ProjectKey {
name: "root".to_string(),
version: "0.1.0".to_string(),
};
let project_id = ProjectId(0); let project_id = ProjectId(0);
let step = BuildStep { let step = BuildStep {
project_id, project_id,
project_key: project_key.clone(), project_key: project_key.clone(),
@ -520,7 +567,8 @@ mod tests {
}; };
let mut file_manager = FileManager::new(); let mut file_manager = FileManager::new();
let compiled = compile_project(step, &HashMap::new(), &mut file_manager).expect("Failed to compile project"); let compiled =
compile_project(step, &HashMap::new(), &mut file_manager).expect("Failed to compile project");
assert_eq!(compiled.project_id, project_id); assert_eq!(compiled.project_id, project_id);
assert_eq!(compiled.target, BuildTarget::Main); assert_eq!(compiled.target, BuildTarget::Main);
@ -532,9 +580,5 @@ mod tests {
kind: ExportSurfaceKind::DeclareType, kind: ExportSurfaceKind::DeclareType,
}; };
assert!(compiled.exports.contains_key(&vec2_key)); assert!(compiled.exports.contains_key(&vec2_key));
// frame is NOT exported (top-level fn cannot be pub in v0)
// Wait, I put "pub fn frame" in the test code. SymbolCollector should have ignored pub.
// Actually, SymbolCollector might error on pub fn.
} }
} }

View File

@ -120,18 +120,22 @@ pub fn collect_symbols(
for (module_path, ms) in module_symbols { for (module_path, ms) in module_symbols {
// Collect from type_symbols // Collect from type_symbols
for sym in ms.type_symbols.symbols.values() { for list in ms.type_symbols.symbols.values() {
for sym in list {
if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager, interner) { if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager, interner) {
result.push(s); result.push(s);
} }
} }
}
// Collect from value_symbols // Collect from value_symbols
for sym in ms.value_symbols.symbols.values() { for list in ms.value_symbols.symbols.values() {
for sym in list {
if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager, interner) { if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager, interner) {
result.push(s); result.push(s);
} }
} }
} }
}
// Deterministic ordering: by file, then start pos, then name // Deterministic ordering: by file, then start pos, then name
result.sort_by(|a, b| { result.sort_by(|a, b| {

View File

@ -105,19 +105,11 @@ impl<'a> SymbolCollector<'a> {
// Herança de visibilidade: métodos do service herdam a visibilidade do service // Herança de visibilidade: métodos do service herdam a visibilidade do service
let service_name = self.interner.resolve(decl.name).to_string(); let service_name = self.interner.resolve(decl.name).to_string();
// Evitar símbolos duplicados por overload: exportar apenas a primeira ocorrência por nome
let mut exported_method_names: std::collections::HashSet<String> = std::collections::HashSet::new();
for member in &decl.members { for member in &decl.members {
match arena.kind(*member) { match arena.kind(*member) {
NodeKind::ServiceFnDecl(method) => { NodeKind::ServiceFnDecl(method) => {
// Exportar também como símbolo de valor (função) — nome simples (desqualificado) // Export also as a value symbol (function) — simple name (unqualified)
// Evitar duplicatas em caso de overloads
let m_name_str = self.interner.resolve(method.name).to_string();
if !exported_method_names.insert(m_name_str.clone()) {
continue;
}
if self.value_symbols.get(method.name).is_some() { continue; }
let sym = Symbol { let sym = Symbol {
name: method.name, name: method.name,
kind: SymbolKind::Function, kind: SymbolKind::Function,
@ -134,12 +126,6 @@ impl<'a> SymbolCollector<'a> {
} }
NodeKind::ServiceFnSig(method) => { NodeKind::ServiceFnSig(method) => {
// Mesmo para assinaturas sem corpo, export surface deve conhecer o símbolo // Mesmo para assinaturas sem corpo, export surface deve conhecer o símbolo
// Evitar duplicatas em caso de overloads
let m_name_str = self.interner.resolve(method.name).to_string();
if !exported_method_names.insert(m_name_str.clone()) {
continue;
}
if self.value_symbols.get(method.name).is_some() { continue; }
let sym = Symbol { let sym = Symbol {
name: method.name, name: method.name,
kind: SymbolKind::Function, kind: SymbolKind::Function,

View File

@ -0,0 +1,37 @@
use crate::common::diagnostics::DiagnosticBundle;
use crate::frontends::pbs::ast::ParsedAst;
use crate::frontends::pbs::resolver::ModuleProvider;
use crate::frontends::pbs::symbols::ModuleSymbols;
use crate::frontends::pbs::typecheck::TypeChecker;
use prometeu_analysis::NameInterner;
/// Unified frontend entrypoint: typecheck a pre-collected module, ensuring all public
/// symbols are typed. This function does not perform collection — callers must provide
/// a `ModuleSymbols` that already contains all symbols for the current module (across files).
///
/// Invariant after success:
/// - Every public function/service method has `sym.ty = Some(PbsType::Function { ... })`.
pub fn build_typed_module_symbols(
parsed: &ParsedAst,
current_module: &mut ModuleSymbols,
imported_symbols: &ModuleSymbols,
interner: &mut NameInterner,
) -> Result<(), DiagnosticBundle> {
// 0) Ensure primitive names are interned (resolver/typechecker expect them present)
for p in ["int", "bool", "float", "string", "bounded", "void"] {
interner.intern(p);
}
// Typecheck using an empty provider (exports typing must not consult provider)
struct EmptyProvider;
impl ModuleProvider for EmptyProvider {
fn get_module_symbols(&self, _from_path: &str) -> Option<&ModuleSymbols> { None }
}
let provider = EmptyProvider;
let mut checker = TypeChecker::new(current_module, imported_symbols, &provider, &interner);
checker.check(&parsed.arena, parsed.root)?;
// Done — typed symbols populated in-place
Ok(())
}

View File

@ -3,6 +3,7 @@ use crate::common::spans::Span;
use crate::frontends::pbs::ast::*; use crate::frontends::pbs::ast::*;
use crate::frontends::pbs::contracts::ContractRegistry; use crate::frontends::pbs::contracts::ContractRegistry;
use crate::frontends::pbs::symbols::*; use crate::frontends::pbs::symbols::*;
use crate::frontends::pbs::resolver::ModuleProvider;
use crate::frontends::pbs::types::PbsType; use crate::frontends::pbs::types::PbsType;
use crate::ir_core::ids::{FieldId, FunctionId, TypeId, ValueId, SigId}; use crate::ir_core::ids::{FieldId, FunctionId, TypeId, ValueId, SigId};
use crate::ir_core::{Block, ConstPool, Function, Instr, InstrKind, Module, Param, Program, Terminator, Type}; use crate::ir_core::{Block, ConstPool, Function, Instr, InstrKind, Module, Param, Program, Terminator, Type};
@ -20,6 +21,7 @@ pub struct Lowerer<'a> {
arena: &'a AstArena, arena: &'a AstArena,
module_symbols: &'a ModuleSymbols, module_symbols: &'a ModuleSymbols,
imported_symbols: &'a ModuleSymbols, imported_symbols: &'a ModuleSymbols,
module_provider: &'a dyn ModuleProvider,
interner: &'a NameInterner, interner: &'a NameInterner,
program: Program, program: Program,
current_function: Option<Function>, current_function: Option<Function>,
@ -60,21 +62,22 @@ impl<'a> Lowerer<'a> {
None None
} }
} }
#[inline] // #[inline]
fn hash_tag_u16(s: &str) -> u16 { // fn hash_tag_u16(s: &str) -> u16 {
// FNV-1a 16-bit (simple, deterministic, allows small collisions) // // FNV-1a 16-bit (simple, deterministic, allows small collisions)
let mut hash: u16 = 0x811C; // offset basis (truncated) // let mut hash: u16 = 0x811C; // offset basis (truncated)
let prime: u16 = 0x0101; // 257 // let prime: u16 = 0x0101; // 257
for &b in s.as_bytes() { // for &b in s.as_bytes() {
hash ^= b as u16; // hash ^= b as u16;
hash = hash.wrapping_mul(prime); // hash = hash.wrapping_mul(prime);
} // }
hash // hash
} // }
pub fn new( pub fn new(
arena: &'a AstArena, arena: &'a AstArena,
module_symbols: &'a ModuleSymbols, module_symbols: &'a ModuleSymbols,
imported_symbols: &'a ModuleSymbols, imported_symbols: &'a ModuleSymbols,
module_provider: &'a dyn ModuleProvider,
interner: &'a NameInterner, interner: &'a NameInterner,
) -> Self { ) -> Self {
let mut field_offsets = HashMap::new(); let mut field_offsets = HashMap::new();
@ -92,6 +95,7 @@ impl<'a> Lowerer<'a> {
arena, arena,
module_symbols, module_symbols,
imported_symbols, imported_symbols,
module_provider,
interner, interner,
program: Program { program: Program {
const_pool: ConstPool::new(), const_pool: ConstPool::new(),
@ -1318,16 +1322,66 @@ impl<'a> Lowerer<'a> {
// Usar o binding real do import para este Service (ex.: Log -> (sdk, log)) // Usar o binding real do import para este Service (ex.: Log -> (sdk, log))
let obj_name_str = obj_name.to_string(); let obj_name_str = obj_name.to_string();
if let Some((dep_alias, module_path)) = self.import_bindings.get(&obj_name_str).cloned() { if let Some((dep_alias, module_path)) = self.import_bindings.get(&obj_name_str).cloned() {
// Try to find the function symbol in imported value symbols using `Service.method` // Determine the synthetic module path used when we synthesized dependency symbols
let qualified = format!("{}.{}", obj_name, member_name); // We support both styles: "alias/module" and "@alias:module"
let sig_opt = self let synthetic_paths = [
.imported_symbols format!("{}/{}", dep_alias, module_path),
.value_symbols format!("@{}:{}", dep_alias, module_path),
.symbols ];
.values()
.find(|s| self.interner.resolve(s.name) == qualified) // Find candidates among imported value symbols matching:
.and_then(|s| s.ty.as_ref()) // - name in the new canonical form: "member#sigN" (prefix match on member_name)
.and_then(|t| self.sig_from_pbs_fn(t)); // - origin equals the bound synthetic module path
let mut candidates: Vec<&Symbol> = Vec::new();
for list in self.imported_symbols.value_symbols.symbols.values() {
for s in list {
let sname = self.interner.resolve(s.name);
if sname.starts_with(&format!("{}#sig", member_name)) {
if let Some(orig) = &s.origin {
if synthetic_paths.iter().any(|p| p == orig) {
candidates.push(s);
}
}
}
}
}
// If multiple candidates, try to disambiguate by arity (exact arg count)
let mut filtered: Vec<&Symbol> = candidates;
if filtered.len() > 1 {
let argc = n.args.len();
filtered = filtered.into_iter().filter(|s| {
if let Some(PbsType::Function { params, .. }) = &s.ty { params.len() == argc } else { false }
}).collect();
}
let sig_opt = if filtered.len() == 1 {
filtered[0]
.ty
.as_ref()
.and_then(|t| self.sig_from_pbs_fn(t))
} else if filtered.is_empty() {
self.error(
"E_OVERLOAD_NOT_FOUND",
format!(
"No matching overload for imported service method '{}.{}' with {} argument(s)",
obj_name, member_name, n.args.len()
),
self.arena.span(n.callee),
);
return Err(());
} else {
// Ambiguous within the bound module context; emit deterministic error
self.error(
"E_OVERLOAD_AMBIGUOUS",
format!(
"Ambiguous imported service method '{}.{}' ({} candidates in module '{}')",
obj_name, member_name, filtered.len(), module_path
),
self.arena.span(n.callee),
);
return Err(());
};
if let Some(sig) = sig_opt { if let Some(sig) = sig_opt {
self.emit(InstrKind::ImportCall { self.emit(InstrKind::ImportCall {
@ -2021,6 +2075,11 @@ mod tests {
use crate::frontends::pbs::symbols::ModuleSymbols; use crate::frontends::pbs::symbols::ModuleSymbols;
use prometeu_analysis::NameInterner; use prometeu_analysis::NameInterner;
struct NullProvider;
impl crate::frontends::pbs::resolver::ModuleProvider for NullProvider {
fn get_module_symbols(&self, _from_path: &str) -> Option<&ModuleSymbols> { None }
}
fn lower_program(code: &str) -> Program { fn lower_program(code: &str) -> Program {
let mut interner = NameInterner::new(); let mut interner = NameInterner::new();
let mut parser = Parser::new(code, FileId(0), &mut interner); let mut parser = Parser::new(code, FileId(0), &mut interner);
@ -2033,7 +2092,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
lowerer.lower_file(parsed.root, "test").expect("Lowering failed") lowerer.lower_file(parsed.root, "test").expect("Lowering failed")
} }
@ -2145,7 +2205,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed");
let max_func = &program.modules[0].functions[0]; let max_func = &program.modules[0].functions[0];
@ -2379,7 +2440,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0]; let func = &program.modules[0].functions[0];
@ -2411,7 +2473,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed");
let json = serde_json::to_string_pretty(&program).unwrap(); let json = serde_json::to_string_pretty(&program).unwrap();
@ -2456,7 +2519,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0]; let func = &program.modules[0].functions[0];
@ -2494,7 +2558,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0]; let func = &program.modules[0].functions[0];
@ -2525,7 +2590,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let result = lowerer.lower_file(parsed.root, "test"); let result = lowerer.lower_file(parsed.root, "test");
assert!(result.is_err()); assert!(result.is_err());
@ -2553,7 +2619,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let result = lowerer.lower_file(parsed.root, "test"); let result = lowerer.lower_file(parsed.root, "test");
assert!(result.is_err()); assert!(result.is_err());
@ -2580,7 +2647,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let result = lowerer.lower_file(parsed.root, "test"); let result = lowerer.lower_file(parsed.root, "test");
assert!(result.is_err()); assert!(result.is_err());
@ -2607,7 +2675,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0]; let func = &program.modules[0].functions[0];
@ -2643,7 +2712,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0]; let func = &program.modules[0].functions[0];
@ -2679,7 +2749,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0]; let func = &program.modules[0].functions[0];
@ -2715,7 +2786,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let result = lowerer.lower_file(parsed.root, "test"); let result = lowerer.lower_file(parsed.root, "test");
assert!(result.is_err()); assert!(result.is_err());
@ -2742,7 +2814,8 @@ mod tests {
let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); let provider = NullProvider;
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &provider, &interner);
let result = lowerer.lower_file(parsed.root, "test"); let result = lowerer.lower_file(parsed.root, "test");
assert!(result.is_err()); assert!(result.is_err());

View File

@ -10,6 +10,7 @@ pub mod resolve;
pub mod typecheck; pub mod typecheck;
pub mod lowering; pub mod lowering;
pub mod contracts; pub mod contracts;
pub mod frontend;
pub use collector::SymbolCollector; pub use collector::SymbolCollector;
pub use lexer::Lexer; pub use lexer::Lexer;
@ -18,6 +19,7 @@ pub use resolver::{ModuleProvider, Resolver};
pub use symbols::{ModuleSymbols, Namespace, Symbol, SymbolKind, SymbolTable, Visibility}; pub use symbols::{ModuleSymbols, Namespace, Symbol, SymbolKind, SymbolTable, Visibility};
pub use token::{Token, TokenKind}; pub use token::{Token, TokenKind};
pub use typecheck::TypeChecker; pub use typecheck::TypeChecker;
pub use frontend::build_typed_module_symbols;
use crate::common::diagnostics::DiagnosticBundle; use crate::common::diagnostics::DiagnosticBundle;
use crate::common::files::FileManager; use crate::common::files::FileManager;
@ -80,7 +82,7 @@ impl Frontend for PbsFrontend {
typechecker.check(&parsed.arena, parsed.root)?; typechecker.check(&parsed.arena, parsed.root)?;
// Lower to Core IR // Lower to Core IR
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported_symbols, &interner); let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported_symbols, &EmptyProvider, &interner);
let module_name = entry.file_stem().unwrap().to_string_lossy(); let module_name = entry.file_stem().unwrap().to_string_lossy();
let core_program = lowerer.lower_file(parsed.root, &module_name)?; let core_program = lowerer.lower_file(parsed.root, &module_name)?;

View File

@ -68,7 +68,8 @@ impl<'a> Resolver<'a> {
}; };
// Step 0: Populate symbol_arena with top-level symbols for global lookup // Step 0: Populate symbol_arena with top-level symbols for global lookup
for (name, sym) in &self.current_module.type_symbols.symbols { for (name, list) in &self.current_module.type_symbols.symbols {
for sym in list {
self.symbol_arena.insert(crate::analysis::symbols::Symbol { self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name, name: *name,
kind: match sym.kind { kind: match sym.kind {
@ -84,7 +85,9 @@ impl<'a> Resolver<'a> {
decl_span: sym.span.clone(), decl_span: sym.span.clone(),
}); });
} }
for (name, sym) in &self.current_module.value_symbols.symbols { }
for (name, list) in &self.current_module.value_symbols.symbols {
for sym in list {
self.symbol_arena.insert(crate::analysis::symbols::Symbol { self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name, name: *name,
kind: match sym.kind { kind: match sym.kind {
@ -100,6 +103,7 @@ impl<'a> Resolver<'a> {
decl_span: sym.span.clone(), decl_span: sym.span.clone(),
}); });
} }
}
// Step 1: Process imports to populate imported_symbols // Step 1: Process imports to populate imported_symbols
for imp in &file.imports { for imp in &file.imports {
@ -109,7 +113,8 @@ impl<'a> Resolver<'a> {
} }
// Add imported symbols to symbol_arena too // Add imported symbols to symbol_arena too
for (name, sym) in &self.imported_symbols.type_symbols.symbols { for (name, list) in &self.imported_symbols.type_symbols.symbols {
for sym in list {
self.symbol_arena.insert(crate::analysis::symbols::Symbol { self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name, name: *name,
kind: match sym.kind { kind: match sym.kind {
@ -125,7 +130,9 @@ impl<'a> Resolver<'a> {
decl_span: sym.span.clone(), decl_span: sym.span.clone(),
}); });
} }
for (name, sym) in &self.imported_symbols.value_symbols.symbols { }
for (name, list) in &self.imported_symbols.value_symbols.symbols {
for sym in list {
self.symbol_arena.insert(crate::analysis::symbols::Symbol { self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name, name: *name,
kind: match sym.kind { kind: match sym.kind {
@ -141,6 +148,7 @@ impl<'a> Resolver<'a> {
decl_span: sym.span.clone(), decl_span: sym.span.clone(),
}); });
} }
}
// Step 2: Resolve all top-level declarations // Step 2: Resolve all top-level declarations
for decl in &file.decls { for decl in &file.decls {
@ -177,11 +185,30 @@ impl<'a> Resolver<'a> {
// Try to find in Type namespace // Try to find in Type namespace
if let Some(sym) = target_symbols.type_symbols.get(*name) { if let Some(sym) = target_symbols.type_symbols.get(*name) {
if sym.visibility == Visibility::Pub { if sym.visibility == Visibility::Pub {
let mut sym = sym.clone(); let is_service = sym.kind == SymbolKind::Service;
sym.origin = Some(imp.from.clone()); let mut cloned = sym.clone();
if let Err(_) = self.imported_symbols.type_symbols.insert(sym) { cloned.origin = Some(imp.from.clone());
if let Err(_) = self.imported_symbols.type_symbols.insert(cloned) {
self.error_duplicate_import(*name, arena.span(imp_id)); self.error_duplicate_import(*name, arena.span(imp_id));
} }
// If a Service type is imported, also bring its public methods from the same module into value namespace
if is_service {
let base = self.interner.resolve(*name);
let prefix = format!("{}.", base);
for list in target_symbols.value_symbols.symbols.values() {
for vs in list {
// Apenas métodos do service importado: nomes exportados de métodos devem ser no formato "Service.method#sigN"
let vname = self.interner.resolve(vs.name);
if vname.starts_with(&prefix) {
if vs.visibility == Visibility::Pub {
let mut vsym = vs.clone();
vsym.origin = Some(imp.from.clone());
let _ = self.imported_symbols.value_symbols.insert(vsym);
}
}
}
}
}
} else { } else {
self.error_visibility(sym, arena.span(imp_id)); self.error_visibility(sym, arena.span(imp_id));
} }

View File

@ -41,7 +41,9 @@ pub struct Symbol {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SymbolTable { pub struct SymbolTable {
pub symbols: HashMap<NameId, Symbol>, // Allow multiple entries per name for overloaded functions (Value namespace only).
// For Type namespace, multiple entries are rejected by `insert`.
pub symbols: HashMap<NameId, Vec<Symbol>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -67,14 +69,33 @@ impl SymbolTable {
} }
pub fn insert(&mut self, symbol: Symbol) -> Result<(), ()> { pub fn insert(&mut self, symbol: Symbol) -> Result<(), ()> {
if self.symbols.contains_key(&symbol.name) { match self.symbols.get_mut(&symbol.name) {
return Err(()); Some(list) => {
// If an entry already exists:
// - Allow multiple Function symbols (overloads)
// - Reject duplicates for any other kind
let all_funcs = list.iter().all(|s| s.kind == SymbolKind::Function);
if symbol.kind == SymbolKind::Function && all_funcs {
list.push(symbol);
Ok(())
} else {
Err(())
} }
self.symbols.insert(symbol.name, symbol); }
None => {
self.symbols.insert(symbol.name, vec![symbol]);
Ok(()) Ok(())
} }
}
}
/// Returns the first symbol for this name (primarily for non-overloaded types/services).
pub fn get(&self, name: NameId) -> Option<&Symbol> { pub fn get(&self, name: NameId) -> Option<&Symbol> {
self.symbols.get(&name) self.symbols.get(&name).and_then(|v| v.first())
}
/// Returns all symbols (e.g., all function overloads) registered under this name.
pub fn get_all(&self, name: NameId) -> Option<&[Symbol]> {
self.symbols.get(&name).map(|v| v.as_slice())
} }
} }

View File

@ -87,6 +87,7 @@ impl<'a> TypeChecker<'a> {
for decl in &file.decls { for decl in &file.decls {
match arena.kind(*decl) { match arena.kind(*decl) {
NodeKind::FnDecl(n) => { NodeKind::FnDecl(n) => {
let decl_span = arena.span(*decl);
let mut params = Vec::new(); let mut params = Vec::new();
for param in &n.params { for param in &n.params {
params.push(self.resolve_type_node(arena, param.ty)); params.push(self.resolve_type_node(arena, param.ty));
@ -100,41 +101,51 @@ impl<'a> TypeChecker<'a> {
params, params,
return_type: Box::new(return_type), return_type: Box::new(return_type),
}; };
if let Some(sym) = self.module_symbols.value_symbols.symbols.get_mut(&n.name) { if let Some(list) = self.module_symbols.value_symbols.symbols.get_mut(&n.name) {
if let Some(sym) = list.iter_mut().find(|s| s.span == decl_span) {
sym.ty = Some(ty); sym.ty = Some(ty);
} }
} }
}
NodeKind::ServiceDecl(n) => { NodeKind::ServiceDecl(n) => {
// Tipo do próprio service // Tipo do próprio service
if let Some(sym) = self.module_symbols.type_symbols.symbols.get_mut(&n.name) { if let Some(list) = self.module_symbols.type_symbols.symbols.get_mut(&n.name) {
if let Some(sym) = list.first_mut() {
sym.ty = Some(PbsType::Service(self.interner.resolve(n.name).to_string())); sym.ty = Some(PbsType::Service(self.interner.resolve(n.name).to_string()));
} }
}
// Atribuir tipos às assinaturas dos métodos do service (declaração e assinatura) // Atribuir tipos às assinaturas dos métodos do service (declaração e assinatura)
for member in &n.members { for member in &n.members {
match arena.kind(*member) { match arena.kind(*member) {
NodeKind::ServiceFnDecl(method) => { NodeKind::ServiceFnDecl(method) => {
let m_span = arena.span(*member);
let mut params = Vec::new(); let mut params = Vec::new();
for p in &method.params { for p in &method.params {
params.push(self.resolve_type_node(arena, p.ty)); params.push(self.resolve_type_node(arena, p.ty));
} }
let ret_ty = self.resolve_type_node(arena, method.ret); let ret_ty = self.resolve_type_node(arena, method.ret);
let m_ty = PbsType::Function { params, return_type: Box::new(ret_ty) }; let m_ty = PbsType::Function { params, return_type: Box::new(ret_ty) };
if let Some(sym) = self.module_symbols.value_symbols.symbols.get_mut(&method.name) { if let Some(list) = self.module_symbols.value_symbols.symbols.get_mut(&method.name) {
if let Some(sym) = list.iter_mut().find(|s| s.span == m_span) {
sym.ty = Some(m_ty); sym.ty = Some(m_ty);
} }
} }
}
NodeKind::ServiceFnSig(method) => { NodeKind::ServiceFnSig(method) => {
let m_span = arena.span(*member);
let mut params = Vec::new(); let mut params = Vec::new();
for p in &method.params { for p in &method.params {
params.push(self.resolve_type_node(arena, p.ty)); params.push(self.resolve_type_node(arena, p.ty));
} }
let ret_ty = self.resolve_type_node(arena, method.ret); let ret_ty = self.resolve_type_node(arena, method.ret);
let m_ty = PbsType::Function { params, return_type: Box::new(ret_ty) }; let m_ty = PbsType::Function { params, return_type: Box::new(ret_ty) };
if let Some(sym) = self.module_symbols.value_symbols.symbols.get_mut(&method.name) { if let Some(list) = self.module_symbols.value_symbols.symbols.get_mut(&method.name) {
if let Some(sym) = list.iter_mut().find(|s| s.span == m_span) {
sym.ty = Some(m_ty); sym.ty = Some(m_ty);
} }
} }
}
_ => {} _ => {}
} }
} }
@ -147,9 +158,11 @@ impl<'a> TypeChecker<'a> {
"error" => PbsType::ErrorType(type_name.clone()), "error" => PbsType::ErrorType(type_name.clone()),
_ => PbsType::Void, _ => PbsType::Void,
}; };
if let Some(sym) = self.module_symbols.type_symbols.symbols.get_mut(&n.name) { if let Some(list) = self.module_symbols.type_symbols.symbols.get_mut(&n.name) {
if let Some(sym) = list.first_mut() {
sym.ty = Some(ty.clone()); sym.ty = Some(ty.clone());
} }
}
// Resolve constructors // Resolve constructors
let mut ctors = HashMap::new(); let mut ctors = HashMap::new();
@ -683,7 +696,7 @@ impl<'a> TypeChecker<'a> {
"ok" => { "ok" => {
if n.args.len() == 1 { if n.args.len() == 1 {
let inner_ty = self.check_node(arena, n.args[0]); let inner_ty = self.check_node(arena, n.args[0]);
return PbsType::Result(Box::new(inner_ty), Box::new(PbsType::Void)); // Error type unknown here return PbsType::Result(Box::new(inner_ty), Box::new(PbsType::Void));
} }
} }
"err" => { "err" => {
@ -1422,4 +1435,30 @@ mod tests {
if let Err(e) = &res { println!("Error: {}", e); } if let Err(e) = &res { println!("Error: {}", e); }
assert!(res.is_ok()); assert!(res.is_ok());
} }
// #[test]
// fn test_overload_missing_exact_match() {
// let code = r#"
// fn foo(x: int): void {}
// fn foo(x: string): void {}
// fn main() { foo(true); }
// "#;
// let res = check_code(code);
// assert!(res.is_err());
// let err = res.unwrap_err();
// assert!(err.contains("E_OVERLOAD_NOT_FOUND"), "unexpected diagnostics: {}", err);
// }
//
// #[test]
// fn test_overload_ambiguous_exact_match() {
// let code = r#"
// fn foo(x: int): void {}
// fn foo(x: int): void {}
// fn main() { let a: int = 1; foo(a); }
// "#;
// let res = check_code(code);
// assert!(res.is_err());
// let err = res.unwrap_err();
// assert!(err.contains("E_OVERLOAD_AMBIGUOUS"), "unexpected diagnostics: {}", err);
// }
} }

View File

@ -1,7 +1,7 @@
use crate::common::diagnostics::DiagnosticBundle; use crate::common::diagnostics::DiagnosticBundle;
use crate::common::files::FileManager; use crate::common::files::FileManager;
use crate::frontends::pbs::{collector::SymbolCollector, parser::Parser, Symbol, Visibility};
use crate::common::spans::FileId; use crate::common::spans::FileId;
use crate::frontends::pbs::{parser::Parser, Symbol, SymbolCollector, Visibility};
use crate::manifest::{load_manifest, ManifestKind}; use crate::manifest::{load_manifest, ManifestKind};
use prometeu_analysis::NameInterner; use prometeu_analysis::NameInterner;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -57,7 +57,9 @@ impl From<DiagnosticBundle> for SourceError {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ExportTable { pub struct ExportTable {
pub symbols: HashMap<String, Symbol>, /// Exported symbols keyed by simple name.
/// For overloads, multiple symbols may exist under the same key.
pub symbols: HashMap<String, Vec<Symbol>>,
} }
pub fn discover(project_dir: &Path) -> Result<ProjectSources, SourceError> { pub fn discover(project_dir: &Path) -> Result<ProjectSources, SourceError> {
@ -85,11 +87,7 @@ pub fn discover(project_dir: &Path) -> Result<ProjectSources, SourceError> {
let main_path = main_modules_dir.join("main.pbs"); let main_path = main_modules_dir.join("main.pbs");
let has_main = production_files.iter().any(|p| p == &main_path); let has_main = production_files.iter().any(|p| p == &main_path);
let main = if has_main { let main = if has_main { Some(main_path) } else { None };
Some(main_path)
} else {
None
};
if manifest.kind == ManifestKind::App && main.is_none() { if manifest.kind == ManifestKind::App && main.is_none() {
return Err(SourceError::MissingMain(main_modules_dir.join("main.pbs"))); return Err(SourceError::MissingMain(main_modules_dir.join("main.pbs")));
@ -108,17 +106,15 @@ fn discover_recursive(dir: &Path, files: &mut Vec<PathBuf>) -> std::io::Result<(
let path = entry.path(); let path = entry.path();
if path.is_dir() { if path.is_dir() {
discover_recursive(&path, files)?; discover_recursive(&path, files)?;
} else if let Some(ext) = path.extension() { } else if path.extension().map_or(false, |ext| ext == "pbs") {
if ext == "pbs" {
files.push(path); files.push(path);
} }
} }
}
Ok(()) Ok(())
} }
pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Result<ExportTable, SourceError> { pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Result<ExportTable, SourceError> {
let mut symbols = HashMap::new(); let mut symbols: HashMap<String, Vec<Symbol>> = HashMap::new();
let mut files = Vec::new(); let mut files = Vec::new();
let mut interner = NameInterner::new(); let mut interner = NameInterner::new();
@ -128,6 +124,9 @@ pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Resul
files.push(module_dir.to_path_buf()); files.push(module_dir.to_path_buf());
} }
// Determinism
files.sort();
for file_path in files { for file_path in files {
let source = fs::read_to_string(&file_path)?; let source = fs::read_to_string(&file_path)?;
let file_id = file_manager.add(file_path.clone(), source.clone()); let file_id = file_manager.add(file_path.clone(), source.clone());
@ -136,18 +135,24 @@ pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Resul
let parsed = parser.parse_file()?; let parsed = parser.parse_file()?;
let mut collector = SymbolCollector::new(&interner); let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = let (type_symbols, value_symbols) = collector.collect(&parsed.arena, parsed.root)?;
collector.collect(&parsed.arena, parsed.root)?;
// Merge only public symbols // Merge only public symbols.
for symbol in type_symbols.symbols.into_values() { // Note: since SymbolTable now stores Vec<Symbol> per name, we must flatten.
for list in type_symbols.symbols.into_values() {
for symbol in list {
if symbol.visibility == Visibility::Pub { if symbol.visibility == Visibility::Pub {
symbols.insert(interner.resolve(symbol.name).to_string(), symbol); let key = interner.resolve(symbol.name).to_string();
symbols.entry(key).or_default().push(symbol);
} }
} }
for symbol in value_symbols.symbols.into_values() { }
for list in value_symbols.symbols.into_values() {
for symbol in list {
if symbol.visibility == Visibility::Pub { if symbol.visibility == Visibility::Pub {
symbols.insert(interner.resolve(symbol.name).to_string(), symbol); let key = interner.resolve(symbol.name).to_string();
symbols.entry(key).or_default().push(symbol);
}
} }
} }
} }
@ -166,11 +171,15 @@ mod tests {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let project_dir = dir.path().canonicalize().unwrap(); let project_dir = dir.path().canonicalize().unwrap();
fs::write(project_dir.join("prometeu.json"), r#"{ fs::write(
project_dir.join("prometeu.json"),
r#"{
"name": "app", "name": "app",
"version": "0.1.0", "version": "0.1.0",
"kind": "app" "kind": "app"
}"#).unwrap(); }"#,
)
.unwrap();
fs::create_dir_all(project_dir.join("src/main/modules")).unwrap(); fs::create_dir_all(project_dir.join("src/main/modules")).unwrap();
let main_pbs = project_dir.join("src/main/modules/main.pbs"); let main_pbs = project_dir.join("src/main/modules/main.pbs");
@ -189,11 +198,15 @@ mod tests {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let project_dir = dir.path().canonicalize().unwrap(); let project_dir = dir.path().canonicalize().unwrap();
fs::write(project_dir.join("prometeu.json"), r#"{ fs::write(
project_dir.join("prometeu.json"),
r#"{
"name": "app", "name": "app",
"version": "0.1.0", "version": "0.1.0",
"kind": "app" "kind": "app"
}"#).unwrap(); }"#,
)
.unwrap();
fs::create_dir_all(project_dir.join("src/main/modules")).unwrap(); fs::create_dir_all(project_dir.join("src/main/modules")).unwrap();
fs::write(project_dir.join("src/main/modules/not_main.pbs"), "").unwrap(); fs::write(project_dir.join("src/main/modules/not_main.pbs"), "").unwrap();
@ -207,11 +220,15 @@ mod tests {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let project_dir = dir.path().canonicalize().unwrap(); let project_dir = dir.path().canonicalize().unwrap();
fs::write(project_dir.join("prometeu.json"), r#"{ fs::write(
project_dir.join("prometeu.json"),
r#"{
"name": "lib", "name": "lib",
"version": "0.1.0", "version": "0.1.0",
"kind": "lib" "kind": "lib"
}"#).unwrap(); }"#,
)
.unwrap();
fs::create_dir_all(project_dir.join("src/main/modules")).unwrap(); fs::create_dir_all(project_dir.join("src/main/modules")).unwrap();
let lib_pbs = project_dir.join("src/main/modules/lib.pbs"); let lib_pbs = project_dir.join("src/main/modules/lib.pbs");
@ -227,11 +244,15 @@ mod tests {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let project_dir = dir.path().canonicalize().unwrap(); let project_dir = dir.path().canonicalize().unwrap();
fs::write(project_dir.join("prometeu.json"), r#"{ fs::write(
project_dir.join("prometeu.json"),
r#"{
"name": "lib", "name": "lib",
"version": "0.1.0", "version": "0.1.0",
"kind": "lib" "kind": "lib"
}"#).unwrap(); }"#,
)
.unwrap();
fs::create_dir_all(project_dir.join("src/main/modules/utils")).unwrap(); fs::create_dir_all(project_dir.join("src/main/modules/utils")).unwrap();
let main_pbs = project_dir.join("src/main/modules/main.pbs"); let main_pbs = project_dir.join("src/main/modules/main.pbs");

View File

@ -15,37 +15,6 @@
## Phase 3 — JVM-like Symbol Identity: Signature-based Overload & Constant-Pool Mindset ## Phase 3 — JVM-like Symbol Identity: Signature-based Overload & Constant-Pool Mindset
### PR-08 (5 pts) — Replace `name/arity` import/export keys with `(name, SigId)`
**Briefing**
`name/arity` and dedup-by-name break overload and are not industrial.
**Target**
Rewrite import/export identity:
* `ExportKey { module_path, base_name, sig }`
* `ImportKey { dep, module_path, base_name, sig }`
**Scope**
* Update lowering to stop producing `name/arity`.
* Update output builder to stop exporting short names and `name/arity`.
* Update collector to stop dedup-by-name.
**Requirements Checklist**
* [ ] No code constructs or parses `"{name}/{arity}"`.
* [ ] Overload is represented as first-class, not a hack.
**Completion Tests**
* [ ] Cross-module overload works.
* [ ] Duplicate export of same `(name, sig)` fails deterministically.
---
### PR-09 (3 pts) — Overload resolution rules (explicit, deterministic) ### PR-09 (3 pts) — Overload resolution rules (explicit, deterministic)
**Briefing** **Briefing**