Files
bux-lang/src/sema.bux
T
dimgigov d70ccba5ca selfhost: implicit generic type argument inference for Array/Iter stdlib functions
- sema: add Sema_InferGenericArg + Sema_ExtractElemType helpers
- Infers T from first argument's collection type:
  - Array_Push(&arr, 10) → Array_Push_int (from &arr's Array<int> type)
  - Iter_HasNext(&it) → Iter_HasNext_int (from &it's Iter<int> type)
- Supports both explicit generic types (Array<int>) and mangled types (Array_int)
- Works with reference expressions (&x) by unwrapping to x's type
- Selfhost loop: C output IDENTICAL
2026-06-10 10:26:08 +03:00

1434 lines
56 KiB
Plaintext

// sema.bux — Semantic analysis (type checker, ported from sema.nim)
// Validates types, resolves identifiers, checks function calls.
module Sema {
extern func bux_string_concat(a: String, b: String) -> String;
extern func bux_int_to_str(n: int64) -> String;
extern func bux_strlen(s: String) -> uint;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
// ---------------------------------------------------------------------------
// Sema context
// ---------------------------------------------------------------------------
struct Sema {
module: *Module;
scope: *Scope;
typeTable: *void;
methodTable: *void;
diagCount: int;
diags: *SemaDiag;
hasError: bool;
currentRetType: int; // return type of the function being checked
checkedFunc: bool; // true inside @[Checked] function
movedCount: int; // number of moved variables
movedName0: String; // inline moved var names (up to 8)
movedName1: String;
movedName2: String;
movedName3: String;
movedName4: String;
movedName5: String;
movedName6: String;
movedName7: String;
closureDepth: int; // nesting depth inside closures
currentClosureExpr: *Expr; // current closure being analyzed (for capture tracking)
closureScope: *Scope; // scope at which the current closure was entered
// Trait bounds tables
interfaceTable: *InterfaceEntry;
interfaceCount: int;
methodEntries: *MethodEntry;
methodCount: int;
}
struct SemaDiag {
line: uint32;
column: uint32;
message: String;
}
// ---------------------------------------------------------------------------
// Interface / Method tables for trait bounds checking
// ---------------------------------------------------------------------------
struct InterfaceEntry {
name: String,
decl: *Decl,
}
struct MethodEntry {
typeName: String,
methodName: String,
decl: *Decl,
}
// ---------------------------------------------------------------------------
// Diagnostics
// ---------------------------------------------------------------------------
func Sema_EmitError(sema: *Sema, line: uint32, col: uint32, msg: String) {
if sema.diagCount < 256 {
sema.diags[sema.diagCount] = SemaDiag { line: line, column: col, message: msg };
sema.diagCount = sema.diagCount + 1;
}
sema.hasError = true;
}
// ---------------------------------------------------------------------------
// Symbol zero-init helper (bootstrap C backend does not zero-init structs)
// ---------------------------------------------------------------------------
func Sema_ZeroInitSymbol(sym: *Symbol) {
sym.kind = 0;
sym.name = "";
sym.typeKind = 0;
sym.typeName = "";
sym.refType = null as *TypeExpr;
sym.isMutable = false;
sym.isPublic = false;
sym.decl = null as *Decl;
}
// ---------------------------------------------------------------------------
// Build a tekFunc TypeExpr from a function declaration
// ---------------------------------------------------------------------------
func Sema_BuildFuncTypeExprFromDecl(decl: *Decl) -> *TypeExpr {
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
te.kind = tekFunc;
te.line = decl.line;
te.column = decl.column;
te.funcRet = decl.retType;
te.funcParamCount = decl.paramCount;
var head: *TypeExprList = null as *TypeExprList;
var tail: *TypeExprList = null as *TypeExprList;
var i: int = 0;
while i < decl.paramCount && i < 9 {
var p: Param;
if i == 0 { p = decl.param0; }
else if i == 1 { p = decl.param1; }
else if i == 2 { p = decl.param2; }
else if i == 3 { p = decl.param3; }
else if i == 4 { p = decl.param4; }
else if i == 5 { p = decl.param5; }
else if i == 6 { p = decl.param6; }
else if i == 7 { p = decl.param7; }
else { p = decl.param8; }
if p.refParamType != null as *TypeExpr {
let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList;
node.te = p.refParamType;
node.next = null as *TypeExprList;
if head == null as *TypeExprList {
head = node;
} else {
tail.next = node;
}
tail = node;
}
i = i + 1;
}
te.funcParams = head;
return te;
}
// ---------------------------------------------------------------------------
// Type resolution from TypeExpr → Type constants
// ---------------------------------------------------------------------------
func Sema_ResolveType(sema: *Sema, te: *TypeExpr) -> int {
if te == null as *TypeExpr { return tyUnknown; }
let name: String = te.typeName;
if te.kind == tekPointer {
return tyPointer;
}
if te.kind == tekFunc {
return tyFunc;
}
if String_Eq(name, "void") { return tyVoid; }
if String_Eq(name, "bool") { return tyBool; }
if String_Eq(name, "bool8") { return tyBool8; }
if String_Eq(name, "bool16") { return tyBool16; }
if String_Eq(name, "bool32") { return tyBool32; }
if String_Eq(name, "char8") { return tyChar8; }
if String_Eq(name, "char16") { return tyChar16; }
if String_Eq(name, "char32") { return tyChar32; }
if String_Eq(name, "String") { return tyStr; }
if String_Eq(name, "str") { return tyStr; }
if String_Eq(name, "int8") { return tyInt8; }
if String_Eq(name, "int16") { return tyInt16; }
if String_Eq(name, "int32") { return tyInt32; }
if String_Eq(name, "int64") { return tyInt64; }
if String_Eq(name, "int") { return tyInt; }
if String_Eq(name, "uint8") { return tyUInt8; }
if String_Eq(name, "uint16") { return tyUInt16; }
if String_Eq(name, "uint32") { return tyUInt32; }
if String_Eq(name, "uint64") { return tyUInt64; }
if String_Eq(name, "uint") { return tyUInt; }
if String_Eq(name, "float32") { return tyFloat32; }
if String_Eq(name, "float64") { return tyFloat64; }
if String_Eq(name, "float") { return tyFloat64; }
// Type resolution uses scope-based lookup via Sema_CollectGlobals
// TODO: add StringMap-based type table for faster named-type validation
return tyNamed;
}
// ---------------------------------------------------------------------------
// Type predicates
// ---------------------------------------------------------------------------
func Sema_IsNumeric(kind: int) -> bool {
if kind == tyUnknown || kind == tyNamed || kind == tyTypeParam { return true; }
if kind == tyInt8 || kind == tyInt16 || kind == tyInt32 || kind == tyInt64 || kind == tyInt { return true; }
if kind == tyUInt8 || kind == tyUInt16 || kind == tyUInt32 || kind == tyUInt64 || kind == tyUInt { return true; }
if kind == tyFloat32 || kind == tyFloat64 { return true; }
return false;
}
func Sema_IsBool(kind: int) -> bool {
return kind == tyBool || kind == tyBool8 || kind == tyBool16 || kind == tyBool32;
}
// ---------------------------------------------------------------------------
// Block checking helper
// ---------------------------------------------------------------------------
func Sema_CheckBlock(sema: *Sema, block: *Block) {
if block == null as *Block { return; }
// Create child scope for block (matching Nim bootstrap behavior)
var blockScope: Scope = Scope_NewChild(sema.scope);
let prevScope: *Scope = sema.scope;
sema.scope = &blockScope;
var stmt: *Stmt = block.firstStmt;
while stmt != null as *Stmt {
Sema_CheckStmt(sema, stmt);
stmt = stmt.nextStmt;
}
sema.scope = prevScope;
}
// ---------------------------------------------------------------------------
// Call argument resolution: inject defaults and reorder named args
// ---------------------------------------------------------------------------
func Sema_ResolveCallArgs(sema: *Sema, expr: *Expr) {
if expr == null as *Expr || expr.kind != ekCall { return; }
if expr.child1 == null as *Expr || expr.child1.kind != ekIdent { return; }
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
if sym.kind != skFunc || sym.decl == null as *Decl { return; }
let decl: *Decl = sym.decl;
if decl.paramCount == 0 { return; }
// Check if any named args present
var hasNamed: bool = false;
var arg: *ExprList = expr.callArgs;
while arg != null as *ExprList {
if !String_Eq(arg.argName, "") { hasNamed = true; }
arg = arg.next;
}
if !hasNamed && expr.callArgCount >= decl.paramCount { return; }
// Build new linked list in param order
var newFirst: *ExprList = null as *ExprList;
var newLast: *ExprList = null as *ExprList;
var newCount: int = 0;
var usedPositional: int = 0;
var positionalAfterNamed: bool = false;
var i: int = 0;
while i < decl.paramCount {
var p: *Param = null as *Param;
if i == 0 { p = &decl.param0; }
else if i == 1 { p = &decl.param1; }
else if i == 2 { p = &decl.param2; }
else if i == 3 { p = &decl.param3; }
else if i == 4 { p = &decl.param4; }
else if i == 5 { p = &decl.param5; }
else if i == 6 { p = &decl.param6; }
else if i == 7 { p = &decl.param7; }
else if i == 8 { p = &decl.param8; }
var matched: *Expr = null as *Expr;
// Look for positional arg at this index
if usedPositional < expr.callArgCount {
var posIdx: int = 0;
var posArg: *ExprList = expr.callArgs;
while posArg != null as *ExprList {
if String_Eq(posArg.argName, "") {
if posIdx == usedPositional {
matched = posArg.expr;
usedPositional = usedPositional + 1;
break;
}
posIdx = posIdx + 1;
}
posArg = posArg.next;
}
}
// Look for named arg matching this param
if matched == null as *Expr {
var namedArg: *ExprList = expr.callArgs;
while namedArg != null as *ExprList {
if String_Eq(namedArg.argName, p.name) {
matched = namedArg.expr;
break;
}
namedArg = namedArg.next;
}
}
// Use default if available
if matched == null as *Expr && p.defaultExpr != null as *Expr {
matched = p.defaultExpr;
}
if matched != null as *Expr {
let node: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList;
node.expr = matched;
node.next = null as *ExprList;
node.argName = "";
if newFirst == null as *ExprList {
newFirst = node;
newLast = node;
} else {
newLast.next = node;
newLast = node;
}
newCount = newCount + 1;
}
i = i + 1;
}
expr.callArgs = newFirst;
expr.callArgCount = newCount;
}
// ---------------------------------------------------------------------------
// Borrow checker helpers (inline array, matching Decl param pattern)
// ---------------------------------------------------------------------------
func Sema_AddMoved(sema: *Sema, name: String) {
if sema == null as *Sema { return; }
if sema.movedCount >= 8 { return; }
if sema.movedCount == 0 { sema.movedName0 = name; }
else if sema.movedCount == 1 { sema.movedName1 = name; }
else if sema.movedCount == 2 { sema.movedName2 = name; }
else if sema.movedCount == 3 { sema.movedName3 = name; }
else if sema.movedCount == 4 { sema.movedName4 = name; }
else if sema.movedCount == 5 { sema.movedName5 = name; }
else if sema.movedCount == 6 { sema.movedName6 = name; }
else if sema.movedCount == 7 { sema.movedName7 = name; }
sema.movedCount = sema.movedCount + 1;
}
func Sema_IsMoved(sema: *Sema, name: String) -> bool {
if sema == null as *Sema { return false; }
if sema.movedCount > 0 && String_Eq(sema.movedName0, name) { return true; }
if sema.movedCount > 1 && String_Eq(sema.movedName1, name) { return true; }
if sema.movedCount > 2 && String_Eq(sema.movedName2, name) { return true; }
if sema.movedCount > 3 && String_Eq(sema.movedName3, name) { return true; }
if sema.movedCount > 4 && String_Eq(sema.movedName4, name) { return true; }
if sema.movedCount > 5 && String_Eq(sema.movedName5, name) { return true; }
if sema.movedCount > 6 && String_Eq(sema.movedName6, name) { return true; }
if sema.movedCount > 7 && String_Eq(sema.movedName7, name) { return true; }
return false;
}
func Sema_RemoveMoved(sema: *Sema, name: String) {
if sema == null as *Sema { return; }
var found: int = -1;
if sema.movedCount > 0 && String_Eq(sema.movedName0, name) { found = 0; }
else if sema.movedCount > 1 && String_Eq(sema.movedName1, name) { found = 1; }
else if sema.movedCount > 2 && String_Eq(sema.movedName2, name) { found = 2; }
else if sema.movedCount > 3 && String_Eq(sema.movedName3, name) { found = 3; }
else if sema.movedCount > 4 && String_Eq(sema.movedName4, name) { found = 4; }
else if sema.movedCount > 5 && String_Eq(sema.movedName5, name) { found = 5; }
else if sema.movedCount > 6 && String_Eq(sema.movedName6, name) { found = 6; }
else if sema.movedCount > 7 && String_Eq(sema.movedName7, name) { found = 7; }
if found >= 0 {
var i: int = found;
while i < sema.movedCount - 1 {
if i == 0 { sema.movedName0 = sema.movedName1; }
else if i == 1 { sema.movedName1 = sema.movedName2; }
else if i == 2 { sema.movedName2 = sema.movedName3; }
else if i == 3 { sema.movedName3 = sema.movedName4; }
else if i == 4 { sema.movedName4 = sema.movedName5; }
else if i == 5 { sema.movedName5 = sema.movedName6; }
else if i == 6 { sema.movedName6 = sema.movedName7; }
i = i + 1;
}
sema.movedCount = sema.movedCount - 1;
}
}
// ---------------------------------------------------------------------------
// Capture tracking for closures
// ---------------------------------------------------------------------------
func Sema_AddCapture(closureExpr: *Expr, name: String, typeKind: int) {
if closureExpr == null as *Expr { return; }
// Check if already captured
var i: int = 0;
while i < closureExpr.captureCount {
var capName: String = "";
if i == 0 { capName = closureExpr.captureName0; }
else if i == 1 { capName = closureExpr.captureName1; }
else if i == 2 { capName = closureExpr.captureName2; }
else if i == 3 { capName = closureExpr.captureName3; }
else if i == 4 { capName = closureExpr.captureName4; }
else if i == 5 { capName = closureExpr.captureName5; }
else if i == 6 { capName = closureExpr.captureName6; }
else if i == 7 { capName = closureExpr.captureName7; }
if String_Eq(capName, name) { return; }
i = i + 1;
}
// Add new capture (up to 8)
if closureExpr.captureCount < 8 {
let idx: int = closureExpr.captureCount;
if idx == 0 { closureExpr.captureName0 = name; closureExpr.captureType0 = typeKind; }
else if idx == 1 { closureExpr.captureName1 = name; closureExpr.captureType1 = typeKind; }
else if idx == 2 { closureExpr.captureName2 = name; closureExpr.captureType2 = typeKind; }
else if idx == 3 { closureExpr.captureName3 = name; closureExpr.captureType3 = typeKind; }
else if idx == 4 { closureExpr.captureName4 = name; closureExpr.captureType4 = typeKind; }
else if idx == 5 { closureExpr.captureName5 = name; closureExpr.captureType5 = typeKind; }
else if idx == 6 { closureExpr.captureName6 = name; closureExpr.captureType6 = typeKind; }
else if idx == 7 { closureExpr.captureName7 = name; closureExpr.captureType7 = typeKind; }
closureExpr.captureCount = closureExpr.captureCount + 1;
}
}
// ---------------------------------------------------------------------------
// Expression type checking
// ---------------------------------------------------------------------------
func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
if expr == null as *Expr { return tyUnknown; }
let kind: int = expr.kind;
// Literal
if kind == ekLiteral {
let tk: int = expr.tokKind;
if tk == tkIntLiteral { return tyInt; }
if tk == tkFloatLiteral { return tyFloat64; }
if tk == tkStringLiteral { return tyStr; }
if tk == tkBoolLiteral { return tyBool; }
if tk == tkCharLiteral { return tyChar32; }
if tk == tkNull { return tyPointer; }
return tyUnknown;
}
// Identifier — look up in scope
if kind == ekIdent {
// Borrow check: use-after-move in @[Checked] functions
if sema.checkedFunc && Sema_IsMoved(sema, expr.strValue) {
Sema_EmitError(sema, expr.line, expr.column,
String_Concat("use of moved value '", String_Concat(expr.strValue, "'")));
}
let sym: Symbol = Scope_Lookup(sema.scope, expr.strValue);
if sym.kind == 0 && !String_Eq(sym.name, expr.strValue) {
let errMsg: String = String_Concat("undeclared identifier '", expr.strValue);
let errMsg2: String = String_Concat(errMsg, "'");
Sema_EmitError(sema, expr.line, expr.column, errMsg2);
return tyUnknown;
}
if sym.refType != null as *TypeExpr {
expr.refType = sym.refType;
} else if sym.typeName != null as String && !String_Eq(sym.typeName, "") {
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
te.kind = tekNamed;
te.typeName = sym.typeName;
expr.refType = te;
}
// Capture tracking: if inside closure and identifier is not local but from parent scope
if sema.closureDepth > 0 && sema.currentClosureExpr != null as *Expr && sema.closureScope != null as *Scope {
let localSym: Symbol = Scope_LookupUpTo(sema.scope, expr.strValue, sema.closureScope);
if localSym.kind == 0 && !String_Eq(localSym.name, expr.strValue) {
// Not local to closure scope — check if it's a variable from outer scope
if sym.kind == skVar {
Sema_AddCapture(sema.currentClosureExpr, expr.strValue, sym.typeKind);
}
}
}
return sym.typeKind;
}
// self
if kind == ekSelf {
let sym: Symbol = Scope_Lookup(sema.scope, "self");
if sym.kind == 0 && !String_Eq(sym.name, "self") {
Sema_EmitError(sema, expr.line, expr.column, "self outside method");
return tyUnknown;
}
return sym.typeKind;
}
// Binary
if kind == ekBinary {
let left: int = Sema_CheckExpr(sema, expr.child1);
let right: int = Sema_CheckExpr(sema, expr.child2);
let op: int = expr.intValue;
// Borrow check: reject assignment through deref in @[Checked] functions
if sema.checkedFunc && op == tkAssign {
if expr.child1 != null as *Expr && expr.child1.kind == ekUnary && expr.child1.intValue == tkStar {
Sema_EmitError(sema, expr.line, expr.column,
"cannot assign through raw pointer in checked function — use '&mut T' instead");
}
}
// Borrow check: reinitialization after move
if sema.checkedFunc && op == tkAssign {
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
Sema_RemoveMoved(sema, expr.child1.strValue);
}
}
// Assignment operators return target type
if op == tkAssign || (op >= tkPlusAssign && op <= tkShrAssign) {
return left;
}
// Comparison operators return bool
if op >= tkEq && op <= tkGe { return tyBool; }
// Logical operators return bool
if op == tkAmpAmp || op == tkPipePipe || op == tkBang { return tyBool; }
// Arithmetic returns wider type
if !Sema_IsNumeric(left) || !Sema_IsNumeric(right) {
Sema_EmitError(sema, expr.line, expr.column, "arithmetic requires numeric operands");
}
if left == tyFloat64 || right == tyFloat64 { return tyFloat64; }
return tyInt;
}
// Unary
if kind == ekUnary {
let operand: int = Sema_CheckExpr(sema, expr.child1);
let op: int = expr.intValue;
if op == tkBang { return tyBool; }
if op == tkStar { return tyUnknown; } // dereference — resolve pointee
if op == tkAmp {
if expr.child1.refType != null as *TypeExpr {
expr.refType = expr.child1.refType;
}
return tyPointer;
}
return operand;
}
// Assign
if kind == ekAssign {
let target: int = Sema_CheckExpr(sema, expr.child1);
let value: int = Sema_CheckExpr(sema, expr.child2);
// Borrow check: reject assignment through deref in @[Checked] functions
if sema.checkedFunc && expr.child1 != null as *Expr && expr.child1.kind == ekUnary && expr.child1.intValue == tkStar {
Sema_EmitError(sema, expr.line, expr.column,
"cannot assign through raw pointer in checked function — use '&mut T' instead");
}
return target;
}
// Call
if kind == ekCall {
let calleeType: int = Sema_CheckExpr(sema, expr.child1);
Sema_ResolveCallArgs(sema, expr);
var arg: *ExprList = expr.callArgs;
while arg != null as *ExprList {
discard Sema_CheckExpr(sema, arg.expr);
arg = arg.next;
}
// Borrow check: reject double mutable borrow in @[Checked] functions
if sema.checkedFunc {
var a: *ExprList = expr.callArgs;
var ai: int = 0;
while a != null as *ExprList {
if a.expr != null as *Expr && a.expr.kind == ekUnary && a.expr.intValue == tkAmp {
if a.expr.child1 != null as *Expr && a.expr.child1.kind == ekIdent {
let name1: String = a.expr.child1.strValue;
var b: *ExprList = a.next;
var bi: int = ai + 1;
while b != null as *ExprList {
if b.expr != null as *Expr && b.expr.kind == ekUnary && b.expr.intValue == tkAmp {
if b.expr.child1 != null as *Expr && b.expr.child1.kind == ekIdent {
if String_Eq(name1, b.expr.child1.strValue) {
Sema_EmitError(sema, expr.line, expr.column,
String_Concat("mutable borrow conflict: multiple &mut references to '", String_Concat(name1, "'")));
}
}
}
b = b.next;
bi = bi + 1;
}
}
}
a = a.next;
ai = ai + 1;
}
}
// Trait bounds checking for explicit generic calls: Max<Circle>(...)
// Must happen before indirect/direct call returns
if expr.child1.kind == ekIdent {
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
if sym.kind == skFunc && sym.decl != null as *Decl {
// Implicit generic type argument inference
if expr.child1.genericTypeArgCount == 0 && sym.decl.typeParamCount > 0 {
let inferred: String = Sema_InferGenericArg(sema, sym.decl, expr);
if !String_Eq(inferred, "") {
expr.child1.genericTypeArgCount = 1;
expr.child1.genericTypeArg0 = inferred;
}
}
if expr.child1.genericTypeArgCount > 0 {
Sema_CheckTraitBounds(sema, sym.decl, expr.child1.genericTypeArg0, expr.child1.genericTypeArg1, expr.child1.genericTypeArgCount, expr.line, expr.column);
}
}
}
// Indirect call through function-typed value
if calleeType == tyFunc {
if expr.child1.refType != null as *TypeExpr && expr.child1.refType.kind == tekFunc {
if expr.child1.refType.funcRet != null as *TypeExpr {
expr.refType = expr.child1.refType.funcRet;
return Sema_ResolveType(sema, expr.child1.refType.funcRet);
}
return tyVoid;
}
}
// Direct call to named function
if expr.child1.kind == ekIdent {
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
if sym.kind == skFunc && sym.decl != null as *Decl {
if sym.decl.retType != null as *TypeExpr {
expr.refType = sym.decl.retType;
return Sema_ResolveType(sema, sym.decl.retType);
}
}
}
return tyUnknown;
}
// Ternary
if kind == ekTernary {
return Sema_CheckExpr(sema, expr.child2); // then type
}
// Cast — return target type
if kind == ekCast {
if expr.refType != null as *TypeExpr {
return Sema_ResolveType(sema, expr.refType);
}
return tyUnknown;
}
// Try (?)
if kind == ekTry {
let inner: int = Sema_CheckExpr(sema, expr.child1);
return inner; // simplified
}
// Struct init: TypeName { field: value, ... }
if kind == ekStructInit {
return tyNamed;
}
// Field access
if kind == ekField {
discard Sema_CheckExpr(sema, expr.child1);
return tyUnknown;
}
// Index
if kind == ekIndex {
let obj: int = Sema_CheckExpr(sema, expr.child1);
let idx: int = Sema_CheckExpr(sema, expr.child2);
if !Sema_IsNumeric(idx) && idx != tyUnknown {
Sema_EmitError(sema, expr.line, expr.column, "index must be integer");
}
return tyUnknown;
}
// Block expression
if kind == ekBlock {
if expr.refBlock != null as *Block {
Sema_CheckBlock(sema, expr.refBlock);
}
return tyVoid;
}
// Closure: |params| -> Ret { body }
if kind == ekClosure {
let savedRetType: int = sema.currentRetType;
let savedScope: *Scope = sema.scope;
let savedClosureDepth: int = sema.closureDepth;
let savedClosureExpr: *Expr = sema.currentClosureExpr;
let savedClosureScope: *Scope = sema.closureScope;
let childScope: Scope = Scope_NewChild(sema.scope);
sema.scope = &childScope;
// Set up closure tracking
sema.closureDepth = sema.closureDepth + 1;
sema.currentClosureExpr = expr;
sema.closureScope = &childScope;
expr.captureCount = 0;
// Set return type from annotation, or unknown for inference
var closureRetType: int = tyUnknown;
if expr.refType != null as *TypeExpr {
closureRetType = Sema_ResolveType(sema, expr.refType);
}
sema.currentRetType = closureRetType;
// Register params in child scope
let params: *Decl = expr.closureParams;
if params != null as *Decl {
var i: int = 0;
while i < params.paramCount {
var p: Param;
if i == 0 { p = params.param0; }
else if i == 1 { p = params.param1; }
else if i == 2 { p = params.param2; }
else if i == 3 { p = params.param3; }
else if i == 4 { p = params.param4; }
else if i == 5 { p = params.param5; }
else if i == 6 { p = params.param6; }
else if i == 7 { p = params.param7; }
else if i == 8 { p = params.param8; }
if !String_Eq(p.name, "") {
var sym: Symbol;
sym.kind = skVar;
sym.name = p.name;
sym.typeKind = tyUnknown;
if p.refParamType != null as *TypeExpr {
sym.typeKind = Sema_ResolveType(sema, p.refParamType);
sym.refType = p.refParamType;
}
sym.typeName = "";
sym.isMutable = false;
sym.isPublic = false;
sym.decl = null as *Decl;
discard Scope_Define(sema.scope, sym);
}
i = i + 1;
}
}
// Check body
if expr.refBlock != null as *Block {
Sema_CheckBlock(sema, expr.refBlock);
}
// Restore
sema.scope = savedScope;
sema.currentRetType = savedRetType;
sema.closureDepth = savedClosureDepth;
sema.currentClosureExpr = savedClosureExpr;
sema.closureScope = savedClosureScope;
// Build function type expression for later use
let funcType: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
funcType.kind = tekFunc;
funcType.funcRet = expr.refType;
funcType.funcParamCount = params.paramCount;
// Build param type list
if params.paramCount > 0 {
var head: *TypeExprList = null as *TypeExprList;
var tail: *TypeExprList = null as *TypeExprList;
var i: int = 0;
while i < params.paramCount {
var p: Param;
if i == 0 { p = params.param0; }
else if i == 1 { p = params.param1; }
else if i == 2 { p = params.param2; }
else if i == 3 { p = params.param3; }
else if i == 4 { p = params.param4; }
else if i == 5 { p = params.param5; }
else if i == 6 { p = params.param6; }
else if i == 7 { p = params.param7; }
else if i == 8 { p = params.param8; }
let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList;
node.te = p.refParamType;
node.next = null as *TypeExprList;
if head == null as *TypeExprList {
head = node;
tail = node;
} else {
tail.next = node;
tail = node;
}
i = i + 1;
}
funcType.funcParams = head;
}
expr.refType = funcType;
return tyFunc;
}
return tyUnknown;
}
// ---------------------------------------------------------------------------
// Statement checking
// ---------------------------------------------------------------------------
func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
if stmt == null as *Stmt { return; }
let kind: int = stmt.kind;
// Let/var
if kind == skLet {
let initType: int = Sema_CheckExpr(sema, stmt.child1);
// Borrow check: move tracking
if sema.checkedFunc && stmt.child1 != null as *Expr && stmt.child1.kind == ekIdent {
Sema_AddMoved(sema, stmt.child1.strValue);
}
// Register variable in scope
var sym: Symbol;
sym.kind = skVar;
sym.name = stmt.strValue;
sym.typeKind = initType;
sym.typeName = "";
sym.refType = null as *TypeExpr;
if stmt.refStmtType != null as *TypeExpr {
sym.refType = stmt.refStmtType;
if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr {
sym.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*");
} else {
sym.typeName = stmt.refStmtType.typeName;
}
} else if stmt.child1 != null as *Expr && stmt.child1.refType != null as *TypeExpr {
// Infer type from initializer expression
sym.refType = stmt.child1.refType;
stmt.refStmtType = stmt.child1.refType;
if stmt.child1.refType.kind == tekPointer && stmt.child1.refType.pointerPointee != null as *TypeExpr {
sym.typeName = String_Concat(stmt.child1.refType.pointerPointee.typeName, "*");
} else {
sym.typeName = stmt.child1.refType.typeName;
}
}
sym.isMutable = stmt.boolValue;
sym.isPublic = false;
sym.decl = null as *Decl;
discard Scope_Define(sema.scope, sym);
return;
}
// Return
if kind == skReturn {
if stmt.child1 != null as *Expr {
let retType: int = Sema_CheckExpr(sema, stmt.child1);
if sema.currentRetType != tyUnknown && retType != tyUnknown {
if retType != sema.currentRetType {
// Be permissive: allow numeric widening, pointer compatibility
if !Sema_IsNumeric(retType) || !Sema_IsNumeric(sema.currentRetType) {
// Allow pointer-type compatibility (String <-> *T, *T <-> *U)
let retIsPtr: bool = retType == tyPointer || retType == tyStr;
let expectedIsPtr: bool = sema.currentRetType == tyPointer || sema.currentRetType == tyStr;
if !retIsPtr || !expectedIsPtr {
Sema_EmitError(sema, stmt.line, stmt.column, "return type mismatch");
}
}
}
}
} else {
if sema.currentRetType != tyVoid && sema.currentRetType != tyUnknown {
Sema_EmitError(sema, stmt.line, stmt.column, "missing return value");
}
}
return;
}
// If
if kind == skIf {
let condType: int = Sema_CheckExpr(sema, stmt.child1);
if !Sema_IsBool(condType) && condType != tyUnknown {
Sema_EmitError(sema, stmt.line, stmt.column, "if condition must be bool");
}
Sema_CheckBlock(sema, stmt.refStmtBlock);
Sema_CheckBlock(sema, stmt.refStmtElse);
return;
}
// While
if kind == skWhile {
let condType: int = Sema_CheckExpr(sema, stmt.child1);
if !Sema_IsBool(condType) && condType != tyUnknown {
Sema_EmitError(sema, stmt.line, stmt.column, "while condition must be bool");
}
Sema_CheckBlock(sema, stmt.refStmtBlock);
return;
}
// Do-while
if kind == skDoWhile {
Sema_CheckBlock(sema, stmt.refStmtBlock);
let condType: int = Sema_CheckExpr(sema, stmt.child1);
if !Sema_IsBool(condType) && condType != tyUnknown {
Sema_EmitError(sema, stmt.line, stmt.column, "do-while condition must be bool");
}
return;
}
// Loop
if kind == skLoop {
Sema_CheckBlock(sema, stmt.refStmtBlock);
return;
}
// For
if kind == skFor {
let iterType: int = Sema_CheckExpr(sema, stmt.child1);
var forScope: Scope = Scope_NewChild(sema.scope);
var loopSym: Symbol;
loopSym.kind = skVar;
loopSym.name = stmt.strValue;
loopSym.typeKind = tyUnknown;
loopSym.typeName = "";
loopSym.refType = null as *TypeExpr;
loopSym.isMutable = true;
loopSym.isPublic = false;
loopSym.decl = null as *Decl;
// Determine loop variable type from iterator expression
if stmt.child1 != null as *Expr {
// Range-based: type from lower bound (selfhost parses .. as ekBinary)
if stmt.child1.kind == ekRange || (stmt.child1.kind == ekBinary && (stmt.child1.intValue == tkDotDot || stmt.child1.intValue == tkDotDotEqual)) {
let boundType: int = Sema_CheckExpr(sema, stmt.child1.child1);
loopSym.typeKind = boundType;
}
// Array-based: extract element type from Array<T> annotation
if stmt.child1.kind == ekIdent {
let sym: Symbol = Scope_Lookup(sema.scope, stmt.child1.strValue);
if sym.refType != null as *TypeExpr {
if String_Eq(sym.refType.typeName, "Array") && sym.refType.typeArgCount > 0 {
let elemTe: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
elemTe.kind = tekNamed;
elemTe.typeName = sym.refType.typeArgName0;
elemTe.line = stmt.line;
elemTe.column = stmt.column;
loopSym.typeKind = Sema_ResolveType(sema, elemTe);
loopSym.typeName = sym.refType.typeArgName0;
loopSym.refType = elemTe;
}
}
}
}
discard Scope_Define(&forScope, loopSym);
let prevScope: *Scope = sema.scope;
sema.scope = &forScope;
Sema_CheckBlock(sema, stmt.refStmtBlock);
sema.scope = prevScope;
return;
}
// Match
if kind == skMatch {
discard Sema_CheckExpr(sema, stmt.child1);
return;
}
// Break / Continue
if kind == skBreak || kind == skContinue {
return;
}
// Expression statement
if kind == skExpr && stmt.child1 != null as *Expr {
discard Sema_CheckExpr(sema, stmt.child1);
return;
}
// Decl (nested)
if kind == skDecl {
if stmt.refStmtDecl != null as *Decl && stmt.refStmtDecl.kind == dkFunc {
Sema_EmitError(sema, stmt.line, stmt.column, "nested functions not yet supported");
}
return;
}
}
// ---------------------------------------------------------------------------
// Collect globals (register functions, structs, enums in scope)
// ---------------------------------------------------------------------------
func Sema_CollectGlobals(sema: *Sema) {
var decl: *Decl = sema.module.firstItem;
var funcCount: int = 0;
var lastDecl: *Decl = null as *Decl;
while decl != null as *Decl {
lastDecl = decl;
let dk: int = decl.kind;
// Function
if dk == dkFunc {
var sym: Symbol;
Sema_ZeroInitSymbol(&sym);
sym.kind = skFunc;
sym.name = decl.strValue;
sym.typeKind = tyFunc;
sym.refType = Sema_BuildFuncTypeExprFromDecl(decl);
sym.isPublic = decl.isPublic;
sym.decl = decl;
discard Scope_Define(sema.scope, sym);
}
// Struct
if dk == dkStruct {
var sym: Symbol;
Sema_ZeroInitSymbol(&sym);
sym.kind = skType;
sym.name = decl.strValue;
sym.typeKind = tyNamed;
sym.isPublic = decl.isPublic;
sym.decl = decl;
discard Scope_Define(sema.scope, sym);
}
// Enum
if dk == dkEnum {
var sym: Symbol;
Sema_ZeroInitSymbol(&sym);
sym.kind = skType;
sym.name = decl.strValue;
sym.typeKind = tyNamed;
sym.isPublic = decl.isPublic;
sym.decl = decl;
discard Scope_Define(sema.scope, sym);
// Register enum variants as constants
var vi: int = 0;
while vi < decl.variantCount && vi < 9 {
var v: EnumVariant;
if vi == 0 { v = decl.variant0; }
else if vi == 1 { v = decl.variant1; }
else if vi == 2 { v = decl.variant2; }
else if vi == 3 { v = decl.variant3; }
else if vi == 4 { v = decl.variant4; }
else if vi == 5 { v = decl.variant5; }
else if vi == 6 { v = decl.variant6; }
else if vi == 7 { v = decl.variant7; }
else if vi == 8 { v = decl.variant8; }
if v.name == null as String || String_Eq(v.name, "") {
vi = vi + 1;
continue;
}
let variantName: String = String_Concat(decl.strValue, "_");
let variantName2: String = String_Concat(variantName, v.name);
var vSym: Symbol;
vSym.kind = skConst;
vSym.name = variantName2;
vSym.typeKind = tyNamed;
vSym.typeName = String_Concat(decl.strValue, "_Tag");
vSym.refType = null as *TypeExpr;
vSym.isMutable = false;
vSym.isPublic = decl.isPublic;
vSym.decl = decl;
discard Scope_Define(sema.scope, vSym);
vi = vi + 1;
}
}
// Const
if dk == dkConst {
var sym: Symbol;
Sema_ZeroInitSymbol(&sym);
sym.kind = skConst;
sym.name = decl.strValue;
if decl.constType != null as *TypeExpr {
sym.typeKind = Sema_ResolveType(sema, decl.constType);
sym.typeName = decl.constType.typeName;
sym.refType = decl.constType;
} else {
sym.typeKind = tyInt;
}
sym.isMutable = false;
sym.isPublic = decl.isPublic;
sym.decl = decl;
discard Scope_Define(sema.scope, sym);
}
// Extern function
if dk == dkExternFunc {
var sym: Symbol;
Sema_ZeroInitSymbol(&sym);
sym.kind = skFunc;
sym.name = decl.strValue;
sym.typeKind = tyFunc;
sym.isPublic = true;
sym.decl = decl;
discard Scope_Define(sema.scope, sym);
}
// Interface
if dk == dkInterface {
if sema.interfaceCount < 64 {
sema.interfaceTable[sema.interfaceCount].name = decl.strValue;
sema.interfaceTable[sema.interfaceCount].decl = decl;
sema.interfaceCount = sema.interfaceCount + 1;
}
}
// Impl block (inherent methods or trait implementations)
if dk == dkImpl {
let implTypeName: String = decl.strValue;
var m: *Decl = decl.childDecl1;
while m != null as *Decl {
if m.kind == dkFunc && sema.methodCount < 256 {
sema.methodEntries[sema.methodCount].typeName = implTypeName;
sema.methodEntries[sema.methodCount].methodName = m.strValue;
sema.methodEntries[sema.methodCount].decl = m;
sema.methodCount = sema.methodCount + 1;
}
m = m.childDecl2;
}
}
decl = decl.childDecl2;
}
// Pass 2: resolve imports by looking up actual symbols
decl = sema.module.firstItem;
while decl != null as *Decl {
let dk: int = decl.kind;
if dk == dkUse {
if decl.useKind == 1 {
// Glob import: add all public symbols from scope
var scope: *Scope = sema.scope;
while scope != null as *Scope {
var i: int = 0;
while i < scope.count {
let sym: Symbol = scope.symbols[i];
if sym.isPublic {
let existing: Symbol = Scope_LookupLocal(sema.scope, sym.name);
if String_Eq(existing.name, "") {
discard Scope_Define(sema.scope, sym);
}
}
i = i + 1;
}
scope = scope.parent;
}
} else if decl.useKind == 2 {
// Multi-import: resolve each name
let namesStr: String = decl.useNames;
if !String_Eq(namesStr, "") {
var start: uint = 0;
var pos: uint = 0;
let totalLen: uint = String_Len(namesStr);
while pos <= totalLen {
let atEnd: bool = pos == totalLen;
let isComma: bool = false;
if pos < totalLen {
let chStr: String = bux_str_slice(namesStr, pos, 1);
isComma = String_Eq(chStr, ",");
}
if atEnd || isComma {
let nameLen: uint = pos - start;
if nameLen > 0 {
let name: String = bux_str_slice(namesStr, start, nameLen);
let found: Symbol = Scope_Lookup(sema.scope, name);
if !String_Eq(found.name, "") && found.isPublic {
let existing: Symbol = Scope_LookupLocal(sema.scope, name);
if String_Eq(existing.name, "") {
discard Scope_Define(sema.scope, found);
}
}
}
start = pos + 1;
}
pos = pos + 1;
}
}
} else {
// Single import: resolve last path segment
let path: String = decl.usePath;
if !String_Eq(path, "") {
var lastSeg: String = path;
let containsColons: int = bux_str_contains(path, "::");
if containsColons != 0 {
let pathLen: uint = String_Len(path);
var tryPos: uint = 0;
while tryPos < pathLen {
let slice: String = bux_str_slice(path, tryPos, pathLen - tryPos);
if String_StartsWith(slice, "::") {
lastSeg = bux_str_slice(slice, 2, String_Len(slice) - 2);
}
tryPos = tryPos + 1;
}
}
let found: Symbol = Scope_Lookup(sema.scope, lastSeg);
if !String_Eq(found.name, "") && found.isPublic {
let existing: Symbol = Scope_LookupLocal(sema.scope, lastSeg);
if String_Eq(existing.name, "") {
discard Scope_Define(sema.scope, found);
}
}
}
}
}
decl = decl.childDecl2;
}
}
// ---------------------------------------------------------------------------
// Trait bounds checking
// ---------------------------------------------------------------------------
func Sema_FindInterface(sema: *Sema, name: String) -> *Decl {
var i: int = 0;
while i < sema.interfaceCount {
if String_Eq(sema.interfaceTable[i].name, name) {
return sema.interfaceTable[i].decl;
}
i = i + 1;
}
return null as *Decl;
}
func Sema_TypeHasMethod(sema: *Sema, typeName: String, methodName: String) -> bool {
var i: int = 0;
while i < sema.methodCount {
if String_Eq(sema.methodEntries[i].typeName, typeName) && String_Eq(sema.methodEntries[i].methodName, methodName) {
return true;
}
i = i + 1;
}
return false;
}
func Sema_TypeImplements(sema: *Sema, typeName: String, interfaceName: String) -> bool {
let iface: *Decl = Sema_FindInterface(sema, interfaceName);
if iface == null as *Decl {
return true; // Unknown interface — be permissive
}
var req: *Decl = iface.childDecl1;
while req != null as *Decl {
if req.kind == dkFunc {
if !Sema_TypeHasMethod(sema, typeName, req.strValue) {
return false;
}
}
req = req.childDecl2;
}
return true;
}
// Extract element type name from a collection TypeExpr.
// Handles both explicit generic types (Array<int>) and mangled types (Array_int).
func Sema_ExtractElemType(te: *TypeExpr) -> String {
if te == null as *TypeExpr { return ""; }
// Pointer: unwrap and recurse
if te.kind == tekPointer && te.pointerPointee != null as *TypeExpr {
return Sema_ExtractElemType(te.pointerPointee);
}
if te.kind == tekNamed {
// Explicit generic: Array<int>
if String_Eq(te.typeName, "Array") || String_Eq(te.typeName, "Iter") {
if te.typeArgCount > 0 {
return te.typeArgName0;
}
}
// Mangled: Array_int, Iter_string
let name: String = te.typeName;
let len: uint = bux_strlen(name);
// Check for "Array_" prefix
if len > 6 && name[0] as int == 65 && name[1] as int == 114 && name[2] as int == 114 && name[3] as int == 97 && name[4] as int == 121 && name[5] as int == 95 {
return bux_str_slice(name, 6, len - 6);
}
// Check for "Iter_" prefix
if len > 5 && name[0] as int == 73 && name[1] as int == 116 && name[2] as int == 101 && name[3] as int == 114 && name[4] as int == 95 {
return bux_str_slice(name, 5, len - 5);
}
}
return "";
}
// Infer generic type argument from call arguments.
// Supports Array_* and Iter_* stdlib functions.
func Sema_InferGenericArg(sema: *Sema, funcDecl: *Decl, expr: *Expr) -> String {
if expr.callArgs == null as *ExprList { return ""; }
let firstArg: *Expr = expr.callArgs.expr;
if firstArg == null as *Expr { return ""; }
// Check first argument's type
let argType: *TypeExpr = firstArg.refType;
if argType == null as *TypeExpr && firstArg.kind == ekUnary && firstArg.intValue == tkAmp {
// &x: use x's type
argType = firstArg.child1.refType;
}
let elemType: String = Sema_ExtractElemType(argType);
if !String_Eq(elemType, "") {
return elemType;
}
return "";
}
func Sema_CheckTraitBounds(sema: *Sema, funcDecl: *Decl, typeArg0: String, typeArg1: String, typeArgCount: int, line: uint32, col: uint32) {
// Trait bounds checking
if funcDecl.typeParamCount >= 1 && typeArgCount >= 1 && !String_Eq(funcDecl.typeParam0Bound, "") {
if !Sema_TypeImplements(sema, typeArg0, funcDecl.typeParam0Bound) {
let errMsg: String = String_Concat("type '", String_Concat(typeArg0, "' does not implement trait '"));
let errMsg2: String = String_Concat(errMsg, String_Concat(funcDecl.typeParam0Bound, "'"));
Sema_EmitError(sema, line, col, errMsg2);
}
}
if funcDecl.typeParamCount >= 2 && typeArgCount >= 2 && !String_Eq(funcDecl.typeParam1Bound, "") {
if !Sema_TypeImplements(sema, typeArg1, funcDecl.typeParam1Bound) {
let errMsg: String = String_Concat("type '", String_Concat(typeArg1, "' does not implement trait '"));
let errMsg2: String = String_Concat(errMsg, String_Concat(funcDecl.typeParam1Bound, "'"));
Sema_EmitError(sema, line, col, errMsg2);
}
}
}
// ---------------------------------------------------------------------------
// Analyze — main entry point
// ---------------------------------------------------------------------------
func Sema_Analyze(mod: *Module) -> *Sema {
let s: *Sema = bux_alloc(sizeof(Sema)) as *Sema;
s.module = mod;
s.scope = bux_alloc(sizeof(Scope)) as *Scope;
s.scope.symbols = bux_alloc(1024 as uint * sizeof(Symbol)) as *Symbol;
s.scope.count = 0;
s.scope.parent = null as *Scope;
s.hasError = false;
s.diagCount = 0;
s.diags = bux_alloc(256 as uint * sizeof(SemaDiag)) as *SemaDiag;
s.typeTable = null as *void;
s.methodTable = null as *void;
s.currentRetType = tyVoid;
s.interfaceTable = bux_alloc(64 as uint * sizeof(InterfaceEntry)) as *InterfaceEntry;
s.interfaceCount = 0;
s.methodEntries = bux_alloc(256 as uint * sizeof(MethodEntry)) as *MethodEntry;
s.methodCount = 0;
// First pass: collect globals
Sema_CollectGlobals(s);
// Second pass: check function bodies
var decl: *Decl = mod.firstItem;
while decl != null as *Decl {
if decl.kind == dkFunc && decl.refBody != null as *Block {
// Create function scope (child of global)
var funcScope: Scope = Scope_NewChild(s.scope);
// Add type params to scope
if decl.typeParamCount >= 1 {
var tpSym: Symbol;
Sema_ZeroInitSymbol(&tpSym);
tpSym.kind = skType;
tpSym.name = decl.typeParam0;
tpSym.typeKind = tyTypeParam;
tpSym.decl = null as *Decl;
discard Scope_Define(&funcScope, tpSym);
}
if decl.typeParamCount >= 2 {
var tpSym2: Symbol;
Sema_ZeroInitSymbol(&tpSym2);
tpSym2.kind = skType;
tpSym2.name = decl.typeParam1;
tpSym2.typeKind = tyTypeParam;
tpSym2.decl = null as *Decl;
discard Scope_Define(&funcScope, tpSym2);
}
// Add parameters to scope
var i: int = 0;
while i < decl.paramCount {
var p: *Param = null as *Param;
if i == 0 { p = &decl.param0; }
else if i == 1 { p = &decl.param1; }
else if i == 2 { p = &decl.param2; }
else if i == 3 { p = &decl.param3; }
else if i == 4 { p = &decl.param4; }
else if i == 5 { p = &decl.param5; }
else if i == 6 { p = &decl.param6; }
else if i == 7 { p = &decl.param7; }
else if i == 8 { p = &decl.param8; }
var pSym: Symbol;
Sema_ZeroInitSymbol(&pSym);
pSym.kind = skVar;
if p != null as *Param && p.refParamType != null as *TypeExpr {
pSym.typeKind = Sema_ResolveType(s, p.refParamType);
pSym.refType = p.refParamType;
if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
pSym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
} else {
pSym.typeName = p.refParamType.typeName;
}
} else {
pSym.typeKind = tyInt;
pSym.typeName = "";
}
pSym.isMutable = false;
pSym.isPublic = false;
pSym.decl = null as *Decl;
if i == 0 { pSym.name = decl.param0.name; }
else if i == 1 { pSym.name = decl.param1.name; }
else if i == 2 { pSym.name = decl.param2.name; }
else if i == 3 { pSym.name = decl.param3.name; }
else if i == 4 { pSym.name = decl.param4.name; }
else if i == 5 { pSym.name = decl.param5.name; }
else if i == 6 { pSym.name = decl.param6.name; }
else if i == 7 { pSym.name = decl.param7.name; }
else if i == 8 { pSym.name = decl.param8.name; }
discard Scope_Define(&funcScope, pSym);
i = i + 1;
}
// Switch to function scope and check body statements
let prevScope: *Scope = s.scope;
s.scope = &funcScope;
// Set current function return type
if decl.retType != null as *TypeExpr {
s.currentRetType = Sema_ResolveType(s, decl.retType);
} else {
s.currentRetType = tyVoid;
s.checkedFunc = false;
s.movedCount = 0;
}
// Enable borrow checking for @[Checked] functions
let wasChecked: bool = s.checkedFunc;
s.checkedFunc = decl.isChecked != 0;
// Check body statements
var stmt: *Stmt = decl.refBody.firstStmt;
while stmt != null as *Stmt {
Sema_CheckStmt(s, stmt);
stmt = stmt.nextStmt;
}
s.checkedFunc = wasChecked;
s.scope = prevScope;
}
decl = decl.childDecl2;
}
return s;
}
func Sema_HasError(sema: *Sema) -> bool {
return sema.hasError;
}
func Sema_DiagCount(sema: *Sema) -> int {
return sema.diagCount;
}
func Sema_Free(sema: *Sema) {
bux_free(sema.scope.symbols as *void);
bux_free(sema.scope as *void);
bux_free(sema.diags as *void);
bux_free(sema as *void);
}
}