selfhost: for x in collection works with non-identifier expressions + buxc2 build merges stdlib

- cli: buxc2 build now delegates to Cli_BuildProject for src/Main.bux,
  enabling stdlib merging (Array_Iter, Iter_HasNext, etc. available)
- sema: set expr.refType on ekCall from function return type
  (needed for for-loop type extraction from MakeArr()-style expressions)
- hir_lower: remove ekIdent restriction on collection-based for loops
  - supports arbitrary expressions: for x in MakeArr() { ... }
  - creates temp variable __tmp_coll_N for non-identifier iterators
  - generates proper Array_T struct instances for temp variables
- Test: for x in MakeArr() sums [10,20,30] → 60
- Selfhost loop: C output IDENTICAL
This commit is contained in:
2026-06-10 10:20:16 +03:00
parent bb84693acb
commit 01677001c3
3 changed files with 63 additions and 11 deletions
+50 -11
View File
@@ -1221,9 +1221,9 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
// let x = Iter_Next_T(&__iter);
// body
// }
if iterExpr != null as *Expr && iterExpr.kind == ekIdent {
if iterExpr != null as *Expr {
let collTypeExpr: *TypeExpr = iterExpr.refType;
if collTypeExpr == null as *TypeExpr {
if collTypeExpr == null as *TypeExpr && iterExpr.kind == ekIdent {
// Fallback: try scope lookup (may have substituted type)
let collSym: Symbol = Scope_Lookup(ctx.scope, iterExpr.strValue);
collTypeExpr = collSym.refType;
@@ -1309,6 +1309,7 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
// __iter = Array_Iter_T(&arr) or just copy if already Iter
var iterInit: *HirNode = null as *HirNode;
var collStore: *HirNode = null as *HirNode;
if isArray {
let callIter: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
callIter.kind = hCall;
@@ -1316,18 +1317,47 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
let addrArr: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
addrArr.kind = hUnary;
addrArr.intValue = tkAmp;
let arrVar: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
arrVar.kind = hVar;
arrVar.strValue = iterExpr.strValue;
addrArr.child1 = arrVar;
if iterExpr.kind == ekIdent {
let arrVar: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
arrVar.kind = hVar;
arrVar.strValue = iterExpr.strValue;
addrArr.child1 = arrVar;
} else {
// Non-identifier: create temp variable for collection
ctx.varCounter = ctx.varCounter + 1;
let tmpName: String = String_Concat("__tmp_coll_", String_FromInt(ctx.varCounter));
// Ensure Array<T> struct instance exists and get mangled name
let arrayGenStruct: *Decl = Lcx_FindGenericStruct(ctx, "Array");
var arrayMangledName: String = collTypeName;
if arrayGenStruct != null as *Decl {
arrayMangledName = Lcx_GenerateStructInstance(ctx, arrayGenStruct, elemTypeName, "", 1);
}
let collAlloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
collAlloca.kind = hAlloca;
collAlloca.strValue = tmpName;
collAlloca.typeName = arrayMangledName;
collAlloca.typeKind = tyNamed;
collStore = bux_alloc(sizeof(HirNode)) as *HirNode;
collStore.kind = hStore;
collStore.child1 = collAlloca;
collStore.child2 = Lcx_LowerExpr(ctx, iterExpr);
let collVar: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
collVar.kind = hVar;
collVar.strValue = tmpName;
addrArr.child1 = collVar;
}
callIter.child1 = addrArr;
iterInit = callIter;
} else {
// Already an iterator: __iter = arr
let arrVar: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
arrVar.kind = hVar;
arrVar.strValue = iterExpr.strValue;
iterInit = arrVar;
if iterExpr.kind == ekIdent {
let arrVar: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
arrVar.kind = hVar;
arrVar.strValue = iterExpr.strValue;
iterInit = arrVar;
} else {
iterInit = Lcx_LowerExpr(ctx, iterExpr);
}
}
let iterStore: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
@@ -1335,6 +1365,11 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
iterStore.child1 = iterAlloca;
iterStore.child2 = iterInit;
// Chain collStore -> iterStore if temp was created
if collStore != null as *HirNode {
collStore.child3 = iterStore;
}
// condition: Iter_HasNext_T(&__iter)
let condCall: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
condCall.kind = hCall;
@@ -1401,7 +1436,11 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
blockNode.kind = hBlock;
blockNode.line = line;
blockNode.column = col;
blockNode.child1 = iterStore;
if collStore != null as *HirNode {
blockNode.child1 = collStore;
} else {
blockNode.child1 = iterStore;
}
return blockNode;
}
}