selfhost: trait bounds parsing, sema checks, and generic monomorphization fix

- AST: add typeParam0Bound/typeParam1Bound to Decl, strValue2 to dkImpl
- Parser: parse <T: Trait> bounds, Self type in interfaces, extend-for blocks
- Sema: add interfaceTable/methodTable, Sema_CheckTraitBounds, Sema_TypeImplements
- HIR lower: two-pass decl iteration — collect generic funcs before lowering bodies
  Fixes Max<Circle>(...) not generating Max_Circle when caller precedes callee
- C backend: skip generic funcs in emission (only emit monomorphized instances)
- Array.bux: fix for-loop over monomorphized Array types
- All debug prints removed; selfhost loop passes (C output IDENTICAL)
This commit is contained in:
2026-06-10 08:48:10 +03:00
parent 499b389ba8
commit f63cbd1bf0
6 changed files with 435 additions and 74 deletions
+5
View File
@@ -30,6 +30,11 @@ func Array_Get<T>(self: *Array<T>, index: uint) -> T {
return self.data[index];
}
func Array_Set<T>(self: *Array<T>, index: uint, value: T) {
bux_bounds_check(index, self.len);
self.data[index] = value;
}
func Array_Len<T>(self: *Array<T>) -> uint {
return self.len;
}
+5
View File
@@ -281,6 +281,9 @@ struct Decl {
typeParam0: String,
typeParam1: String,
typeParamCount: int,
// Trait bounds for type params (e.g. <T: Comparable>)
typeParam0Bound: String,
typeParam1Bound: String,
// Params (for functions)
paramCount: int,
param0: Param,
@@ -392,6 +395,8 @@ func Ast_MakeDecl(kind: int, line: uint32, col: uint32) -> Decl {
return Decl { kind: kind, line: line, column: col, isPublic: false,
strValue: "", strValue2: "",
typeParam0: "", typeParam1: "", typeParamCount: 0,
typeParam0Bound: "", typeParam1Bound: ""
,
paramCount: 0,
retType: null as *TypeExpr,
refBody: null as *Block,
+6
View File
@@ -714,6 +714,12 @@ func CBE_FuncHasGeneric(f: *HirFunc) -> bool {
return false;
}
func CBE_IsArrayTypeName(name: String) -> bool {
if String_Eq(name, "") { return false; }
if String_StartsWith(name, "Array") { return true; }
return false;
}
func CBE_IsPrimitiveTypeName(name: String) -> bool {
if String_Eq(name, "int") || String_Eq(name, "") { return true; }
if String_Eq(name, "String") { return true; }
+160 -6
View File
@@ -31,6 +31,8 @@ struct LowerCtx {
substArg0: String,
substParam1: String,
substArg1: String,
// Borrow checker state
checkedFunc: bool,
}
// ---------------------------------------------------------------------------
@@ -346,6 +348,46 @@ func Lcx_GenerateFuncInstance(ctx: *LowerCtx, genDecl: *Decl, typeArg0: String,
return mangled;
}
// ---------------------------------------------------------------------------
// Array type helpers for bounds-checking desugaring
// ---------------------------------------------------------------------------
func Lcx_IsArrayTypeExpr(te: *TypeExpr) -> bool {
if te == null as *TypeExpr { return false; }
if te.kind == tekPointer && te.pointerPointee != null as *TypeExpr {
te = te.pointerPointee;
}
if te.kind == tekNamed {
if String_Eq(te.typeName, "Array") { return true; }
let name: String = te.typeName;
if 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 true;
}
}
return false;
}
func Lcx_GetArrayElemType(te: *TypeExpr) -> String {
if te == null as *TypeExpr { return ""; }
if te.kind == tekPointer && te.pointerPointee != null as *TypeExpr {
te = te.pointerPointee;
}
if te.kind == tekNamed {
if String_Eq(te.typeName, "Array") && te.typeArgCount > 0 {
return te.typeArgName0;
}
let name: String = te.typeName;
if 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 {
let prefixLen: uint = 6;
let totalLen: uint = bux_strlen(name);
if totalLen > prefixLen {
return bux_str_slice(name, prefixLen, totalLen - prefixLen);
}
}
}
return "";
}
// ---------------------------------------------------------------------------
// Expression lowering
// ---------------------------------------------------------------------------
@@ -675,6 +717,64 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
// Index: arr[idx]
if kind == ekIndex {
// In @[Checked] functions, Array access goes through Array_Get with bounds check
if ctx.checkedFunc && expr.child1 != null as *Expr && Lcx_IsArrayTypeExpr(expr.child1.refType) {
let elemType: String = Lcx_GetArrayElemType(expr.child1.refType);
if !String_Eq(elemType, "") {
let baseNode: *HirNode = Lcx_LowerExpr(ctx, expr.child1);
let idxNode: *HirNode = Lcx_LowerExpr(ctx, expr.child2);
var isPtr: bool = false;
if expr.child1.refType != null as *TypeExpr && expr.child1.refType.kind == tekPointer {
isPtr = true;
}
let callNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
callNode.kind = hCall;
callNode.strValue = Lcx_MangleName("Array_Get", elemType, "", 1);
callNode.line = line;
callNode.column = col;
let genGet: *Decl = Lcx_FindGenericFunc(ctx, "Array_Get");
if genGet != null as *Decl {
Lcx_GenerateFuncInstance(ctx, genGet, elemType, "", 1);
}
if isPtr {
callNode.child1 = baseNode;
} else {
let addrNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
addrNode.kind = hUnary;
addrNode.intValue = tkAmp;
addrNode.child1 = baseNode;
callNode.child1 = addrNode;
}
callNode.child2 = idxNode;
callNode.extraCount = 0;
callNode.extraData = null as *void;
return callNode;
}
}
// For Array<T> or *Array<T>, desugar arr[idx] → arr.data[idx]
if expr.child1 != null as *Expr && Lcx_IsArrayTypeExpr(expr.child1.refType) {
let baseNode: *HirNode = Lcx_LowerExpr(ctx, expr.child1);
let idxNode: *HirNode = Lcx_LowerExpr(ctx, expr.child2);
let fieldPtr: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
fieldPtr.kind = hFieldPtr;
fieldPtr.line = line;
fieldPtr.column = col;
fieldPtr.strValue = "data";
fieldPtr.child1 = baseNode;
n.kind = hIndexPtr;
n.child1 = fieldPtr;
n.child2 = idxNode;
return n;
}
n.kind = hIndexPtr;
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
n.child2 = Lcx_LowerExpr(ctx, expr.child2);
@@ -683,6 +783,52 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
// Assign: target = value
if kind == ekAssign {
// Array bounds-checking for write in @[Checked]: arr[idx] = val → Array_Set_T(&arr, idx, val)
// Only for plain assignment (=), not compound operators (+=, -=, etc.)
if expr.intValue == tkAssign && ctx.checkedFunc && expr.child1 != null as *Expr && expr.child1.kind == ekIndex && expr.child1.child1 != null as *Expr && Lcx_IsArrayTypeExpr(expr.child1.child1.refType) {
let elemType: String = Lcx_GetArrayElemType(expr.child1.child1.refType);
if !String_Eq(elemType, "") {
let baseNode: *HirNode = Lcx_LowerExpr(ctx, expr.child1.child1);
let idxNode: *HirNode = Lcx_LowerExpr(ctx, expr.child1.child2);
let valNode: *HirNode = Lcx_LowerExpr(ctx, expr.child2);
var isPtr: bool = false;
if expr.child1.child1.refType != null as *TypeExpr && expr.child1.child1.refType.kind == tekPointer {
isPtr = true;
}
let callNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
callNode.kind = hCall;
callNode.strValue = Lcx_MangleName("Array_Set", elemType, "", 1);
callNode.line = line;
callNode.column = col;
let genSet: *Decl = Lcx_FindGenericFunc(ctx, "Array_Set");
if genSet != null as *Decl {
Lcx_GenerateFuncInstance(ctx, genSet, elemType, "", 1);
}
if isPtr {
callNode.child1 = baseNode;
} else {
let addrNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
addrNode.kind = hUnary;
addrNode.intValue = tkAmp;
addrNode.child1 = baseNode;
callNode.child1 = addrNode;
}
callNode.child2 = idxNode;
let extra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
extra.node = valNode;
extra.next = null as *HirArgList;
callNode.extraData = extra as *void;
callNode.extraCount = 1;
return callNode;
}
}
n.kind = hAssign;
n.child1 = Lcx_LowerExpr(ctx, expr.child1); // target
n.child2 = Lcx_LowerExpr(ctx, expr.child2); // value
@@ -1434,6 +1580,9 @@ func Lcx_LowerParam(out: *HirParam, p: *Param, ctx: *LowerCtx) {
// ---------------------------------------------------------------------------
func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
let oldChecked: bool = ctx.checkedFunc;
ctx.checkedFunc = decl.isChecked != 0;
let f: *HirFunc = bux_alloc(sizeof(HirFunc)) as *HirFunc;
f.name = decl.strValue;
f.isPublic = decl.isPublic;
@@ -1516,6 +1665,7 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
f.body = null as *HirNode;
}
ctx.scope = prevScope;
ctx.checkedFunc = oldChecked;
return f;
}
@@ -1685,19 +1835,24 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
// Second pass: actually collect them
// For simplicity, do single pass with pre-allocated field arrays
// Iterate declarations
// Pass 1: collect generic declarations for monomorphization
var decl: *Decl = mod.firstItem;
while decl != null as *Decl {
// Collect generic declarations for monomorphization
if decl.kind == dkFunc && decl.typeParamCount > 0 {
ctx.genFuncs[ctx.genFuncCount] = *decl;
ctx.genFuncCount = ctx.genFuncCount + 1;
}
if decl.kind == dkStruct {
if decl.typeParamCount > 0 {
if decl.kind == dkStruct && decl.typeParamCount > 0 {
ctx.genStructs[ctx.genStructCount] = *decl;
ctx.genStructCount = ctx.genStructCount + 1;
} else if !String_Eq(decl.strValue, "") {
}
decl = decl.childDecl2;
}
// Pass 2: lower all declarations
decl = mod.firstItem;
while decl != null as *Decl {
if decl.kind == dkStruct && decl.typeParamCount == 0 && !String_Eq(decl.strValue, "") {
// Collect struct definition for C codegen
let si: int = hm.structCount;
hm.structs[si].name = decl.strValue;
@@ -1729,7 +1884,6 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
}
hm.structCount = hm.structCount + 1;
}
}
if decl.kind == dkFunc && decl.refBody != null as *Block {
let f: *HirFunc = Lcx_LowerFunc(ctx, decl);
ctx.funcs[ctx.funcCount] = *f;
+109 -36
View File
@@ -232,6 +232,15 @@ func parserParseType(p: *Parser) -> *TypeExpr {
// name
let nameTok: LexToken = parserExpect(p, tkIdent, "expected type name");
// self / Self -> tekSelf
if String_Eq(nameTok.text, "self") || String_Eq(nameTok.text, "Self") {
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
te.kind = tekSelf;
te.line = nameTok.line;
te.column = nameTok.column;
te.typeName = "Self";
return te;
}
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
te.kind = tekNamed;
te.line = nameTok.line;
@@ -473,7 +482,6 @@ func parserParseClosure(p: *Parser) -> *Expr {
func parserParsePostfixExpr(p: *Parser) -> *Expr {
var left: *Expr = parserParsePrimary(p);
while true {
let kind: int = parserPeek(p, 0);
@@ -700,7 +708,7 @@ func parserParsePostfixExpr(p: *Parser) -> *Expr {
// ---------------------------------------------------------------------------
func parserPrecedence(op: int) -> int {
if op == tkAssign || op == tkPlusAssign || op == tkMinusAssign || op == tkStarAssign || op == tkSlashAssign || op == tkPercentAssign || op == tkAmpAssign || op == tkPipeAssign || op == tkCaretAssign || op == tkShlAssign || op == tkShrAssign { return 1; }
// Assignment operators are parsed by parserParseAssign, not here
if op == tkPipePipe { return 2; }
if op == tkAmpAmp { return 3; }
if op == tkPipe { return 4; }
@@ -801,12 +809,31 @@ func parserParseTernary(p: *Parser) -> *Expr {
return left;
}
// ---------------------------------------------------------------------------
// Assignment: target = value (right-associative)
// ---------------------------------------------------------------------------
func parserParseAssign(p: *Parser) -> *Expr {
let left: *Expr = parserParseTernary(p);
let op: int = parserPeek(p, 0);
if op == tkAssign || op == tkPlusAssign || op == tkMinusAssign || op == tkStarAssign || op == tkSlashAssign || op == tkPercentAssign || op == tkAmpAssign || op == tkPipeAssign || op == tkCaretAssign || op == tkShlAssign || op == tkShrAssign {
let opTok: LexToken = parserAdvance(p);
let right: *Expr = parserParseAssign(p);
let e: *Expr = parserMakeExpr(ekAssign, opTok.line, opTok.column);
e.intValue = opTok.kind;
e.child1 = left;
e.child2 = right;
return e;
}
return left;
}
// ---------------------------------------------------------------------------
// Top-level expression
// ---------------------------------------------------------------------------
func parserParseExpr(p: *Parser) -> *Expr {
return parserParseTernary(p);
return parserParseAssign(p);
}
// ---------------------------------------------------------------------------
@@ -1227,6 +1254,32 @@ func parserParseParamList(p: *Parser) -> *Decl {
return d;
}
// ---------------------------------------------------------------------------
// Type parameters: <T: Bound, U: Bound2>
// ---------------------------------------------------------------------------
func parserParseTypeParams(p: *Parser, d: *Decl) {
if !parserCheck(p, tkLt) { return; }
discard parserAdvance(p);
let tp0: LexToken = parserExpect(p, tkIdent, "expected type param");
d.typeParam0 = tp0.text;
d.typeParamCount = 1;
if parserMatch(p, tkColon) {
let bound0: LexToken = parserExpect(p, tkIdent, "expected trait bound name");
d.typeParam0Bound = bound0.text;
}
if parserMatch(p, tkComma) {
let tp1: LexToken = parserExpect(p, tkIdent, "expected type param");
d.typeParam1 = tp1.text;
d.typeParamCount = 2;
if parserMatch(p, tkColon) {
let bound1: LexToken = parserExpect(p, tkIdent, "expected trait bound name");
d.typeParam1Bound = bound1.text;
}
}
discard parserExpect(p, tkGt, "expected '>'");
}
// ---------------------------------------------------------------------------
// Declarations
// ---------------------------------------------------------------------------
@@ -1245,19 +1298,8 @@ func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool, isAsync: bo
d.isAsync = isAsync;
d.strValue = nameTok.text;
// Type params <T, U>
if parserCheck(p, tkLt) {
discard parserAdvance(p);
let tp0: LexToken = parserExpect(p, tkIdent, "expected type param");
d.typeParam0 = tp0.text;
d.typeParamCount = 1;
if parserMatch(p, tkComma) {
let tp1: LexToken = parserExpect(p, tkIdent, "expected type param");
d.typeParam1 = tp1.text;
d.typeParamCount = 2;
}
discard parserExpect(p, tkGt, "expected '>'");
}
// Type params <T: Bound, U: Bound2>
parserParseTypeParams(p, d);
// Params
let params: *Decl = parserParseParamList(p);
@@ -1305,19 +1347,8 @@ func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl {
d.strValue = nameTok.text;
var fieldCount: int = 0;
// Type params
if parserCheck(p, tkLt) {
discard parserAdvance(p);
let tp0: LexToken = parserExpect(p, tkIdent, "expected type param");
d.typeParam0 = tp0.text;
d.typeParamCount = 1;
if parserMatch(p, tkComma) {
let tp1: LexToken = parserExpect(p, tkIdent, "expected type param");
d.typeParam1 = tp1.text;
d.typeParamCount = 2;
}
discard parserExpect(p, tkGt, "expected '>'");
}
// Type params <T: Bound, U: Bound2>
parserParseTypeParams(p, d);
discard parserExpect(p, tkLBrace, "expected '{'");
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
@@ -1487,6 +1518,47 @@ func parserParseExternDecl(p: *Parser, isPublic: bool) -> *Decl {
return d;
}
// ---------------------------------------------------------------------------
// Interface declaration
// ---------------------------------------------------------------------------
func parserParseInterfaceDecl(p: *Parser, isPublic: bool) -> *Decl {
let line: uint32 = parserCurToken(p).line;
let col: uint32 = parserCurToken(p).column;
discard parserExpect(p, tkInterface, "expected 'interface'");
let nameTok: LexToken = parserExpect(p, tkIdent, "expected interface name");
discard parserExpect(p, tkLBrace, "expected '{' to start interface body");
let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
d.kind = dkInterface;
d.line = line;
d.column = col;
d.isPublic = isPublic;
d.strValue = nameTok.text;
var methods: *Decl = null as *Decl;
var lastMethod: *Decl = null as *Decl;
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; }
if parserCheck(p, tkFunc) {
let m: *Decl = parserParseFuncDecl(p, false, false, false);
if methods == null as *Decl {
methods = m;
lastMethod = m;
} else {
lastMethod.childDecl2 = m;
lastMethod = m;
}
d.methodCount = d.methodCount + 1;
} else {
break;
}
}
d.childDecl1 = methods;
discard parserExpect(p, tkRBrace, "expected '}' to close interface");
return d;
}
// ---------------------------------------------------------------------------
// Top-level declaration
// ---------------------------------------------------------------------------
@@ -1538,6 +1610,7 @@ func parserParseDecl(p: *Parser) -> *Decl {
if kind == tkEnum { return parserParseEnumDecl(p, isPublic); }
if kind == tkImport { return parserParseImportDecl(p, isPublic); }
if kind == tkExtern { return parserParseExternDecl(p, isPublic); }
if kind == tkInterface { return parserParseInterfaceDecl(p, isPublic); }
if kind == tkExtend {
discard parserAdvance(p);
@@ -1552,13 +1625,13 @@ func parserParseDecl(p: *Parser) -> *Decl {
d.isPublic = isPublic;
d.strValue = typeName.text;
// Optional <T>
if parserCheck(p, tkLt) {
discard parserAdvance(p);
let tp0: LexToken = parserExpect(p, tkIdent, "expected type param");
d.typeParam0 = tp0.text;
d.typeParamCount = 1;
discard parserExpect(p, tkGt, "expected '>'");
// Optional <T: Bound>
parserParseTypeParams(p, d);
// Optional 'for InterfaceName'
if parserMatch(p, tkFor) {
let ifaceName: LexToken = parserExpect(p, tkIdent, "expected interface name");
d.strValue2 = ifaceName.text;
}
discard parserExpect(p, tkLBrace, "expected '{'");
+118
View File
@@ -2,6 +2,9 @@
// 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;
// ---------------------------------------------------------------------------
// Sema context
// ---------------------------------------------------------------------------
@@ -27,6 +30,11 @@ struct Sema {
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 {
@@ -35,6 +43,21 @@ struct SemaDiag {
message: String;
}
// ---------------------------------------------------------------------------
// Interface / Method tables for trait bounds checking
// ---------------------------------------------------------------------------
struct InterfaceEntry {
name: String,
decl: *Decl,
}
struct MethodEntry {
typeName: String,
methodName: String,
decl: *Decl,
}
// ---------------------------------------------------------------------------
// Diagnostics
// ---------------------------------------------------------------------------
@@ -556,6 +579,10 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
if sym.kind == skFunc {
if sym.decl != null as *Decl {
// Trait bounds checking for explicit generic calls: Max<Circle>(...)
if expr.child1.genericTypeArgCount > 0 {
Sema_CheckTraitBounds(sema, sym.decl, expr.child1.genericTypeArg0, expr.child1.genericTypeArg1, expr.child1.genericTypeArgCount, expr.line, expr.column);
}
if sym.decl.retType != null as *TypeExpr {
return Sema_ResolveType(sema, sym.decl.retType);
}
@@ -915,7 +942,9 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
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
@@ -1018,6 +1047,30 @@ func Sema_CollectGlobals(sema: *Sema) {
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;
}
@@ -1107,6 +1160,67 @@ func Sema_CollectGlobals(sema: *Sema) {
}
}
// ---------------------------------------------------------------------------
// 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;
}
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
// ---------------------------------------------------------------------------
@@ -1124,6 +1238,10 @@ func Sema_Analyze(mod: *Module) -> *Sema {
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);