// 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 releaseFunc: bool; // true inside @[Release] 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_IsMutRefDeref(target: *Expr) -> bool { if target == null as *Expr { return false; } if target.kind != ekUnary { return false; } if target.intValue != tkStar { return false; } let operand: *Expr = target.child1; if operand == null as *Expr { return false; } if operand.refType == null as *TypeExpr { return false; } return operand.refType.kind == tekMutRef; } 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; let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; te.kind = tekNamed; if tk == tkIntLiteral { te.typeName = "int"; expr.refType = te; return tyInt; } if tk == tkFloatLiteral { te.typeName = "float64"; expr.refType = te; return tyFloat64; } if tk == tkStringLiteral { te.typeName = "String"; expr.refType = te; return tyStr; } if tk == tkBoolLiteral { te.typeName = "bool"; expr.refType = te; return tyBool; } if tk == tkCharLiteral { te.typeName = "char32"; expr.refType = te; return tyChar32; } if tk == tkNull { te.typeName = "*void"; expr.refType = te; return tyPointer; } return tyUnknown; } // Identifier — look up in scope if kind == ekIdent { // Borrow check: use-after-move in @[Checked] functions if sema.checkedFunc && !sema.releaseFunc && 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 raw pointer in @[Checked] functions. // Allow writes through &mut T references. if sema.checkedFunc && !sema.releaseFunc && op == tkAssign && expr.child1 != null as *Expr && expr.child1.kind == ekUnary && expr.child1.intValue == tkStar && !Sema_IsMutRefDeref(expr.child1) { 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 && !sema.releaseFunc && 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; } // Operator overloading: check method table var opMethodName: String = ""; if op == tkPlus { opMethodName = "operator_add"; } else if op == tkMinus { opMethodName = "operator_sub"; } else if op == tkStar { opMethodName = "operator_mul"; } else if op == tkSlash { opMethodName = "operator_div"; } else if op == tkPercent { opMethodName = "operator_mod"; } else if op == tkEq { opMethodName = "operator_eq"; } else if op == tkNe { opMethodName = "operator_ne"; } else if op == tkLt { opMethodName = "operator_lt"; } else if op == tkLe { opMethodName = "operator_le"; } else if op == tkGt { opMethodName = "operator_gt"; } else if op == tkGe { opMethodName = "operator_ge"; } else if op == tkAmp { opMethodName = "operator_bitand"; } else if op == tkPipe { opMethodName = "operator_bitor"; } else if op == tkCaret { opMethodName = "operator_xor"; } else if op == tkShl { opMethodName = "operator_shl"; } else if op == tkShr { opMethodName = "operator_shr"; } if !String_Eq(opMethodName, "") && left == tyNamed { var receiverTypeName: String = ""; if expr.child1 != null as *Expr && expr.child1.refType != null as *TypeExpr { receiverTypeName = expr.child1.refType.typeName; } if !String_Eq(receiverTypeName, "") { var i: int = 0; while i < sema.methodCount { if String_Eq(sema.methodEntries[i].typeName, receiverTypeName) && String_Eq(sema.methodEntries[i].methodName, opMethodName) { let methodDecl: *Decl = sema.methodEntries[i].decl; if methodDecl != null as *Decl && methodDecl.paramCount == 2 { if methodDecl.retType != null as *TypeExpr { expr.refType = methodDecl.retType; return Sema_ResolveType(sema, methodDecl.retType); } return tyUnknown; } } i = i + 1; } } } // 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 == tkStar { return tyUnknown; } if op == tkBang { return tyBool; } 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 raw pointer in @[Checked] functions. // Allow writes through &mut T references. if sema.checkedFunc && !sema.releaseFunc && expr.child1 != null as *Expr && expr.child1.kind == ekUnary && expr.child1.intValue == tkStar && !Sema_IsMutRefDeref(expr.child1) { 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 && !sema.releaseFunc { 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(...) // 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 { Sema_InferGenericArgs(sema, sym.decl, expr); } 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 } // spawn Callee(args) if kind == ekSpawn { discard Sema_CheckExpr(sema, expr.child1); if expr.child2 != null as *Expr { discard Sema_CheckExpr(sema, expr.child2); } // Determine if callee is async var calleeName: String = ""; if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { calleeName = expr.child1.strValue; } if !String_Eq(calleeName, "") { let sym: Symbol = Scope_Lookup(sema.scope, calleeName); if sym.decl != null as *Decl && sym.decl.kind == dkFunc && sym.decl.isAsync { expr.boolValue = true; } } return tyPointer; } // 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"); } // Operator overloading: check method table for operator_index_get var receiverTypeName: String = ""; if expr.child1 != null as *Expr && expr.child1.refType != null as *TypeExpr { let refTe: *TypeExpr = expr.child1.refType; if refTe.kind == tekNamed { receiverTypeName = refTe.typeName; } else if refTe.kind == tekPointer && refTe.pointerPointee != null as *TypeExpr && refTe.pointerPointee.kind == tekNamed { receiverTypeName = refTe.pointerPointee.typeName; } } if !String_Eq(receiverTypeName, "") { var i: int = 0; while i < sema.methodCount { if String_Eq(sema.methodEntries[i].typeName, receiverTypeName) && String_Eq(sema.methodEntries[i].methodName, "operator_index_get") { let methodDecl: *Decl = sema.methodEntries[i].decl; if methodDecl != null as *Decl && methodDecl.paramCount == 2 { if methodDecl.retType != null as *TypeExpr { expr.refType = methodDecl.retType; return Sema_ResolveType(sema, methodDecl.retType); } return tyUnknown; } } i = i + 1; } } 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 && !sema.releaseFunc && 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 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 { if 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; } // Also define TypeName_MethodName in scope so auto-drop can find it let methodSymName: String = String_Concat(implTypeName, "_"); let methodSymName2: String = String_Concat(methodSymName, m.strValue); var methodSym: Symbol; Sema_ZeroInitSymbol(&methodSym); methodSym.kind = skFunc; methodSym.name = methodSymName2; methodSym.typeKind = tyFunc; methodSym.isPublic = decl.isPublic; methodSym.decl = m; discard Scope_Define(sema.scope, methodSym); } 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; } // Pass 3: auto-register func Type_Method(self: Type, ...) as methods decl = sema.module.firstItem; while decl != null as *Decl { if decl.kind == dkFunc && decl.paramCount > 0 { let selfParam: Param = decl.param0; if String_Eq(selfParam.name, "self") { var typeName: String = ""; var i: int = bux_strlen(decl.strValue) as int - 1; while i > 0 { if decl.strValue[i] as int == 95 { // '_' let prefix: String = bux_str_slice(decl.strValue, 0, i as uint); let typeSym: Symbol = Scope_Lookup(sema.scope, prefix); if typeSym.kind == skType && typeSym.decl != null as *Decl && typeSym.decl.kind == dkStruct { typeName = prefix; break; } } i = i - 1; } if !String_Eq(typeName, "") { let methodName: String = bux_str_slice(decl.strValue, (i + 1) as uint, bux_strlen(decl.strValue) - (i + 1) as uint); if sema.methodCount < 256 { sema.methodEntries[sema.methodCount].typeName = typeName; sema.methodEntries[sema.methodCount].methodName = methodName; sema.methodEntries[sema.methodCount].decl = decl; sema.methodCount = sema.methodCount + 1; } } } } 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) 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 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_InferGenericArgs(sema: *Sema, funcDecl: *Decl, expr: *Expr) { if expr.callArgs == null as *ExprList { return; } var argList: *ExprList = expr.callArgs; var pi: int = 0; while argList != null as *ExprList && pi < funcDecl.paramCount { let argExpr: *Expr = argList.expr; if argExpr == null as *Expr { argList = argList.next; pi = pi + 1; continue; } var argType: *TypeExpr = argExpr.refType; if argType == null as *TypeExpr && argExpr.kind == ekUnary && argExpr.intValue == tkAmp { argType = argExpr.child1.refType; } var paramType: *TypeExpr = null as *TypeExpr; if pi == 0 { paramType = funcDecl.param0.refParamType; } else if pi == 1 { paramType = funcDecl.param1.refParamType; } else if pi == 2 { paramType = funcDecl.param2.refParamType; } else if pi == 3 { paramType = funcDecl.param3.refParamType; } else if pi == 4 { paramType = funcDecl.param4.refParamType; } else if pi == 5 { paramType = funcDecl.param5.refParamType; } else if pi == 6 { paramType = funcDecl.param6.refParamType; } else if pi == 7 { paramType = funcDecl.param7.refParamType; } else if pi == 8 { paramType = funcDecl.param8.refParamType; } if paramType != null as *TypeExpr && paramType.kind == tekNamed && argType != null as *TypeExpr { let inferred: String = Sema_ExtractElemType(argType); var typeName: String = inferred; if String_Eq(typeName, "") && argType.kind == tekNamed { typeName = argType.typeName; } if !String_Eq(typeName, "") { if funcDecl.typeParamCount >= 1 && String_Eq(paramType.typeName, funcDecl.typeParam0) && String_Eq(expr.child1.genericTypeArg0, "") { expr.child1.genericTypeArg0 = typeName; if expr.child1.genericTypeArgCount < 1 { expr.child1.genericTypeArgCount = 1; } } if funcDecl.typeParamCount >= 2 && String_Eq(paramType.typeName, funcDecl.typeParam1) && String_Eq(expr.child1.genericTypeArg1, "") { expr.child1.genericTypeArg1 = typeName; if expr.child1.genericTypeArgCount < 2 { expr.child1.genericTypeArgCount = 2; } } } } argList = argList.next; pi = pi + 1; } } 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; let wasRelease: bool = s.releaseFunc; s.releaseFunc = decl.isRelease != 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.releaseFunc = wasRelease; 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); } }