pr 03.C
This commit is contained in:
parent
4e66387f4e
commit
4c97430ea4
@ -4,7 +4,7 @@ use crate::common::diagnostics::DiagnosticBundle;
|
||||
use crate::common::files::FileManager;
|
||||
use crate::common::spans::Span;
|
||||
use crate::deps::resolver::ProjectId;
|
||||
use crate::frontends::pbs::ast::FileNode;
|
||||
use crate::frontends::pbs::ast::ParsedAst;
|
||||
use crate::frontends::pbs::collector::SymbolCollector;
|
||||
use crate::frontends::pbs::lowering::Lowerer;
|
||||
use crate::frontends::pbs::parser::Parser;
|
||||
@ -116,7 +116,7 @@ pub fn compile_project(
|
||||
let mut interner = NameInterner::new();
|
||||
// 1. Parse all files and group symbols by module
|
||||
let mut module_symbols_map: HashMap<String, ModuleSymbols> = HashMap::new();
|
||||
let mut parsed_files: Vec<(String, FileNode)> = Vec::new(); // (module_path, ast)
|
||||
let mut parsed_files: Vec<(String, ParsedAst)> = Vec::new(); // (module_path, ast)
|
||||
|
||||
for source_rel in &step.sources {
|
||||
let source_abs = step.project_dir.join(source_rel);
|
||||
@ -124,10 +124,10 @@ pub fn compile_project(
|
||||
let file_id = file_manager.add(source_abs.clone(), source_code.clone());
|
||||
|
||||
let mut parser = Parser::new(&source_code, file_id, &mut interner);
|
||||
let ast = parser.parse_file()?;
|
||||
let parsed = parser.parse_file()?;
|
||||
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&ast)?;
|
||||
let (ts, vs) = collector.collect(&parsed.arena, parsed.root)?;
|
||||
|
||||
let full_path = source_rel.to_string_lossy().replace('\\', "/");
|
||||
let logical_module_path = if let Some(stripped) = full_path.strip_prefix("src/main/modules/") {
|
||||
@ -173,7 +173,7 @@ pub fn compile_project(
|
||||
let _ = ms.value_symbols.insert(sym);
|
||||
}
|
||||
|
||||
parsed_files.push((module_path, ast));
|
||||
parsed_files.push((module_path, parsed));
|
||||
}
|
||||
|
||||
// 2. Synthesize ModuleSymbols for dependencies
|
||||
@ -243,10 +243,10 @@ pub fn compile_project(
|
||||
// We need to collect imported symbols for Lowerer
|
||||
let mut file_imported_symbols: HashMap<String, ModuleSymbols> = HashMap::new(); // keyed by module_path
|
||||
|
||||
for (module_path, ast) in &parsed_files {
|
||||
for (module_path, parsed) in &parsed_files {
|
||||
let ms = module_symbols_map.get(module_path).unwrap();
|
||||
let mut resolver = Resolver::new(ms, &module_provider, &interner);
|
||||
resolver.resolve(ast)?;
|
||||
resolver.resolve(&parsed.arena, parsed.root)?;
|
||||
|
||||
// Capture imported symbols
|
||||
file_imported_symbols.insert(module_path.clone(), resolver.imported_symbols.clone());
|
||||
@ -255,7 +255,7 @@ pub fn compile_project(
|
||||
let mut ms_mut = module_symbols_map.get_mut(module_path).unwrap();
|
||||
let imported = file_imported_symbols.get(module_path).unwrap();
|
||||
let mut typechecker = TypeChecker::new(&mut ms_mut, imported, &module_provider, &interner);
|
||||
typechecker.check(ast)?;
|
||||
typechecker.check(&parsed.arena, parsed.root)?;
|
||||
}
|
||||
|
||||
// 4. Lower to IR
|
||||
@ -266,11 +266,11 @@ pub fn compile_project(
|
||||
field_types: HashMap::new(),
|
||||
};
|
||||
|
||||
for (module_path, ast) in &parsed_files {
|
||||
for (module_path, parsed) in &parsed_files {
|
||||
let ms = module_symbols_map.get(module_path).unwrap();
|
||||
let imported = file_imported_symbols.get(module_path).unwrap();
|
||||
let lowerer = Lowerer::new(ms, imported, &interner);
|
||||
let program = lowerer.lower_file(ast, module_path)?;
|
||||
let lowerer = Lowerer::new(&parsed.arena, ms, imported, &interner);
|
||||
let program = lowerer.lower_file(parsed.root, module_path)?;
|
||||
|
||||
// Combine program into combined_program
|
||||
if combined_program.modules.is_empty() {
|
||||
|
||||
@ -2,296 +2,296 @@ use crate::common::spans::Span;
|
||||
use prometeu_analysis::NameId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct NodeId(pub u32);
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
pub struct AstArena {
|
||||
pub nodes: Vec<NodeKind>,
|
||||
pub spans: Vec<Span>,
|
||||
pub roots: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ParsedAst {
|
||||
pub arena: AstArena,
|
||||
pub root: NodeId,
|
||||
}
|
||||
|
||||
impl AstArena {
|
||||
pub fn push(&mut self, kind: NodeKind, span: Span) -> NodeId {
|
||||
let id = NodeId(self.nodes.len() as u32);
|
||||
self.nodes.push(kind);
|
||||
self.spans.push(span);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn kind(&self, id: NodeId) -> &NodeKind {
|
||||
&self.nodes[id.0 as usize]
|
||||
}
|
||||
|
||||
pub fn span(&self, id: NodeId) -> Span {
|
||||
self.spans[id.0 as usize]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum Node {
|
||||
File(FileNode),
|
||||
Import(ImportNode),
|
||||
ImportSpec(ImportSpecNode),
|
||||
ServiceDecl(ServiceDeclNode),
|
||||
ServiceFnSig(ServiceFnSigNode),
|
||||
FnDecl(FnDeclNode),
|
||||
TypeDecl(TypeDeclNode),
|
||||
TypeBody(TypeBodyNode),
|
||||
Block(BlockNode),
|
||||
LetStmt(LetStmtNode),
|
||||
ExprStmt(ExprStmtNode),
|
||||
ReturnStmt(ReturnStmtNode),
|
||||
IntLit(IntLitNode),
|
||||
FloatLit(FloatLitNode),
|
||||
BoundedLit(BoundedLitNode),
|
||||
StringLit(StringLitNode),
|
||||
Ident(IdentNode),
|
||||
Call(CallNode),
|
||||
Unary(UnaryNode),
|
||||
Binary(BinaryNode),
|
||||
Cast(CastNode),
|
||||
IfExpr(IfExprNode),
|
||||
WhenExpr(WhenExprNode),
|
||||
WhenArm(WhenArmNode),
|
||||
TypeName(TypeNameNode),
|
||||
TypeApp(TypeAppNode),
|
||||
ConstructorDecl(ConstructorDeclNode),
|
||||
ConstantDecl(ConstantDeclNode),
|
||||
Alloc(AllocNode),
|
||||
Mutate(MutateNode),
|
||||
Borrow(BorrowNode),
|
||||
Peek(PeekNode),
|
||||
MemberAccess(MemberAccessNode),
|
||||
pub enum NodeKind {
|
||||
File(FileNodeArena),
|
||||
Import(ImportNodeArena),
|
||||
ImportSpec(ImportSpecNodeArena),
|
||||
ServiceDecl(ServiceDeclNodeArena),
|
||||
ServiceFnSig(ServiceFnSigNodeArena),
|
||||
FnDecl(FnDeclNodeArena),
|
||||
TypeDecl(TypeDeclNodeArena),
|
||||
TypeBody(TypeBodyNodeArena),
|
||||
Block(BlockNodeArena),
|
||||
LetStmt(LetStmtNodeArena),
|
||||
ExprStmt(ExprStmtNodeArena),
|
||||
ReturnStmt(ReturnStmtNodeArena),
|
||||
IntLit(IntLitNodeArena),
|
||||
FloatLit(FloatLitNodeArena),
|
||||
BoundedLit(BoundedLitNodeArena),
|
||||
StringLit(StringLitNodeArena),
|
||||
Ident(IdentNodeArena),
|
||||
Call(CallNodeArena),
|
||||
Unary(UnaryNodeArena),
|
||||
Binary(BinaryNodeArena),
|
||||
Cast(CastNodeArena),
|
||||
IfExpr(IfExprNodeArena),
|
||||
WhenExpr(WhenExprNodeArena),
|
||||
WhenArm(WhenArmNodeArena),
|
||||
TypeName(TypeNameNodeArena),
|
||||
TypeApp(TypeAppNodeArena),
|
||||
ConstructorDecl(ConstructorDeclNodeArena),
|
||||
ConstantDecl(ConstantDeclNodeArena),
|
||||
Alloc(AllocNodeArena),
|
||||
Mutate(MutateNodeArena),
|
||||
Borrow(BorrowNodeArena),
|
||||
Peek(PeekNodeArena),
|
||||
MemberAccess(MemberAccessNodeArena),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct FileNode {
|
||||
pub span: Span,
|
||||
pub imports: Vec<Node>,
|
||||
pub decls: Vec<Node>,
|
||||
pub struct FileNodeArena {
|
||||
pub imports: Vec<NodeId>,
|
||||
pub decls: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ImportNode {
|
||||
pub span: Span,
|
||||
pub spec: Box<Node>, // Must be ImportSpec
|
||||
pub struct ImportNodeArena {
|
||||
pub spec: NodeId,
|
||||
pub from: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ImportSpecNode {
|
||||
pub span: Span,
|
||||
pub struct ImportSpecNodeArena {
|
||||
pub path: Vec<NameId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ServiceDeclNode {
|
||||
pub span: Span,
|
||||
pub vis: Option<String>, // "pub" | "mod"
|
||||
pub struct ServiceDeclNodeArena {
|
||||
pub vis: Option<String>,
|
||||
pub name: NameId,
|
||||
pub extends: Option<NameId>,
|
||||
pub members: Vec<Node>, // ServiceFnSig
|
||||
pub members: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ServiceFnSigNode {
|
||||
pub struct ServiceFnSigNodeArena {
|
||||
pub name: NameId,
|
||||
pub params: Vec<ParamNodeArena>,
|
||||
pub ret: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ParamNodeArena {
|
||||
pub span: Span,
|
||||
pub name: NameId,
|
||||
pub params: Vec<ParamNode>,
|
||||
pub ret: Box<Node>, // TypeName or TypeApp
|
||||
pub ty: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ParamNode {
|
||||
pub span: Span,
|
||||
pub name: NameId,
|
||||
pub ty: Box<Node>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct FnDeclNode {
|
||||
pub span: Span,
|
||||
pub struct FnDeclNodeArena {
|
||||
pub vis: Option<String>,
|
||||
pub name: NameId,
|
||||
pub params: Vec<ParamNode>,
|
||||
pub ret: Option<Box<Node>>,
|
||||
pub else_fallback: Option<Box<Node>>, // Block
|
||||
pub body: Box<Node>, // Block
|
||||
pub params: Vec<ParamNodeArena>,
|
||||
pub ret: Option<NodeId>,
|
||||
pub else_fallback: Option<NodeId>,
|
||||
pub body: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TypeDeclNode {
|
||||
pub span: Span,
|
||||
pub struct TypeDeclNodeArena {
|
||||
pub vis: Option<String>,
|
||||
pub type_kind: String, // "struct" | "contract" | "error"
|
||||
pub type_kind: String,
|
||||
pub name: NameId,
|
||||
pub is_host: bool,
|
||||
pub params: Vec<ParamNode>, // fields for struct/error
|
||||
pub constructors: Vec<ConstructorDeclNode>, // [ ... ]
|
||||
pub constants: Vec<ConstantDeclNode>, // [[ ... ]]
|
||||
pub body: Option<Box<Node>>, // TypeBody (methods)
|
||||
pub params: Vec<ParamNodeArena>,
|
||||
pub constructors: Vec<NodeId>,
|
||||
pub constants: Vec<NodeId>,
|
||||
pub body: Option<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ConstructorDeclNode {
|
||||
pub span: Span,
|
||||
pub params: Vec<ParamNode>,
|
||||
pub initializers: Vec<Node>,
|
||||
pub struct ConstructorDeclNodeArena {
|
||||
pub params: Vec<ParamNodeArena>,
|
||||
pub initializers: Vec<NodeId>,
|
||||
pub name: NameId,
|
||||
pub body: Box<Node>, // BlockNode
|
||||
pub body: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ConstantDeclNode {
|
||||
pub struct ConstantDeclNodeArena {
|
||||
pub name: NameId,
|
||||
pub value: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TypeBodyNodeArena {
|
||||
pub members: Vec<TypeMemberNodeArena>,
|
||||
pub methods: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TypeMemberNodeArena {
|
||||
pub span: Span,
|
||||
pub name: NameId,
|
||||
pub value: Box<Node>,
|
||||
pub ty: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TypeBodyNode {
|
||||
pub span: Span,
|
||||
pub members: Vec<TypeMemberNode>,
|
||||
pub methods: Vec<ServiceFnSigNode>,
|
||||
pub struct BlockNodeArena {
|
||||
pub stmts: Vec<NodeId>,
|
||||
pub tail: Option<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TypeMemberNode {
|
||||
pub span: Span,
|
||||
pub name: NameId,
|
||||
pub ty: Box<Node>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct BlockNode {
|
||||
pub span: Span,
|
||||
pub stmts: Vec<Node>,
|
||||
pub tail: Option<Box<Node>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct LetStmtNode {
|
||||
pub span: Span,
|
||||
pub struct LetStmtNodeArena {
|
||||
pub name: NameId,
|
||||
pub is_mut: bool,
|
||||
pub ty: Option<Box<Node>>,
|
||||
pub init: Box<Node>,
|
||||
pub ty: Option<NodeId>,
|
||||
pub init: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ExprStmtNode {
|
||||
pub span: Span,
|
||||
pub expr: Box<Node>,
|
||||
pub struct ExprStmtNodeArena {
|
||||
pub expr: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ReturnStmtNode {
|
||||
pub span: Span,
|
||||
pub expr: Option<Box<Node>>,
|
||||
pub struct ReturnStmtNodeArena {
|
||||
pub expr: Option<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct IntLitNode {
|
||||
pub span: Span,
|
||||
pub struct IntLitNodeArena {
|
||||
pub value: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct FloatLitNode {
|
||||
pub span: Span,
|
||||
pub struct FloatLitNodeArena {
|
||||
pub value: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct BoundedLitNode {
|
||||
pub span: Span,
|
||||
pub struct BoundedLitNodeArena {
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct StringLitNode {
|
||||
pub span: Span,
|
||||
pub struct StringLitNodeArena {
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct IdentNode {
|
||||
pub span: Span,
|
||||
pub struct IdentNodeArena {
|
||||
pub name: NameId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CallNode {
|
||||
pub span: Span,
|
||||
pub callee: Box<Node>,
|
||||
pub args: Vec<Node>,
|
||||
pub struct CallNodeArena {
|
||||
pub callee: NodeId,
|
||||
pub args: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct UnaryNode {
|
||||
pub span: Span,
|
||||
pub struct UnaryNodeArena {
|
||||
pub op: String,
|
||||
pub expr: Box<Node>,
|
||||
pub expr: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct BinaryNode {
|
||||
pub span: Span,
|
||||
pub struct BinaryNodeArena {
|
||||
pub op: String,
|
||||
pub left: Box<Node>,
|
||||
pub right: Box<Node>,
|
||||
pub left: NodeId,
|
||||
pub right: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CastNode {
|
||||
pub span: Span,
|
||||
pub expr: Box<Node>,
|
||||
pub ty: Box<Node>,
|
||||
pub struct CastNodeArena {
|
||||
pub expr: NodeId,
|
||||
pub ty: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct IfExprNode {
|
||||
pub span: Span,
|
||||
pub cond: Box<Node>,
|
||||
pub then_block: Box<Node>,
|
||||
pub else_block: Option<Box<Node>>,
|
||||
pub struct IfExprNodeArena {
|
||||
pub cond: NodeId,
|
||||
pub then_block: NodeId,
|
||||
pub else_block: Option<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct WhenExprNode {
|
||||
pub span: Span,
|
||||
pub arms: Vec<Node>, // WhenArm
|
||||
pub struct WhenExprNodeArena {
|
||||
pub arms: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct WhenArmNode {
|
||||
pub span: Span,
|
||||
pub cond: Box<Node>,
|
||||
pub body: Box<Node>,
|
||||
pub struct WhenArmNodeArena {
|
||||
pub cond: NodeId,
|
||||
pub body: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TypeNameNode {
|
||||
pub span: Span,
|
||||
pub struct TypeNameNodeArena {
|
||||
pub name: NameId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TypeAppNode {
|
||||
pub span: Span,
|
||||
pub struct TypeAppNodeArena {
|
||||
pub base: NameId,
|
||||
pub args: Vec<Node>,
|
||||
pub args: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AllocNode {
|
||||
pub span: Span,
|
||||
pub ty: Box<Node>,
|
||||
pub struct AllocNodeArena {
|
||||
pub ty: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct MutateNode {
|
||||
pub span: Span,
|
||||
pub target: Box<Node>,
|
||||
pub struct MutateNodeArena {
|
||||
pub target: NodeId,
|
||||
pub binding: NameId,
|
||||
pub body: Box<Node>, // BlockNode
|
||||
pub body: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct BorrowNode {
|
||||
pub span: Span,
|
||||
pub target: Box<Node>,
|
||||
pub struct BorrowNodeArena {
|
||||
pub target: NodeId,
|
||||
pub binding: NameId,
|
||||
pub body: Box<Node>, // BlockNode
|
||||
pub body: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PeekNode {
|
||||
pub span: Span,
|
||||
pub target: Box<Node>,
|
||||
pub struct PeekNodeArena {
|
||||
pub target: NodeId,
|
||||
pub binding: NameId,
|
||||
pub body: Box<Node>, // BlockNode
|
||||
pub body: NodeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct MemberAccessNode {
|
||||
pub span: Span,
|
||||
pub object: Box<Node>,
|
||||
pub struct MemberAccessNodeArena {
|
||||
pub object: NodeId,
|
||||
pub member: NameId,
|
||||
}
|
||||
|
||||
@ -21,12 +21,26 @@ impl<'a> SymbolCollector<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect(&mut self, file: &FileNode) -> Result<(SymbolTable, SymbolTable), DiagnosticBundle> {
|
||||
pub fn collect(
|
||||
&mut self,
|
||||
arena: &AstArena,
|
||||
root: NodeId,
|
||||
) -> Result<(SymbolTable, SymbolTable), DiagnosticBundle> {
|
||||
let file = match arena.kind(root) {
|
||||
NodeKind::File(file) => file,
|
||||
_ => {
|
||||
return Err(DiagnosticBundle::error(
|
||||
"Expected File node as root".to_string(),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
for decl in &file.decls {
|
||||
match decl {
|
||||
Node::FnDecl(fn_decl) => self.collect_fn(fn_decl),
|
||||
Node::ServiceDecl(service_decl) => self.collect_service(service_decl),
|
||||
Node::TypeDecl(type_decl) => self.collect_type(type_decl),
|
||||
match arena.kind(*decl) {
|
||||
NodeKind::FnDecl(fn_decl) => self.collect_fn(arena, *decl, fn_decl),
|
||||
NodeKind::ServiceDecl(service_decl) => self.collect_service(arena, *decl, service_decl),
|
||||
NodeKind::TypeDecl(type_decl) => self.collect_type(arena, *decl, type_decl),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -43,50 +57,53 @@ impl<'a> SymbolCollector<'a> {
|
||||
))
|
||||
}
|
||||
|
||||
fn collect_fn(&mut self, decl: &FnDeclNode) {
|
||||
fn collect_fn(&mut self, arena: &AstArena, id: NodeId, decl: &FnDeclNodeArena) {
|
||||
let vis = match decl.vis.as_deref() {
|
||||
Some("pub") => Visibility::Pub,
|
||||
Some("mod") => Visibility::Mod,
|
||||
_ => Visibility::FilePrivate,
|
||||
};
|
||||
|
||||
self.check_export_eligibility(SymbolKind::Function, vis, decl.span);
|
||||
let span = arena.span(id);
|
||||
self.check_export_eligibility(SymbolKind::Function, vis, span);
|
||||
|
||||
let symbol = Symbol {
|
||||
name: decl.name.clone(),
|
||||
name: decl.name,
|
||||
kind: SymbolKind::Function,
|
||||
namespace: Namespace::Value,
|
||||
visibility: vis,
|
||||
ty: None, // Will be resolved later
|
||||
is_host: false,
|
||||
span: decl.span,
|
||||
span,
|
||||
origin: None,
|
||||
};
|
||||
self.insert_value_symbol(symbol);
|
||||
}
|
||||
|
||||
fn collect_service(&mut self, decl: &ServiceDeclNode) {
|
||||
fn collect_service(&mut self, arena: &AstArena, id: NodeId, decl: &ServiceDeclNodeArena) {
|
||||
let vis = match decl.vis.as_deref() {
|
||||
Some("pub") => Visibility::Pub,
|
||||
_ => Visibility::Mod, // Defaults to Mod
|
||||
};
|
||||
|
||||
self.check_export_eligibility(SymbolKind::Service, vis, decl.span);
|
||||
let span = arena.span(id);
|
||||
|
||||
self.check_export_eligibility(SymbolKind::Service, vis, span);
|
||||
|
||||
let symbol = Symbol {
|
||||
name: decl.name.clone(),
|
||||
name: decl.name,
|
||||
kind: SymbolKind::Service,
|
||||
namespace: Namespace::Type, // Service is a type
|
||||
visibility: vis,
|
||||
ty: None,
|
||||
is_host: false,
|
||||
span: decl.span,
|
||||
span,
|
||||
origin: None,
|
||||
};
|
||||
self.insert_type_symbol(symbol);
|
||||
}
|
||||
|
||||
fn collect_type(&mut self, decl: &TypeDeclNode) {
|
||||
fn collect_type(&mut self, arena: &AstArena, id: NodeId, decl: &TypeDeclNodeArena) {
|
||||
let vis = match decl.vis.as_deref() {
|
||||
Some("pub") => Visibility::Pub,
|
||||
Some("mod") => Visibility::Mod,
|
||||
@ -99,16 +116,18 @@ impl<'a> SymbolCollector<'a> {
|
||||
_ => SymbolKind::Struct, // Default
|
||||
};
|
||||
|
||||
self.check_export_eligibility(kind.clone(), vis, decl.span);
|
||||
let span = arena.span(id);
|
||||
|
||||
self.check_export_eligibility(kind.clone(), vis, span);
|
||||
|
||||
let symbol = Symbol {
|
||||
name: decl.name.clone(),
|
||||
name: decl.name,
|
||||
kind,
|
||||
namespace: Namespace::Type,
|
||||
visibility: vis,
|
||||
ty: None,
|
||||
is_host: decl.is_host,
|
||||
span: decl.span,
|
||||
span,
|
||||
origin: None,
|
||||
};
|
||||
self.insert_type_symbol(symbol);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -45,10 +45,11 @@ impl Frontend for PbsFrontend {
|
||||
|
||||
let mut interner = NameInterner::new();
|
||||
let mut parser = parser::Parser::new(&source, file_id, &mut interner);
|
||||
let ast = parser.parse_file()?;
|
||||
let parsed = parser.parse_file()?;
|
||||
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (type_symbols, value_symbols) = collector.collect(&ast)?;
|
||||
let (type_symbols, value_symbols) =
|
||||
collector.collect(&parsed.arena, parsed.root)?;
|
||||
let mut module_symbols = ModuleSymbols { type_symbols, value_symbols };
|
||||
|
||||
struct EmptyProvider;
|
||||
@ -57,16 +58,16 @@ impl Frontend for PbsFrontend {
|
||||
}
|
||||
|
||||
let mut resolver = Resolver::new(&module_symbols, &EmptyProvider, &interner);
|
||||
resolver.resolve(&ast)?;
|
||||
resolver.resolve(&parsed.arena, parsed.root)?;
|
||||
let imported_symbols = resolver.imported_symbols;
|
||||
|
||||
let mut typechecker = TypeChecker::new(&mut module_symbols, &imported_symbols, &EmptyProvider, &interner);
|
||||
typechecker.check(&ast)?;
|
||||
typechecker.check(&parsed.arena, parsed.root)?;
|
||||
|
||||
// Lower to Core IR
|
||||
let lowerer = Lowerer::new(&module_symbols, &imported_symbols, &interner);
|
||||
let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported_symbols, &interner);
|
||||
let module_name = entry.file_stem().unwrap().to_string_lossy();
|
||||
let core_program = lowerer.lower_file(&ast, &module_name)?;
|
||||
let core_program = lowerer.lower_file(parsed.root, &module_name)?;
|
||||
|
||||
// Validate Core IR Invariants
|
||||
crate::ir_core::validate_program(&core_program).map_err(|e| {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -34,17 +34,27 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(&mut self, file: &FileNode) -> Result<(), DiagnosticBundle> {
|
||||
pub fn resolve(&mut self, arena: &AstArena, root: NodeId) -> Result<(), DiagnosticBundle> {
|
||||
let file = match arena.kind(root) {
|
||||
NodeKind::File(file) => file,
|
||||
_ => {
|
||||
return Err(DiagnosticBundle::error(
|
||||
"Expected File node as root".to_string(),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// Step 1: Process imports to populate imported_symbols
|
||||
for imp in &file.imports {
|
||||
if let Node::Import(imp_node) = imp {
|
||||
self.resolve_import(imp_node);
|
||||
if let NodeKind::Import(imp_node) = arena.kind(*imp) {
|
||||
self.resolve_import(arena, *imp, imp_node);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Resolve all top-level declarations
|
||||
for decl in &file.decls {
|
||||
self.resolve_node(decl);
|
||||
self.resolve_node(arena, *decl);
|
||||
}
|
||||
|
||||
if !self.diagnostics.is_empty() {
|
||||
@ -56,10 +66,22 @@ impl<'a> Resolver<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_import(&mut self, imp: &ImportNode) {
|
||||
fn resolve_import(&mut self, arena: &AstArena, imp_id: NodeId, imp: &ImportNodeArena) {
|
||||
let provider = self.module_provider;
|
||||
if let Some(target_symbols) = provider.get_module_symbols(&imp.from) {
|
||||
if let Node::ImportSpec(spec) = &*imp.spec {
|
||||
let spec = match arena.kind(imp.spec) {
|
||||
NodeKind::ImportSpec(spec) => spec,
|
||||
_ => {
|
||||
self.diagnostics.push(Diagnostic {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_RESOLVE_INVALID_IMPORT".to_string()),
|
||||
message: "Invalid import spec".to_string(),
|
||||
span: Some(arena.span(imp_id)),
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
for name in &spec.path {
|
||||
// Try to find in Type namespace
|
||||
if let Some(sym) = target_symbols.type_symbols.get(*name) {
|
||||
@ -67,10 +89,10 @@ impl<'a> Resolver<'a> {
|
||||
let mut sym = sym.clone();
|
||||
sym.origin = Some(imp.from.clone());
|
||||
if let Err(_) = self.imported_symbols.type_symbols.insert(sym) {
|
||||
self.error_duplicate_import(*name, imp.span);
|
||||
self.error_duplicate_import(*name, arena.span(imp_id));
|
||||
}
|
||||
} else {
|
||||
self.error_visibility(sym, imp.span);
|
||||
self.error_visibility(sym, arena.span(imp_id));
|
||||
}
|
||||
}
|
||||
// Try to find in Value namespace
|
||||
@ -79,14 +101,13 @@ impl<'a> Resolver<'a> {
|
||||
let mut sym = sym.clone();
|
||||
sym.origin = Some(imp.from.clone());
|
||||
if let Err(_) = self.imported_symbols.value_symbols.insert(sym) {
|
||||
self.error_duplicate_import(*name, imp.span);
|
||||
self.error_duplicate_import(*name, arena.span(imp_id));
|
||||
}
|
||||
} else {
|
||||
self.error_visibility(sym, imp.span);
|
||||
self.error_visibility(sym, arena.span(imp_id));
|
||||
}
|
||||
} else {
|
||||
self.error_undefined(*name, imp.span);
|
||||
}
|
||||
self.error_undefined(*name, arena.span(imp_id));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -94,199 +115,221 @@ impl<'a> Resolver<'a> {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_RESOLVE_INVALID_IMPORT".to_string()),
|
||||
message: format!("Module not found: {}", imp.from),
|
||||
span: Some(imp.span),
|
||||
span: Some(arena.span(imp_id)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_node(&mut self, node: &Node) {
|
||||
match node {
|
||||
Node::FnDecl(n) => self.resolve_fn_decl(n),
|
||||
Node::ServiceDecl(n) => self.resolve_service_decl(n),
|
||||
Node::TypeDecl(n) => self.resolve_type_decl(n),
|
||||
Node::Block(n) => self.resolve_block(n),
|
||||
Node::LetStmt(n) => self.resolve_let_stmt(n),
|
||||
Node::ExprStmt(n) => self.resolve_node(&n.expr),
|
||||
Node::ReturnStmt(n) => {
|
||||
if let Some(expr) = &n.expr {
|
||||
self.resolve_node(expr);
|
||||
fn resolve_node(&mut self, arena: &AstArena, node: NodeId) {
|
||||
match arena.kind(node) {
|
||||
NodeKind::FnDecl(n) => self.resolve_fn_decl(arena, node, n),
|
||||
NodeKind::ServiceDecl(n) => self.resolve_service_decl(arena, node, n),
|
||||
NodeKind::TypeDecl(n) => self.resolve_type_decl(arena, node, n),
|
||||
NodeKind::Block(n) => self.resolve_block(arena, n),
|
||||
NodeKind::LetStmt(n) => self.resolve_let_stmt(arena, node, n),
|
||||
NodeKind::ExprStmt(n) => self.resolve_node(arena, n.expr),
|
||||
NodeKind::ReturnStmt(n) => {
|
||||
if let Some(expr) = n.expr {
|
||||
self.resolve_node(arena, expr);
|
||||
}
|
||||
}
|
||||
Node::Call(n) => {
|
||||
self.resolve_node(&n.callee);
|
||||
NodeKind::Call(n) => {
|
||||
if let NodeKind::Ident(id) = arena.kind(n.callee) {
|
||||
if !self.is_builtin(id.name, Namespace::Value) {
|
||||
let value_sym = self.lookup_identifier(id.name, Namespace::Value);
|
||||
if value_sym.is_none() && self.lookup_identifier(id.name, Namespace::Type).is_none() {
|
||||
// TODO: Resolver for v0 allows unresolved call targets (e.g. constructors via prelude)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.resolve_node(arena, n.callee);
|
||||
}
|
||||
for arg in &n.args {
|
||||
self.resolve_node(arg);
|
||||
self.resolve_node(arena, *arg);
|
||||
}
|
||||
}
|
||||
Node::Unary(n) => self.resolve_node(&n.expr),
|
||||
Node::Binary(n) => {
|
||||
self.resolve_node(&n.left);
|
||||
self.resolve_node(&n.right);
|
||||
NodeKind::Unary(n) => self.resolve_node(arena, n.expr),
|
||||
NodeKind::Binary(n) => {
|
||||
self.resolve_node(arena, n.left);
|
||||
self.resolve_node(arena, n.right);
|
||||
}
|
||||
Node::Cast(n) => {
|
||||
self.resolve_node(&n.expr);
|
||||
self.resolve_type_ref(&n.ty);
|
||||
NodeKind::Cast(n) => {
|
||||
self.resolve_node(arena, n.expr);
|
||||
self.resolve_type_ref(arena, n.ty);
|
||||
}
|
||||
Node::IfExpr(n) => {
|
||||
self.resolve_node(&n.cond);
|
||||
self.resolve_node(&n.then_block);
|
||||
if let Some(else_block) = &n.else_block {
|
||||
self.resolve_node(else_block);
|
||||
NodeKind::IfExpr(n) => {
|
||||
self.resolve_node(arena, n.cond);
|
||||
self.resolve_node(arena, n.then_block);
|
||||
if let Some(else_block) = n.else_block {
|
||||
self.resolve_node(arena, else_block);
|
||||
}
|
||||
}
|
||||
Node::WhenExpr(n) => {
|
||||
NodeKind::WhenExpr(n) => {
|
||||
for arm in &n.arms {
|
||||
if let Node::WhenArm(arm_node) = arm {
|
||||
self.resolve_node(&arm_node.cond);
|
||||
self.resolve_node(&arm_node.body);
|
||||
if let NodeKind::WhenArm(arm_node) = arena.kind(*arm) {
|
||||
self.resolve_node(arena, arm_node.cond);
|
||||
self.resolve_node(arena, arm_node.body);
|
||||
}
|
||||
}
|
||||
}
|
||||
Node::Ident(n) => {
|
||||
self.resolve_identifier(n.name, n.span, Namespace::Value);
|
||||
NodeKind::Ident(n) => {
|
||||
self.resolve_identifier(n.name, arena.span(node), Namespace::Value);
|
||||
}
|
||||
Node::TypeName(n) => {
|
||||
self.resolve_identifier(n.name, n.span, Namespace::Type);
|
||||
NodeKind::TypeName(n) => {
|
||||
self.resolve_identifier(n.name, arena.span(node), Namespace::Type);
|
||||
}
|
||||
Node::TypeApp(n) => {
|
||||
self.resolve_identifier(n.base, n.span, Namespace::Type);
|
||||
NodeKind::TypeApp(n) => {
|
||||
self.resolve_identifier(n.base, arena.span(node), Namespace::Type);
|
||||
for arg in &n.args {
|
||||
self.resolve_type_ref(arg);
|
||||
self.resolve_type_ref(arena, *arg);
|
||||
}
|
||||
}
|
||||
Node::ConstructorDecl(n) => self.resolve_constructor_decl(n),
|
||||
Node::ConstantDecl(n) => self.resolve_node(&n.value),
|
||||
Node::Alloc(n) => {
|
||||
self.resolve_type_ref(&n.ty);
|
||||
NodeKind::ConstructorDecl(n) => self.resolve_constructor_decl(arena, node, n),
|
||||
NodeKind::ConstantDecl(n) => self.resolve_node(arena, n.value),
|
||||
NodeKind::Alloc(n) => {
|
||||
self.resolve_type_ref(arena, n.ty);
|
||||
}
|
||||
Node::Mutate(n) => {
|
||||
self.resolve_node(&n.target);
|
||||
NodeKind::Mutate(n) => {
|
||||
self.resolve_node(arena, n.target);
|
||||
self.enter_scope();
|
||||
self.define_local(n.binding, n.span, SymbolKind::Local);
|
||||
self.resolve_node(&n.body);
|
||||
self.define_local(n.binding, arena.span(node), SymbolKind::Local);
|
||||
self.resolve_node(arena, n.body);
|
||||
self.exit_scope();
|
||||
}
|
||||
Node::Borrow(n) => {
|
||||
self.resolve_node(&n.target);
|
||||
NodeKind::Borrow(n) => {
|
||||
self.resolve_node(arena, n.target);
|
||||
self.enter_scope();
|
||||
self.define_local(n.binding, n.span, SymbolKind::Local);
|
||||
self.resolve_node(&n.body);
|
||||
self.define_local(n.binding, arena.span(node), SymbolKind::Local);
|
||||
self.resolve_node(arena, n.body);
|
||||
self.exit_scope();
|
||||
}
|
||||
Node::Peek(n) => {
|
||||
self.resolve_node(&n.target);
|
||||
NodeKind::Peek(n) => {
|
||||
self.resolve_node(arena, n.target);
|
||||
self.enter_scope();
|
||||
self.define_local(n.binding, n.span, SymbolKind::Local);
|
||||
self.resolve_node(&n.body);
|
||||
self.define_local(n.binding, arena.span(node), SymbolKind::Local);
|
||||
self.resolve_node(arena, n.body);
|
||||
self.exit_scope();
|
||||
}
|
||||
Node::MemberAccess(n) => {
|
||||
if let Node::Ident(id) = &*n.object {
|
||||
NodeKind::MemberAccess(n) => match arena.kind(n.object) {
|
||||
NodeKind::Ident(id) => {
|
||||
let ident_span = arena.span(n.object);
|
||||
if !self.is_builtin(id.name, Namespace::Type) {
|
||||
if self.lookup_identifier(id.name, Namespace::Value).is_none() {
|
||||
// If not found in Value namespace, try Type namespace (for Contracts/Services)
|
||||
if self.lookup_identifier(id.name, Namespace::Type).is_none() {
|
||||
// Still not found, use resolve_identifier to report error in Value namespace
|
||||
self.resolve_identifier(id.name, id.span, Namespace::Value);
|
||||
self.resolve_identifier(id.name, ident_span, Namespace::Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.resolve_node(&n.object);
|
||||
}
|
||||
_ => {
|
||||
self.resolve_node(arena, n.object);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_fn_decl(&mut self, n: &FnDeclNode) {
|
||||
fn resolve_fn_decl(&mut self, arena: &AstArena, id: NodeId, n: &FnDeclNodeArena) {
|
||||
self.enter_scope();
|
||||
for param in &n.params {
|
||||
self.resolve_type_ref(¶m.ty);
|
||||
self.resolve_type_ref(arena, param.ty);
|
||||
self.define_local(param.name, param.span, SymbolKind::Local);
|
||||
}
|
||||
if let Some(ret) = &n.ret {
|
||||
self.resolve_type_ref(ret);
|
||||
if let Some(ret) = n.ret {
|
||||
self.resolve_type_ref(arena, ret);
|
||||
}
|
||||
self.resolve_node(&n.body);
|
||||
self.resolve_node(arena, n.body);
|
||||
self.exit_scope();
|
||||
}
|
||||
|
||||
fn resolve_service_decl(&mut self, n: &ServiceDeclNode) {
|
||||
if let Some(ext) = &n.extends {
|
||||
self.resolve_identifier(*ext, n.span, Namespace::Type);
|
||||
fn resolve_service_decl(&mut self, arena: &AstArena, id: NodeId, n: &ServiceDeclNodeArena) {
|
||||
if let Some(ext) = n.extends {
|
||||
self.resolve_identifier(ext, arena.span(id), Namespace::Type);
|
||||
}
|
||||
for member in &n.members {
|
||||
if let Node::ServiceFnSig(sig) = member {
|
||||
if let NodeKind::ServiceFnSig(sig) = arena.kind(*member) {
|
||||
for param in &sig.params {
|
||||
self.resolve_type_ref(¶m.ty);
|
||||
self.resolve_type_ref(arena, param.ty);
|
||||
}
|
||||
self.resolve_type_ref(&sig.ret);
|
||||
self.resolve_type_ref(arena, sig.ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_type_decl(&mut self, n: &TypeDeclNode) {
|
||||
fn resolve_type_decl(&mut self, arena: &AstArena, _id: NodeId, n: &TypeDeclNodeArena) {
|
||||
for param in &n.params {
|
||||
self.resolve_type_ref(¶m.ty);
|
||||
self.resolve_type_ref(arena, param.ty);
|
||||
}
|
||||
for constructor in &n.constructors {
|
||||
self.resolve_constructor_decl(constructor);
|
||||
if let NodeKind::ConstructorDecl(ctor) = arena.kind(*constructor) {
|
||||
self.resolve_constructor_decl(arena, *constructor, ctor);
|
||||
}
|
||||
}
|
||||
self.enter_scope();
|
||||
for ctor in &n.constructors {
|
||||
self.define_local(ctor.name, ctor.span, SymbolKind::Local);
|
||||
for ctor_id in &n.constructors {
|
||||
if let NodeKind::ConstructorDecl(ctor) = arena.kind(*ctor_id) {
|
||||
self.define_local(ctor.name, arena.span(*ctor_id), SymbolKind::Local);
|
||||
}
|
||||
}
|
||||
for constant in &n.constants {
|
||||
self.resolve_node(&constant.value);
|
||||
self.resolve_node(arena, *constant);
|
||||
}
|
||||
self.exit_scope();
|
||||
if let Some(body_node) = &n.body {
|
||||
if let Node::TypeBody(body) = &**body_node {
|
||||
if let Some(body_node) = n.body {
|
||||
if let NodeKind::TypeBody(body) = arena.kind(body_node) {
|
||||
for member in &body.members {
|
||||
self.resolve_type_ref(&member.ty);
|
||||
self.resolve_type_ref(arena, member.ty);
|
||||
}
|
||||
for method in &body.methods {
|
||||
for param in &method.params {
|
||||
self.resolve_type_ref(¶m.ty);
|
||||
if let NodeKind::ServiceFnSig(sig) = arena.kind(*method) {
|
||||
for param in &sig.params {
|
||||
self.resolve_type_ref(arena, param.ty);
|
||||
}
|
||||
self.resolve_type_ref(arena, sig.ret);
|
||||
}
|
||||
self.resolve_type_ref(&method.ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_constructor_decl(&mut self, n: &ConstructorDeclNode) {
|
||||
fn resolve_constructor_decl(
|
||||
&mut self,
|
||||
arena: &AstArena,
|
||||
_id: NodeId,
|
||||
n: &ConstructorDeclNodeArena,
|
||||
) {
|
||||
self.enter_scope();
|
||||
for param in &n.params {
|
||||
self.resolve_type_ref(¶m.ty);
|
||||
self.resolve_type_ref(arena, param.ty);
|
||||
self.define_local(param.name, param.span, SymbolKind::Local);
|
||||
}
|
||||
for init in &n.initializers {
|
||||
self.resolve_node(init);
|
||||
self.resolve_node(arena, *init);
|
||||
}
|
||||
self.resolve_node(&n.body);
|
||||
self.resolve_node(arena, n.body);
|
||||
self.exit_scope();
|
||||
}
|
||||
|
||||
fn resolve_block(&mut self, n: &BlockNode) {
|
||||
fn resolve_block(&mut self, arena: &AstArena, n: &BlockNodeArena) {
|
||||
self.enter_scope();
|
||||
for stmt in &n.stmts {
|
||||
self.resolve_node(stmt);
|
||||
self.resolve_node(arena, *stmt);
|
||||
}
|
||||
self.exit_scope();
|
||||
}
|
||||
|
||||
fn resolve_let_stmt(&mut self, n: &LetStmtNode) {
|
||||
if let Some(ty) = &n.ty {
|
||||
self.resolve_type_ref(ty);
|
||||
fn resolve_let_stmt(&mut self, arena: &AstArena, id: NodeId, n: &LetStmtNodeArena) {
|
||||
if let Some(ty) = n.ty {
|
||||
self.resolve_type_ref(arena, ty);
|
||||
}
|
||||
self.resolve_node(&n.init);
|
||||
self.define_local(n.name, n.span, SymbolKind::Local);
|
||||
self.resolve_node(arena, n.init);
|
||||
self.define_local(n.name, arena.span(id), SymbolKind::Local);
|
||||
}
|
||||
|
||||
fn resolve_type_ref(&mut self, node: &Node) {
|
||||
self.resolve_node(node);
|
||||
fn resolve_type_ref(&mut self, arena: &AstArena, node: NodeId) {
|
||||
self.resolve_node(arena, node);
|
||||
}
|
||||
|
||||
fn resolve_identifier(&mut self, name: NameId, span: Span, namespace: Namespace) -> Option<Symbol> {
|
||||
@ -461,13 +504,13 @@ mod tests {
|
||||
use crate::frontends::pbs::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn setup_test(source: &str) -> (ast::FileNode, usize, NameInterner) {
|
||||
fn setup_test(source: &str) -> (ParsedAst, usize, NameInterner) {
|
||||
let mut fm = FileManager::new();
|
||||
let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string());
|
||||
let mut interner = NameInterner::new();
|
||||
let mut parser = parser::Parser::new(source, file_id, &mut interner);
|
||||
let ast = parser.parse_file().expect("Parsing failed");
|
||||
(ast, file_id, interner)
|
||||
let parsed = parser.parse_file().expect("Parsing failed");
|
||||
(parsed, file_id, interner)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -476,9 +519,9 @@ mod tests {
|
||||
declare struct Foo {}
|
||||
declare struct Foo {}
|
||||
";
|
||||
let (ast, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let result = collector.collect(&ast);
|
||||
let result = collector.collect(&parsed.arena, parsed.root);
|
||||
|
||||
assert!(result.is_err());
|
||||
let bundle = result.unwrap_err();
|
||||
@ -491,9 +534,9 @@ mod tests {
|
||||
declare struct Foo {}
|
||||
fn Foo() {}
|
||||
";
|
||||
let (ast, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let result = collector.collect(&ast);
|
||||
let result = collector.collect(&parsed.arena, parsed.root);
|
||||
|
||||
assert!(result.is_err());
|
||||
let bundle = result.unwrap_err();
|
||||
@ -507,9 +550,11 @@ mod tests {
|
||||
let x = y;
|
||||
}
|
||||
";
|
||||
let (ast, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
|
||||
let (ts, vs) = collector
|
||||
.collect(&parsed.arena, parsed.root)
|
||||
.expect("Collection failed");
|
||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||
|
||||
struct EmptyProvider;
|
||||
@ -518,7 +563,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
|
||||
let result = resolver.resolve(&ast);
|
||||
let result = resolver.resolve(&parsed.arena, parsed.root);
|
||||
|
||||
assert!(result.is_err());
|
||||
let bundle = result.unwrap_err();
|
||||
@ -533,9 +578,11 @@ mod tests {
|
||||
let y = x;
|
||||
}
|
||||
";
|
||||
let (ast, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
|
||||
let (ts, vs) = collector
|
||||
.collect(&parsed.arena, parsed.root)
|
||||
.expect("Collection failed");
|
||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||
|
||||
struct EmptyProvider;
|
||||
@ -544,7 +591,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
|
||||
let result = resolver.resolve(&ast);
|
||||
let result = resolver.resolve(&parsed.arena, parsed.root);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
@ -555,9 +602,11 @@ mod tests {
|
||||
import PrivateType from \"./other.pbs\"
|
||||
fn main() {}
|
||||
";
|
||||
let (ast, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
|
||||
let (ts, vs) = collector
|
||||
.collect(&parsed.arena, parsed.root)
|
||||
.expect("Collection failed");
|
||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||
|
||||
struct MockProvider {
|
||||
@ -586,7 +635,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let mut resolver = Resolver::new(&ms, &mock_provider, &interner);
|
||||
let result = resolver.resolve(&ast);
|
||||
let result = resolver.resolve(&parsed.arena, parsed.root);
|
||||
|
||||
assert!(result.is_err());
|
||||
let bundle = result.unwrap_err();
|
||||
@ -601,9 +650,11 @@ mod tests {
|
||||
let x: PubType = 10;
|
||||
}
|
||||
";
|
||||
let (ast, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
|
||||
let (ts, vs) = collector
|
||||
.collect(&parsed.arena, parsed.root)
|
||||
.expect("Collection failed");
|
||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||
|
||||
struct MockProvider {
|
||||
@ -632,7 +683,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let mut resolver = Resolver::new(&ms, &mock_provider, &interner);
|
||||
let result = resolver.resolve(&ast);
|
||||
let result = resolver.resolve(&parsed.arena, parsed.root);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
@ -643,9 +694,11 @@ mod tests {
|
||||
import NonExistent from \"./missing.pbs\"
|
||||
fn main() {}
|
||||
";
|
||||
let (ast, _, interner) = setup_test(source);
|
||||
let (parsed, _, interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
|
||||
let (ts, vs) = collector
|
||||
.collect(&parsed.arena, parsed.root)
|
||||
.expect("Collection failed");
|
||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||
|
||||
struct EmptyProvider;
|
||||
@ -654,7 +707,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
|
||||
let result = resolver.resolve(&ast);
|
||||
let result = resolver.resolve(&parsed.arena, parsed.root);
|
||||
|
||||
assert!(result.is_err());
|
||||
let bundle = result.unwrap_err();
|
||||
|
||||
@ -46,13 +46,23 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check(&mut self, file: &FileNode) -> Result<(), DiagnosticBundle> {
|
||||
pub fn check(&mut self, arena: &AstArena, root: NodeId) -> Result<(), DiagnosticBundle> {
|
||||
let file = match arena.kind(root) {
|
||||
NodeKind::File(file) => file,
|
||||
_ => {
|
||||
return Err(DiagnosticBundle::error(
|
||||
"Expected File node as root".to_string(),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// Step 1: Resolve signatures of all top-level declarations
|
||||
self.resolve_signatures(file);
|
||||
self.resolve_signatures(arena, file);
|
||||
|
||||
// Step 2: Check bodies
|
||||
for decl in &file.decls {
|
||||
self.check_node(decl);
|
||||
self.check_node(arena, *decl);
|
||||
}
|
||||
|
||||
if !self.diagnostics.is_empty() {
|
||||
@ -64,16 +74,16 @@ impl<'a> TypeChecker<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_signatures(&mut self, file: &FileNode) {
|
||||
fn resolve_signatures(&mut self, arena: &AstArena, file: &FileNodeArena) {
|
||||
for decl in &file.decls {
|
||||
match decl {
|
||||
Node::FnDecl(n) => {
|
||||
match arena.kind(*decl) {
|
||||
NodeKind::FnDecl(n) => {
|
||||
let mut params = Vec::new();
|
||||
for param in &n.params {
|
||||
params.push(self.resolve_type_node(¶m.ty));
|
||||
params.push(self.resolve_type_node(arena, param.ty));
|
||||
}
|
||||
let return_type = if let Some(ret) = &n.ret {
|
||||
self.resolve_type_node(ret)
|
||||
let return_type = if let Some(ret) = n.ret {
|
||||
self.resolve_type_node(arena, ret)
|
||||
} else {
|
||||
PbsType::Void
|
||||
};
|
||||
@ -85,13 +95,13 @@ impl<'a> TypeChecker<'a> {
|
||||
sym.ty = Some(ty);
|
||||
}
|
||||
}
|
||||
Node::ServiceDecl(n) => {
|
||||
NodeKind::ServiceDecl(n) => {
|
||||
// For service, the symbol's type is just Service(name)
|
||||
if let Some(sym) = self.module_symbols.type_symbols.symbols.get_mut(&n.name) {
|
||||
sym.ty = Some(PbsType::Service(self.interner.resolve(n.name).to_string()));
|
||||
}
|
||||
}
|
||||
Node::TypeDecl(n) => {
|
||||
NodeKind::TypeDecl(n) => {
|
||||
let type_name = self.interner.resolve(n.name).to_string();
|
||||
let ty = match n.type_kind.as_str() {
|
||||
"struct" => PbsType::Struct(type_name.clone()),
|
||||
@ -109,26 +119,22 @@ impl<'a> TypeChecker<'a> {
|
||||
// Default constructor: TypeName(...)
|
||||
if n.type_kind == "struct" {
|
||||
let mut params = Vec::new();
|
||||
let mut initializers = Vec::new();
|
||||
for p in &n.params {
|
||||
let p_ty = self.resolve_type_node(&p.ty);
|
||||
let p_ty = self.resolve_type_node(arena, p.ty);
|
||||
params.push(p_ty);
|
||||
initializers.push(Node::Ident(IdentNode {
|
||||
span: p.span,
|
||||
name: p.name.clone(),
|
||||
}));
|
||||
}
|
||||
let default_ctor_ty = PbsType::Function {
|
||||
params: params.clone(),
|
||||
params,
|
||||
return_type: Box::new(ty.clone()),
|
||||
};
|
||||
ctors.insert(type_name.clone(), default_ctor_ty);
|
||||
}
|
||||
|
||||
for ctor in &n.constructors {
|
||||
if let NodeKind::ConstructorDecl(ctor) = arena.kind(*ctor) {
|
||||
let mut params = Vec::new();
|
||||
for p in &ctor.params {
|
||||
params.push(self.resolve_type_node(&p.ty));
|
||||
params.push(self.resolve_type_node(arena, p.ty));
|
||||
}
|
||||
let ctor_ty = PbsType::Function {
|
||||
params,
|
||||
@ -136,22 +142,25 @@ impl<'a> TypeChecker<'a> {
|
||||
};
|
||||
ctors.insert(self.interner.resolve(ctor.name).to_string(), ctor_ty);
|
||||
}
|
||||
}
|
||||
self.struct_constructors.insert(type_name.clone(), ctors);
|
||||
|
||||
// Resolve methods
|
||||
let mut methods = HashMap::new();
|
||||
if let Some(body_node) = &n.body {
|
||||
if let Node::TypeBody(body) = &**body_node {
|
||||
if let Some(body_node) = n.body {
|
||||
if let NodeKind::TypeBody(body) = arena.kind(body_node) {
|
||||
for m in &body.methods {
|
||||
if let NodeKind::ServiceFnSig(sig) = arena.kind(*m) {
|
||||
let mut params = Vec::new();
|
||||
for p in &m.params {
|
||||
params.push(self.resolve_type_node(&p.ty));
|
||||
for p in &sig.params {
|
||||
params.push(self.resolve_type_node(arena, p.ty));
|
||||
}
|
||||
let m_ty = PbsType::Function {
|
||||
params,
|
||||
return_type: Box::new(self.resolve_type_node(&m.ret)),
|
||||
return_type: Box::new(self.resolve_type_node(arena, sig.ret)),
|
||||
};
|
||||
methods.insert(self.interner.resolve(m.name).to_string(), m_ty);
|
||||
methods.insert(self.interner.resolve(sig.name).to_string(), m_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,66 +171,77 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_node(&mut self, node: &Node) -> PbsType {
|
||||
match node {
|
||||
Node::FnDecl(n) => {
|
||||
self.check_fn_decl(n);
|
||||
fn check_node(&mut self, arena: &AstArena, node: NodeId) -> PbsType {
|
||||
match arena.kind(node) {
|
||||
NodeKind::FnDecl(n) => {
|
||||
self.check_fn_decl(arena, node, n);
|
||||
PbsType::Void
|
||||
}
|
||||
Node::TypeDecl(n) => {
|
||||
self.check_type_decl(n);
|
||||
NodeKind::TypeDecl(n) => {
|
||||
self.check_type_decl(arena, node, n);
|
||||
PbsType::Void
|
||||
}
|
||||
Node::ConstructorDecl(n) => {
|
||||
self.check_constructor_decl(n);
|
||||
NodeKind::ConstructorDecl(n) => {
|
||||
self.check_constructor_decl(arena, node, n);
|
||||
PbsType::Void
|
||||
}
|
||||
Node::ConstantDecl(n) => self.check_node(&n.value),
|
||||
Node::Block(n) => self.check_block(n),
|
||||
Node::LetStmt(n) => {
|
||||
self.check_let_stmt(n);
|
||||
NodeKind::ConstantDecl(n) => self.check_node(arena, n.value),
|
||||
NodeKind::Block(n) => self.check_block(arena, n),
|
||||
NodeKind::LetStmt(n) => {
|
||||
self.check_let_stmt(arena, node, n);
|
||||
PbsType::Void
|
||||
}
|
||||
Node::ExprStmt(n) => {
|
||||
self.check_node(&n.expr);
|
||||
NodeKind::ExprStmt(n) => {
|
||||
self.check_node(arena, n.expr);
|
||||
PbsType::Void
|
||||
}
|
||||
Node::ReturnStmt(n) => {
|
||||
let ret_ty = if let Some(expr) = &n.expr {
|
||||
self.check_node(expr)
|
||||
NodeKind::ReturnStmt(n) => {
|
||||
let ret_ty = if let Some(expr) = n.expr {
|
||||
self.check_node(arena, expr)
|
||||
} else {
|
||||
PbsType::Void
|
||||
};
|
||||
if let Some(expected) = self.current_return_type.clone() {
|
||||
if !self.is_assignable(&expected, &ret_ty) {
|
||||
self.error_type_mismatch(&expected, &ret_ty, n.span);
|
||||
self.error_type_mismatch(&expected, &ret_ty, arena.span(node));
|
||||
}
|
||||
}
|
||||
PbsType::Void
|
||||
}
|
||||
Node::IntLit(_) => PbsType::Int,
|
||||
Node::FloatLit(_) => PbsType::Float,
|
||||
Node::BoundedLit(_) => PbsType::Bounded,
|
||||
Node::StringLit(_) => PbsType::String,
|
||||
Node::Ident(n) => self.check_identifier(n),
|
||||
Node::Call(n) => self.check_call(n),
|
||||
Node::Unary(n) => self.check_unary(n),
|
||||
Node::Binary(n) => self.check_binary(n),
|
||||
Node::Cast(n) => self.check_cast(n),
|
||||
Node::IfExpr(n) => self.check_if_expr(n),
|
||||
Node::WhenExpr(n) => self.check_when_expr(n),
|
||||
Node::Alloc(n) => self.check_alloc(n),
|
||||
Node::Mutate(n) => self.check_hip(n.span, &n.target, n.binding, &n.body, true),
|
||||
Node::Borrow(n) => self.check_hip(n.span, &n.target, n.binding, &n.body, false),
|
||||
Node::Peek(n) => self.check_hip(n.span, &n.target, n.binding, &n.body, false),
|
||||
Node::MemberAccess(n) => self.check_member_access(n),
|
||||
NodeKind::IntLit(_) => PbsType::Int,
|
||||
NodeKind::FloatLit(_) => PbsType::Float,
|
||||
NodeKind::BoundedLit(_) => PbsType::Bounded,
|
||||
NodeKind::StringLit(_) => PbsType::String,
|
||||
NodeKind::Ident(n) => self.check_identifier(arena, node, n),
|
||||
NodeKind::Call(n) => self.check_call(arena, node, n),
|
||||
NodeKind::Unary(n) => self.check_unary(arena, node, n),
|
||||
NodeKind::Binary(n) => self.check_binary(arena, node, n),
|
||||
NodeKind::Cast(n) => self.check_cast(arena, node, n),
|
||||
NodeKind::IfExpr(n) => self.check_if_expr(arena, node, n),
|
||||
NodeKind::WhenExpr(n) => self.check_when_expr(arena, node, n),
|
||||
NodeKind::Alloc(n) => self.check_alloc(arena, node, n),
|
||||
NodeKind::Mutate(n) => {
|
||||
self.check_hip(arena, arena.span(node), n.target, n.binding, n.body, true)
|
||||
}
|
||||
NodeKind::Borrow(n) => {
|
||||
self.check_hip(arena, arena.span(node), n.target, n.binding, n.body, false)
|
||||
}
|
||||
NodeKind::Peek(n) => {
|
||||
self.check_hip(arena, arena.span(node), n.target, n.binding, n.body, false)
|
||||
}
|
||||
NodeKind::MemberAccess(n) => self.check_member_access(arena, node, n),
|
||||
_ => PbsType::Void,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_member_access(&mut self, n: &MemberAccessNode) -> PbsType {
|
||||
fn check_member_access(
|
||||
&mut self,
|
||||
arena: &AstArena,
|
||||
node: NodeId,
|
||||
n: &MemberAccessNodeArena,
|
||||
) -> PbsType {
|
||||
let member_str = self.interner.resolve(n.member);
|
||||
if let Node::Ident(id) = &*n.object {
|
||||
if let NodeKind::Ident(id) = arena.kind(n.object) {
|
||||
let name_str = self.interner.resolve(id.name);
|
||||
// Check if it's a local first
|
||||
let is_local = self.scopes.iter().any(|s| s.contains_key(name_str));
|
||||
@ -244,7 +264,7 @@ impl<'a> TypeChecker<'a> {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_RESOLVE_UNDEFINED".to_string()),
|
||||
message: format!("Method '{}' not found on host contract '{}'", member_str, name_str),
|
||||
span: Some(n.span),
|
||||
span: Some(arena.span(node)),
|
||||
});
|
||||
}
|
||||
return PbsType::Void;
|
||||
@ -284,7 +304,7 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let obj_ty = self.check_node(&n.object);
|
||||
let obj_ty = self.check_node(arena, n.object);
|
||||
if let PbsType::Struct(ref name) = obj_ty {
|
||||
if let Some(methods) = self.struct_methods.get(name) {
|
||||
if let Some(ty) = methods.get(member_str) {
|
||||
@ -355,21 +375,29 @@ impl<'a> TypeChecker<'a> {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_RESOLVE_UNDEFINED".to_string()),
|
||||
message: msg,
|
||||
span: Some(n.span),
|
||||
span: Some(arena.span(node)),
|
||||
});
|
||||
}
|
||||
PbsType::Void
|
||||
}
|
||||
|
||||
fn check_alloc(&mut self, n: &AllocNode) -> PbsType {
|
||||
let ty = self.resolve_type_node(&n.ty);
|
||||
fn check_alloc(&mut self, arena: &AstArena, _node: NodeId, n: &AllocNodeArena) -> PbsType {
|
||||
let ty = self.resolve_type_node(arena, n.ty);
|
||||
// For v0, alloc returns something that can be used with mutate/borrow/peek.
|
||||
// We'll call it a gate to the type.
|
||||
PbsType::Contract(format!("Gate<{}>", ty)) // Approximation for v0
|
||||
}
|
||||
|
||||
fn check_hip(&mut self, _span: Span, target: &Node, binding: NameId, body: &Node, is_mut: bool) -> PbsType {
|
||||
let target_ty = self.check_node(target);
|
||||
fn check_hip(
|
||||
&mut self,
|
||||
arena: &AstArena,
|
||||
_span: Span,
|
||||
target: NodeId,
|
||||
binding: NameId,
|
||||
body: NodeId,
|
||||
is_mut: bool,
|
||||
) -> PbsType {
|
||||
let target_ty = self.check_node(arena, target);
|
||||
// In v0, we assume target is a gate. We bind the inner type to the binding.
|
||||
let inner_ty = match target_ty {
|
||||
PbsType::Contract(name) if name.starts_with("Gate<") => {
|
||||
@ -389,12 +417,12 @@ impl<'a> TypeChecker<'a> {
|
||||
self.enter_scope();
|
||||
let binding_str = self.interner.resolve(binding);
|
||||
self.define_local(binding_str, inner_ty, is_mut);
|
||||
let body_ty = self.check_node(body);
|
||||
let body_ty = self.check_node(arena, body);
|
||||
self.exit_scope();
|
||||
body_ty
|
||||
}
|
||||
|
||||
fn check_fn_decl(&mut self, n: &FnDeclNode) {
|
||||
fn check_fn_decl(&mut self, arena: &AstArena, id: NodeId, n: &FnDeclNodeArena) {
|
||||
let sig = self.module_symbols.value_symbols.get(n.name).and_then(|s| s.ty.clone());
|
||||
if let Some(PbsType::Function { params, return_type }) = sig {
|
||||
self.enter_scope();
|
||||
@ -404,10 +432,10 @@ impl<'a> TypeChecker<'a> {
|
||||
self.define_local(self.interner.resolve(param.name), ty.clone(), false);
|
||||
}
|
||||
|
||||
let _body_ty = self.check_node(&n.body);
|
||||
let _body_ty = self.check_node(arena, n.body);
|
||||
|
||||
// Return path validation
|
||||
if !self.all_paths_return(&n.body) {
|
||||
if !self.all_paths_return(arena, n.body) {
|
||||
if n.else_fallback.is_some() {
|
||||
// OK
|
||||
} else if matches!(*return_type, PbsType::Optional(_)) {
|
||||
@ -423,13 +451,13 @@ impl<'a> TypeChecker<'a> {
|
||||
self.interner.resolve(n.name),
|
||||
return_type
|
||||
),
|
||||
span: Some(n.span),
|
||||
span: Some(arena.span(id)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(fallback) = &n.else_fallback {
|
||||
self.check_node(fallback);
|
||||
if let Some(fallback) = n.else_fallback {
|
||||
self.check_node(arena, fallback);
|
||||
}
|
||||
|
||||
self.current_return_type = None;
|
||||
@ -437,13 +465,13 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_block(&mut self, n: &BlockNode) -> PbsType {
|
||||
fn check_block(&mut self, arena: &AstArena, n: &BlockNodeArena) -> PbsType {
|
||||
self.enter_scope();
|
||||
for stmt in &n.stmts {
|
||||
self.check_node(stmt);
|
||||
self.check_node(arena, *stmt);
|
||||
}
|
||||
let tail_ty = if let Some(tail) = &n.tail {
|
||||
self.check_node(tail)
|
||||
let tail_ty = if let Some(tail) = n.tail {
|
||||
self.check_node(arena, tail)
|
||||
} else {
|
||||
PbsType::Void
|
||||
};
|
||||
@ -451,13 +479,13 @@ impl<'a> TypeChecker<'a> {
|
||||
tail_ty
|
||||
}
|
||||
|
||||
fn check_let_stmt(&mut self, n: &LetStmtNode) {
|
||||
let init_ty = self.check_node(&n.init);
|
||||
let declared_ty = n.ty.as_ref().map(|t| self.resolve_type_node(t));
|
||||
fn check_let_stmt(&mut self, arena: &AstArena, id: NodeId, n: &LetStmtNodeArena) {
|
||||
let init_ty = self.check_node(arena, n.init);
|
||||
let declared_ty = n.ty.map(|t| self.resolve_type_node(arena, t));
|
||||
|
||||
let final_ty = if let Some(dty) = declared_ty {
|
||||
if !self.is_assignable(&dty, &init_ty) {
|
||||
self.error_type_mismatch(&dty, &init_ty, n.span);
|
||||
self.error_type_mismatch(&dty, &init_ty, arena.span(id));
|
||||
}
|
||||
dty
|
||||
} else {
|
||||
@ -467,7 +495,7 @@ impl<'a> TypeChecker<'a> {
|
||||
self.define_local(self.interner.resolve(n.name), final_ty, n.is_mut);
|
||||
}
|
||||
|
||||
fn check_identifier(&mut self, n: &IdentNode) -> PbsType {
|
||||
fn check_identifier(&mut self, _arena: &AstArena, _id: NodeId, n: &IdentNodeArena) -> PbsType {
|
||||
let name_str = self.interner.resolve(n.name);
|
||||
// Check locals
|
||||
for scope in self.scopes.iter().rev() {
|
||||
@ -510,27 +538,27 @@ impl<'a> TypeChecker<'a> {
|
||||
PbsType::Void
|
||||
}
|
||||
|
||||
fn check_call(&mut self, n: &CallNode) -> PbsType {
|
||||
let callee_ty = self.check_node(&n.callee);
|
||||
fn check_call(&mut self, arena: &AstArena, node: NodeId, n: &CallNodeArena) -> PbsType {
|
||||
let callee_ty = self.check_node(arena, n.callee);
|
||||
|
||||
// Handle special built-in "constructors"
|
||||
if let Node::Ident(id) = &*n.callee {
|
||||
if let NodeKind::Ident(id) = arena.kind(n.callee) {
|
||||
match self.interner.resolve(id.name) {
|
||||
"some" => {
|
||||
if n.args.len() == 1 {
|
||||
let inner_ty = self.check_node(&n.args[0]);
|
||||
let inner_ty = self.check_node(arena, n.args[0]);
|
||||
return PbsType::Optional(Box::new(inner_ty));
|
||||
}
|
||||
}
|
||||
"ok" => {
|
||||
if n.args.len() == 1 {
|
||||
let inner_ty = self.check_node(&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
|
||||
}
|
||||
}
|
||||
"err" => {
|
||||
if n.args.len() == 1 {
|
||||
let inner_ty = self.check_node(&n.args[0]);
|
||||
let inner_ty = self.check_node(arena, n.args[0]);
|
||||
return PbsType::Result(Box::new(PbsType::Void), Box::new(inner_ty));
|
||||
}
|
||||
}
|
||||
@ -545,13 +573,13 @@ impl<'a> TypeChecker<'a> {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_TYPE_MISMATCH".to_string()),
|
||||
message: format!("Expected {} arguments, found {}", params.len(), n.args.len()),
|
||||
span: Some(n.span),
|
||||
span: Some(arena.span(node)),
|
||||
});
|
||||
} else {
|
||||
for (i, arg) in n.args.iter().enumerate() {
|
||||
let arg_ty = self.check_node(arg);
|
||||
let arg_ty = self.check_node(arena, *arg);
|
||||
if !self.is_assignable(¶ms[i], &arg_ty) {
|
||||
self.error_type_mismatch(¶ms[i], &arg_ty, arg.span());
|
||||
self.error_type_mismatch(¶ms[i], &arg_ty, arena.span(*arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -563,7 +591,7 @@ impl<'a> TypeChecker<'a> {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_TYPE_MISMATCH".to_string()),
|
||||
message: format!("Type {} is not callable", callee_ty),
|
||||
span: Some(n.span),
|
||||
span: Some(arena.span(node)),
|
||||
});
|
||||
}
|
||||
PbsType::Void
|
||||
@ -571,14 +599,14 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_unary(&mut self, n: &UnaryNode) -> PbsType {
|
||||
let expr_ty = self.check_node(&n.expr);
|
||||
fn check_unary(&mut self, arena: &AstArena, node: NodeId, n: &UnaryNodeArena) -> PbsType {
|
||||
let expr_ty = self.check_node(arena, n.expr);
|
||||
match n.op.as_str() {
|
||||
"-" => {
|
||||
if expr_ty == PbsType::Int || expr_ty == PbsType::Float {
|
||||
expr_ty
|
||||
} else {
|
||||
self.error_type_mismatch(&PbsType::Int, &expr_ty, n.span);
|
||||
self.error_type_mismatch(&PbsType::Int, &expr_ty, arena.span(node));
|
||||
PbsType::Void
|
||||
}
|
||||
}
|
||||
@ -586,7 +614,7 @@ impl<'a> TypeChecker<'a> {
|
||||
if expr_ty == PbsType::Bool {
|
||||
PbsType::Bool
|
||||
} else {
|
||||
self.error_type_mismatch(&PbsType::Bool, &expr_ty, n.span);
|
||||
self.error_type_mismatch(&PbsType::Bool, &expr_ty, arena.span(node));
|
||||
PbsType::Void
|
||||
}
|
||||
}
|
||||
@ -594,16 +622,16 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_binary(&mut self, n: &BinaryNode) -> PbsType {
|
||||
let left_ty = self.check_node(&n.left);
|
||||
let right_ty = self.check_node(&n.right);
|
||||
fn check_binary(&mut self, arena: &AstArena, node: NodeId, n: &BinaryNodeArena) -> PbsType {
|
||||
let left_ty = self.check_node(arena, n.left);
|
||||
let right_ty = self.check_node(arena, n.right);
|
||||
|
||||
match n.op.as_str() {
|
||||
"+" | "-" | "*" | "/" | "%" => {
|
||||
if (left_ty == PbsType::Int || left_ty == PbsType::Float) && left_ty == right_ty {
|
||||
left_ty
|
||||
} else {
|
||||
self.error_type_mismatch(&left_ty, &right_ty, n.span);
|
||||
self.error_type_mismatch(&left_ty, &right_ty, arena.span(node));
|
||||
PbsType::Void
|
||||
}
|
||||
}
|
||||
@ -611,7 +639,7 @@ impl<'a> TypeChecker<'a> {
|
||||
if left_ty == right_ty {
|
||||
PbsType::Bool
|
||||
} else {
|
||||
self.error_type_mismatch(&left_ty, &right_ty, n.span);
|
||||
self.error_type_mismatch(&left_ty, &right_ty, arena.span(node));
|
||||
PbsType::Bool
|
||||
}
|
||||
}
|
||||
@ -619,7 +647,7 @@ impl<'a> TypeChecker<'a> {
|
||||
if (left_ty == PbsType::Int || left_ty == PbsType::Float) && left_ty == right_ty {
|
||||
PbsType::Bool
|
||||
} else {
|
||||
self.error_type_mismatch(&left_ty, &right_ty, n.span);
|
||||
self.error_type_mismatch(&left_ty, &right_ty, arena.span(node));
|
||||
PbsType::Bool
|
||||
}
|
||||
}
|
||||
@ -627,8 +655,8 @@ impl<'a> TypeChecker<'a> {
|
||||
if left_ty == PbsType::Bool && right_ty == PbsType::Bool {
|
||||
PbsType::Bool
|
||||
} else {
|
||||
self.error_type_mismatch(&PbsType::Bool, &left_ty, n.left.span());
|
||||
self.error_type_mismatch(&PbsType::Bool, &right_ty, n.right.span());
|
||||
self.error_type_mismatch(&PbsType::Bool, &left_ty, arena.span(n.left));
|
||||
self.error_type_mismatch(&PbsType::Bool, &right_ty, arena.span(n.right));
|
||||
PbsType::Bool
|
||||
}
|
||||
}
|
||||
@ -636,23 +664,23 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_cast(&mut self, n: &CastNode) -> PbsType {
|
||||
let _expr_ty = self.check_node(&n.expr);
|
||||
let target_ty = self.resolve_type_node(&n.ty);
|
||||
fn check_cast(&mut self, arena: &AstArena, _node: NodeId, n: &CastNodeArena) -> PbsType {
|
||||
let _expr_ty = self.check_node(arena, n.expr);
|
||||
let target_ty = self.resolve_type_node(arena, n.ty);
|
||||
// Minimal cast validation for v0
|
||||
target_ty
|
||||
}
|
||||
|
||||
fn check_if_expr(&mut self, n: &IfExprNode) -> PbsType {
|
||||
let cond_ty = self.check_node(&n.cond);
|
||||
fn check_if_expr(&mut self, arena: &AstArena, node: NodeId, n: &IfExprNodeArena) -> PbsType {
|
||||
let cond_ty = self.check_node(arena, n.cond);
|
||||
if cond_ty != PbsType::Bool {
|
||||
self.error_type_mismatch(&PbsType::Bool, &cond_ty, n.cond.span());
|
||||
self.error_type_mismatch(&PbsType::Bool, &cond_ty, arena.span(n.cond));
|
||||
}
|
||||
let then_ty = self.check_node(&n.then_block);
|
||||
if let Some(else_block) = &n.else_block {
|
||||
let else_ty = self.check_node(else_block);
|
||||
let then_ty = self.check_node(arena, n.then_block);
|
||||
if let Some(else_block) = n.else_block {
|
||||
let else_ty = self.check_node(arena, else_block);
|
||||
if then_ty != else_ty {
|
||||
self.error_type_mismatch(&then_ty, &else_ty, n.span);
|
||||
self.error_type_mismatch(&then_ty, &else_ty, arena.span(node));
|
||||
}
|
||||
then_ty
|
||||
} else {
|
||||
@ -660,20 +688,20 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_when_expr(&mut self, n: &WhenExprNode) -> PbsType {
|
||||
fn check_when_expr(&mut self, arena: &AstArena, _node: NodeId, n: &WhenExprNodeArena) -> PbsType {
|
||||
let mut first_ty = None;
|
||||
for arm in &n.arms {
|
||||
if let Node::WhenArm(arm_node) = arm {
|
||||
let cond_ty = self.check_node(&arm_node.cond);
|
||||
if let NodeKind::WhenArm(arm_node) = arena.kind(*arm) {
|
||||
let cond_ty = self.check_node(arena, arm_node.cond);
|
||||
if cond_ty != PbsType::Bool {
|
||||
self.error_type_mismatch(&PbsType::Bool, &cond_ty, arm_node.cond.span());
|
||||
self.error_type_mismatch(&PbsType::Bool, &cond_ty, arena.span(arm_node.cond));
|
||||
}
|
||||
let body_ty = self.check_node(&arm_node.body);
|
||||
let body_ty = self.check_node(arena, arm_node.body);
|
||||
if first_ty.is_none() {
|
||||
first_ty = Some(body_ty);
|
||||
} else if let Some(fty) = &first_ty {
|
||||
if *fty != body_ty {
|
||||
self.error_type_mismatch(fty, &body_ty, arm_node.body.span());
|
||||
self.error_type_mismatch(fty, &body_ty, arena.span(arm_node.body));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -681,9 +709,11 @@ impl<'a> TypeChecker<'a> {
|
||||
first_ty.unwrap_or(PbsType::Void)
|
||||
}
|
||||
|
||||
fn check_type_decl(&mut self, n: &TypeDeclNode) {
|
||||
fn check_type_decl(&mut self, arena: &AstArena, _id: NodeId, n: &TypeDeclNodeArena) {
|
||||
for constructor in &n.constructors {
|
||||
self.check_constructor_decl(constructor);
|
||||
if let NodeKind::ConstructorDecl(ctor) = arena.kind(*constructor) {
|
||||
self.check_constructor_decl(arena, *constructor, ctor);
|
||||
}
|
||||
}
|
||||
|
||||
let type_name = self.interner.resolve(n.name).to_string();
|
||||
@ -697,37 +727,47 @@ impl<'a> TypeChecker<'a> {
|
||||
|
||||
let mut constants_map = HashMap::new();
|
||||
self.scopes.push(constants_scope);
|
||||
for constant in &n.constants {
|
||||
let val_ty = self.check_node(&constant.value);
|
||||
for constant_id in &n.constants {
|
||||
if let NodeKind::ConstantDecl(constant) = arena.kind(*constant_id) {
|
||||
let val_ty = self.check_node(arena, constant.value);
|
||||
if !self.is_assignable(&struct_ty, &val_ty) {
|
||||
self.error_type_mismatch(&struct_ty, &val_ty, constant.span);
|
||||
self.error_type_mismatch(&struct_ty, &val_ty, arena.span(*constant_id));
|
||||
}
|
||||
constants_map.insert(
|
||||
self.interner.resolve(constant.name).to_string(),
|
||||
struct_ty.clone(),
|
||||
);
|
||||
}
|
||||
constants_map.insert(self.interner.resolve(constant.name).to_string(), struct_ty.clone());
|
||||
}
|
||||
self.scopes.pop();
|
||||
self.struct_constants.insert(type_name, constants_map);
|
||||
|
||||
if let Some(body) = &n.body {
|
||||
self.check_node(body);
|
||||
if let Some(body) = n.body {
|
||||
self.check_node(arena, body);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_constructor_decl(&mut self, n: &ConstructorDeclNode) {
|
||||
fn check_constructor_decl(
|
||||
&mut self,
|
||||
arena: &AstArena,
|
||||
_id: NodeId,
|
||||
n: &ConstructorDeclNodeArena,
|
||||
) {
|
||||
self.enter_scope();
|
||||
for param in &n.params {
|
||||
let ty = self.resolve_type_node(¶m.ty);
|
||||
let ty = self.resolve_type_node(arena, param.ty);
|
||||
self.define_local(self.interner.resolve(param.name), ty, false);
|
||||
}
|
||||
for init in &n.initializers {
|
||||
self.check_node(init);
|
||||
self.check_node(arena, *init);
|
||||
}
|
||||
self.check_node(&n.body);
|
||||
self.check_node(arena, n.body);
|
||||
self.exit_scope();
|
||||
}
|
||||
|
||||
fn resolve_type_node(&mut self, node: &Node) -> PbsType {
|
||||
match node {
|
||||
Node::TypeName(tn) => {
|
||||
fn resolve_type_node(&mut self, arena: &AstArena, node: NodeId) -> PbsType {
|
||||
match arena.kind(node) {
|
||||
NodeKind::TypeName(tn) => {
|
||||
let name_str = self.interner.resolve(tn.name);
|
||||
match name_str {
|
||||
"int" => PbsType::Int,
|
||||
@ -752,18 +792,17 @@ impl<'a> TypeChecker<'a> {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_TYPE_UNKNOWN_TYPE".to_string()),
|
||||
message: format!("Unknown type: {}", name_str),
|
||||
span: Some(tn.span),
|
||||
span: Some(arena.span(node)),
|
||||
});
|
||||
PbsType::Void
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Node::TypeApp(ta) => {
|
||||
match self.interner.resolve(ta.base) {
|
||||
NodeKind::TypeApp(ta) => match self.interner.resolve(ta.base) {
|
||||
"optional" => {
|
||||
if ta.args.len() == 1 {
|
||||
PbsType::Optional(Box::new(self.resolve_type_node(&ta.args[0])))
|
||||
PbsType::Optional(Box::new(self.resolve_type_node(arena, ta.args[0])))
|
||||
} else {
|
||||
PbsType::Void
|
||||
}
|
||||
@ -771,16 +810,15 @@ impl<'a> TypeChecker<'a> {
|
||||
"result" => {
|
||||
if ta.args.len() == 2 {
|
||||
PbsType::Result(
|
||||
Box::new(self.resolve_type_node(&ta.args[0])),
|
||||
Box::new(self.resolve_type_node(&ta.args[1])),
|
||||
Box::new(self.resolve_type_node(arena, ta.args[0])),
|
||||
Box::new(self.resolve_type_node(arena, ta.args[1])),
|
||||
)
|
||||
} else {
|
||||
PbsType::Void
|
||||
}
|
||||
}
|
||||
_ => PbsType::Void,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => PbsType::Void,
|
||||
}
|
||||
}
|
||||
@ -826,23 +864,26 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn all_paths_return(&self, node: &Node) -> bool {
|
||||
match node {
|
||||
Node::ReturnStmt(_) => true,
|
||||
Node::Block(n) => {
|
||||
fn all_paths_return(&self, arena: &AstArena, node: NodeId) -> bool {
|
||||
match arena.kind(node) {
|
||||
NodeKind::ReturnStmt(_) => true,
|
||||
NodeKind::Block(n) => {
|
||||
for stmt in &n.stmts {
|
||||
if self.all_paths_return(stmt) {
|
||||
if self.all_paths_return(arena, *stmt) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(tail) = &n.tail {
|
||||
return self.all_paths_return(tail);
|
||||
if let Some(tail) = n.tail {
|
||||
return self.all_paths_return(arena, tail);
|
||||
}
|
||||
false
|
||||
}
|
||||
Node::IfExpr(n) => {
|
||||
let then_returns = self.all_paths_return(&n.then_block);
|
||||
let else_returns = n.else_block.as_ref().map(|b| self.all_paths_return(b)).unwrap_or(false);
|
||||
NodeKind::IfExpr(n) => {
|
||||
let then_returns = self.all_paths_return(arena, n.then_block);
|
||||
let else_returns = n
|
||||
.else_block
|
||||
.map(|b| self.all_paths_return(arena, b))
|
||||
.unwrap_or(false);
|
||||
then_returns && else_returns
|
||||
}
|
||||
// For simplicity, we don't assume When returns unless all arms do
|
||||
|
||||
@ -132,10 +132,11 @@ pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Resul
|
||||
let file_id = file_manager.add(file_path.clone(), source.clone());
|
||||
|
||||
let mut parser = Parser::new(&source, file_id, &mut interner);
|
||||
let ast = parser.parse_file()?;
|
||||
let parsed = parser.parse_file()?;
|
||||
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (type_symbols, value_symbols) = collector.collect(&ast)?;
|
||||
let (type_symbols, value_symbols) =
|
||||
collector.collect(&parsed.arena, parsed.root)?;
|
||||
|
||||
// Merge only public symbols
|
||||
for symbol in type_symbols.symbols.into_values() {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use prometeu_bytecode::disasm::disasm;
|
||||
use prometeu_bytecode::BytecodeLoader;
|
||||
use prometeu_compiler::compiler::compile;
|
||||
use prometeu_compiler::frontends::pbs::ast::Node;
|
||||
use prometeu_compiler::frontends::pbs::parser::Parser;
|
||||
use prometeu_analysis::NameInterner;
|
||||
use std::fs;
|
||||
@ -59,9 +58,8 @@ fn generate_canonical_goldens() {
|
||||
let source = fs::read_to_string(project_dir.join("src/main/modules/main.pbs")).unwrap();
|
||||
let mut interner = NameInterner::new();
|
||||
let mut parser = Parser::new(&source, 0, &mut interner);
|
||||
let ast = parser.parse_file().expect("Failed to parse AST");
|
||||
let ast_node = Node::File(ast);
|
||||
let ast_json = serde_json::to_string_pretty(&ast_node).unwrap();
|
||||
let parsed = parser.parse_file().expect("Failed to parse AST");
|
||||
let ast_json = serde_json::to_string_pretty(parsed.arena.kind(parsed.root)).unwrap();
|
||||
fs::write(golden_dir.join("ast.json"), ast_json).unwrap();
|
||||
|
||||
println!("Golden artifacts generated in test-cartridges/canonical/golden/");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user