feat: match guards, HOF inference, ownership C.2/C.3, LSP sema hover

Sessions 18–23 quality work:

- B.3c match arm guards + sequential found-flag lower (bootstrap + selfhost)
- Generic HOF type inference (Array/Iter map/filter/fold without type args)
- Pattern binding shadowing via unique C locals (__pN_src)
- Ownership C.2 exclusive &mut data-flow + C.4 goldens; *p= store-through fix
- Ownership C.3 auto-drop on early return/branches: scoped defers, move-on-return,
  Drop monomorphization, materialize return before Drop
- LSP 0.3.0: hover from real sema types
- Examples and QUALITY_PLAN session log; selfhost-loop identical
This commit is contained in:
2026-07-18 21:52:14 +03:00
parent 66f11d1869
commit 3eb1ad3a82
25 changed files with 2246 additions and 457 deletions
+380 -217
View File
@@ -34,6 +34,101 @@ struct LowerCtx {
// Borrow checker state
checkedFunc: bool,
releaseFunc: bool,
// Pattern binding renames: source name → unique C local (shadowing-safe)
patMapCount: int,
patMapFrom0: String,
patMapTo0: String,
patMapFrom1: String,
patMapTo1: String,
patMapFrom2: String,
patMapTo2: String,
patMapFrom3: String,
patMapTo3: String,
patMapFrom4: String,
patMapTo4: String,
patMapFrom5: String,
patMapTo5: String,
patMapFrom6: String,
patMapTo6: String,
patMapFrom7: String,
patMapTo7: String,
}
func Lcx_PatLookup(ctx: *LowerCtx, src: String) -> String {
// Most recent rename wins (scan from end)
var i: int = ctx.patMapCount - 1;
while i >= 0 {
var from: String = "";
var to: String = "";
if i == 0 { from = ctx.patMapFrom0; to = ctx.patMapTo0; }
else if i == 1 { from = ctx.patMapFrom1; to = ctx.patMapTo1; }
else if i == 2 { from = ctx.patMapFrom2; to = ctx.patMapTo2; }
else if i == 3 { from = ctx.patMapFrom3; to = ctx.patMapTo3; }
else if i == 4 { from = ctx.patMapFrom4; to = ctx.patMapTo4; }
else if i == 5 { from = ctx.patMapFrom5; to = ctx.patMapTo5; }
else if i == 6 { from = ctx.patMapFrom6; to = ctx.patMapTo6; }
else if i == 7 { from = ctx.patMapFrom7; to = ctx.patMapTo7; }
if String_Eq(from, src) { return to; }
i = i - 1;
}
return "";
}
func Lcx_PatPush(ctx: *LowerCtx, src: String, dst: String) {
if ctx.patMapCount >= 8 { return; }
let i: int = ctx.patMapCount;
if i == 0 { ctx.patMapFrom0 = src; ctx.patMapTo0 = dst; }
else if i == 1 { ctx.patMapFrom1 = src; ctx.patMapTo1 = dst; }
else if i == 2 { ctx.patMapFrom2 = src; ctx.patMapTo2 = dst; }
else if i == 3 { ctx.patMapFrom3 = src; ctx.patMapTo3 = dst; }
else if i == 4 { ctx.patMapFrom4 = src; ctx.patMapTo4 = dst; }
else if i == 5 { ctx.patMapFrom5 = src; ctx.patMapTo5 = dst; }
else if i == 6 { ctx.patMapFrom6 = src; ctx.patMapTo6 = dst; }
else if i == 7 { ctx.patMapFrom7 = src; ctx.patMapTo7 = dst; }
ctx.patMapCount = ctx.patMapCount + 1;
}
func Lcx_FreshPatName(ctx: *LowerCtx, src: String) -> String {
ctx.varCounter = ctx.varCounter + 1;
var safe: String = src;
if String_Eq(src, "") || String_Eq(src, "_") { safe = "x"; }
return String_Concat(String_Concat("__p", String_FromInt(ctx.varCounter as int64)),
String_Concat("_", safe));
}
// Alloca unique C local + store + scope define + rename map for pattern binding.
func Lcx_BindPatIdent(ctx: *LowerCtx, src: String, ty: String, value: *HirNode,
line: uint32, col: uint32) -> *HirNode {
if String_Eq(src, "") || String_Eq(src, "_") { return null as *HirNode; }
let cName: String = Lcx_FreshPatName(ctx, src);
Lcx_PatPush(ctx, src, cName);
let alloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
alloca.kind = hAlloca;
alloca.line = line;
alloca.column = col;
alloca.strValue = cName;
alloca.typeName = ty;
let store: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
store.kind = hStore;
store.line = line;
store.column = col;
let v: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
v.kind = hVar;
v.strValue = cName;
store.child1 = v;
store.child2 = value;
alloca.child3 = store;
var bsym: Symbol;
bsym.kind = skVar;
bsym.name = src;
bsym.typeKind = tyInt;
bsym.typeName = ty;
bsym.refType = null as *TypeExpr;
bsym.isMutable = false;
bsym.isPublic = false;
bsym.decl = null as *Decl;
discard Scope_Define(ctx.scope, bsym);
return alloca;
}
// ---------------------------------------------------------------------------
@@ -265,6 +360,27 @@ func Lcx_FindGenericStruct(ctx: *LowerCtx, name: String) -> *Decl {
return null as *Decl;
}
// Extract element type from mangled collection name: Array_int → int, Iter_String → String
func Lcx_ExtractElemFromName(typeName: String) -> String {
if String_Eq(typeName, "") { return ""; }
let len: uint = bux_strlen(typeName);
// "Array_" prefix (6 chars)
if len > 6 {
let p: String = bux_str_slice(typeName, 0, 6);
if String_Eq(p, "Array_") {
return bux_str_slice(typeName, 6, len - 6);
}
}
// "Iter_" prefix (5 chars)
if len > 5 {
let p: String = bux_str_slice(typeName, 0, 5);
if String_Eq(p, "Iter_") {
return bux_str_slice(typeName, 5, len - 5);
}
}
return "";
}
func Lcx_MangleName(base: String, typeArg0: String, typeArg1: String, typeArgCount: int) -> String {
let r: String = String_Concat(base, "_");
r = String_Concat(r, typeArg0);
@@ -487,6 +603,11 @@ func Lcx_PatternCond(ctx: *LowerCtx, subject: *HirNode, pat: *Pattern,
if pat == null as *Pattern { return null as *HirNode; }
let kind: int = pat.kind;
// Guarded: condition is only the inner pattern; guard applied after binds
if kind == pkGuarded {
return Lcx_PatternCond(ctx, subject, pat.patChild1, subjectEnumName, subjectHasData, line, col);
}
if kind == pkWildcard || kind == pkIdent {
return null as *HirNode;
}
@@ -568,40 +689,16 @@ func Lcx_PatternBindings(ctx: *LowerCtx, subject: *HirNode, pat: *Pattern,
subjectEnumName: String, subjectHasData: bool,
line: uint32, col: uint32) -> *HirNode {
if pat == null as *Pattern { return null as *HirNode; }
// Guarded: bind from inner pattern
if pat.kind == pkGuarded {
return Lcx_PatternBindings(ctx, subject, pat.patChild1, subjectEnumName, subjectHasData, line, col);
}
if pat.kind == pkIdent {
// `_` is wildcard, not a binding
if String_Eq(pat.patIdent, "_") { return null as *HirNode; }
let ty: String = "int";
if subject != null as *HirNode && !String_Eq(subject.typeName, "") {
ty = subject.typeName;
}
let alloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
alloca.kind = hAlloca;
alloca.line = line;
alloca.column = col;
alloca.strValue = pat.patIdent;
alloca.typeName = ty;
let store: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
store.kind = hStore;
store.line = line;
store.column = col;
let v: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
v.kind = hVar;
v.strValue = pat.patIdent;
store.child1 = v;
store.child2 = subject;
alloca.child3 = store;
var bsym: Symbol;
bsym.kind = skVar;
bsym.name = pat.patIdent;
bsym.typeKind = tyInt;
bsym.typeName = ty;
bsym.refType = null as *TypeExpr;
bsym.isMutable = false;
bsym.isPublic = false;
bsym.decl = null as *Decl;
discard Scope_Define(ctx.scope, bsym);
return alloca;
return Lcx_BindPatIdent(ctx, pat.patIdent, ty, subject, line, col);
}
// Tuple: (a, b) → a = subject._0; b = subject._1
@@ -629,38 +726,16 @@ func Lcx_PatternBindings(ctx: *LowerCtx, subject: *HirNode, pat: *Pattern,
fLoad.column = col;
fLoad.child1 = fPtr;
fLoad.typeName = "int";
let alloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
alloca.kind = hAlloca;
alloca.line = line;
alloca.column = col;
alloca.strValue = elem.patIdent;
alloca.typeName = "int";
let store: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
store.kind = hStore;
store.line = line;
store.column = col;
let v: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
v.kind = hVar;
v.strValue = elem.patIdent;
store.child1 = v;
store.child2 = fLoad;
alloca.child3 = store;
var bsym: Symbol;
bsym.kind = skVar;
bsym.name = elem.patIdent;
bsym.typeKind = tyInt;
bsym.typeName = "int";
bsym.refType = null as *TypeExpr;
bsym.isMutable = false;
bsym.isPublic = false;
bsym.decl = null as *Decl;
discard Scope_Define(ctx.scope, bsym);
if head == null as *HirNode {
head = alloca;
tail = store;
} else {
tail.child3 = alloca;
tail = store;
let bound: *HirNode = Lcx_BindPatIdent(ctx, elem.patIdent, "int", fLoad, line, col);
if bound != null as *HirNode {
if head == null as *HirNode {
head = bound;
tail = bound;
while tail.child3 != null as *HirNode { tail = tail.child3; }
} else {
tail.child3 = bound;
while tail.child3 != null as *HirNode { tail = tail.child3; }
}
}
}
elem = elem.patNext;
@@ -690,38 +765,16 @@ func Lcx_PatternBindings(ctx: *LowerCtx, subject: *HirNode, pat: *Pattern,
fLoad.column = col;
fLoad.child1 = fPtr;
fLoad.typeName = "int";
let alloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
alloca.kind = hAlloca;
alloca.line = line;
alloca.column = col;
alloca.strValue = field.patIdent;
alloca.typeName = "int";
let store: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
store.kind = hStore;
store.line = line;
store.column = col;
let v: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
v.kind = hVar;
v.strValue = field.patIdent;
store.child1 = v;
store.child2 = fLoad;
alloca.child3 = store;
var bsym: Symbol;
bsym.kind = skVar;
bsym.name = field.patIdent;
bsym.typeKind = tyInt;
bsym.typeName = "int";
bsym.refType = null as *TypeExpr;
bsym.isMutable = false;
bsym.isPublic = false;
bsym.decl = null as *Decl;
discard Scope_Define(ctx.scope, bsym);
if head == null as *HirNode {
head = alloca;
tail = store;
} else {
tail.child3 = alloca;
tail = store;
let bound: *HirNode = Lcx_BindPatIdent(ctx, field.patIdent, "int", fLoad, line, col);
if bound != null as *HirNode {
if head == null as *HirNode {
head = bound;
tail = bound;
while tail.child3 != null as *HirNode { tail = tail.child3; }
} else {
tail.child3 = bound;
while tail.child3 != null as *HirNode { tail = tail.child3; }
}
}
}
field = field.patNext;
@@ -827,41 +880,16 @@ func Lcx_PatternBindings(ctx: *LowerCtx, subject: *HirNode, pat: *Pattern,
fLoad.typeName = ftype;
if arg.kind == pkIdent && !String_Eq(arg.patIdent, "_") {
let alloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
alloca.kind = hAlloca;
alloca.line = line;
alloca.column = col;
alloca.strValue = arg.patIdent;
alloca.typeName = ftype;
let store: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
store.kind = hStore;
store.line = line;
store.column = col;
let bv: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
bv.kind = hVar;
bv.strValue = arg.patIdent;
store.child1 = bv;
store.child2 = fLoad;
alloca.child3 = store;
var bsym: Symbol;
bsym.kind = skVar;
bsym.name = arg.patIdent;
bsym.typeKind = tyInt;
bsym.typeName = ftype;
bsym.refType = null as *TypeExpr;
bsym.isMutable = false;
bsym.isPublic = false;
bsym.decl = null as *Decl;
discard Scope_Define(ctx.scope, bsym);
if head == null as *HirNode {
head = alloca;
tail = store;
} else {
tail.child3 = alloca;
tail = store;
let bound: *HirNode = Lcx_BindPatIdent(ctx, arg.patIdent, ftype, fLoad, line, col);
if bound != null as *HirNode {
if head == null as *HirNode {
head = bound;
tail = bound;
while tail.child3 != null as *HirNode { tail = tail.child3; }
} else {
tail.child3 = bound;
while tail.child3 != null as *HirNode { tail = tail.child3; }
}
}
} else if arg.kind == pkTuple || arg.kind == pkStruct || arg.kind == pkEnum {
// Nested pattern on payload field
@@ -913,7 +941,9 @@ func Lcx_AppendToChain(head: *HirNode, node: *HirNode) {
cur.child3 = node;
}
// Lower match expr → hBlock: alloca result; if-else stores; strValue = result name
// Lower match expr → sequential ifs with a found flag (no shared HIR DAG).
// Each arm: if (!found) { if (cond) { binds; if (guard) { result=body; found=true; } } }
// Guards see pattern bindings. Supports pkGuarded (`p if cond`).
func Lcx_LowerMatch(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
let line: uint32 = expr.line;
let col: uint32 = expr.column;
@@ -921,6 +951,8 @@ func Lcx_LowerMatch(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
ctx.varCounter = ctx.varCounter + 1;
let resultName: String = String_Concat("__match_", String_FromInt(ctx.varCounter as int64));
ctx.varCounter = ctx.varCounter + 1;
let foundName: String = String_Concat("__found_", String_FromInt(ctx.varCounter as int64));
// Result type from sema refType, default int
var typeName: String = "int";
@@ -938,7 +970,7 @@ func Lcx_LowerMatch(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
}
}
// Alloca result
// Alloca result + found flag
let allocaNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
allocaNode.kind = hAlloca;
allocaNode.line = line;
@@ -946,35 +978,48 @@ func Lcx_LowerMatch(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
allocaNode.strValue = resultName;
allocaNode.typeName = typeName;
// Collect arms into a temporary array via reverse build of if-chain
// First count arms and build from last to first
var armCount: int = 0;
var arm: *MatchArm = expr.matchArms;
while arm != null as *MatchArm {
armCount = armCount + 1;
arm = arm.next;
}
let foundAlloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
foundAlloca.kind = hAlloca;
foundAlloca.line = line;
foundAlloca.column = col;
foundAlloca.strValue = foundName;
foundAlloca.typeName = "bool";
allocaNode.child3 = foundAlloca;
// Build if-else from last arm to first
var ifChain: *HirNode = null as *HirNode;
var ai: int = armCount - 1;
while ai >= 0 {
// Find arm at index ai
var cur: *MatchArm = expr.matchArms;
var j: int = 0;
while j < ai && cur != null as *MatchArm {
cur = cur.next;
j = j + 1;
}
if cur == null as *MatchArm {
ai = ai - 1;
continue;
}
let foundInit: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
foundInit.kind = hStore;
foundInit.line = line;
foundInit.column = col;
let foundVar0: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
foundVar0.kind = hVar;
foundVar0.strValue = foundName;
foundInit.child1 = foundVar0;
foundInit.child2 = Lcx_MakeLitHir(tkBoolLiteral, "false", line, col);
foundAlloca.child3 = foundInit;
// Pattern bindings before body (so body idents resolve)
let bindHead: *HirNode = Lcx_PatternBindings(ctx, subject, cur.pattern, subjectEnumName, subjectHasData, line, col);
// Chain of arm ifs (forward order), linked via child3
var tail: *HirNode = foundInit;
var cur: *MatchArm = expr.matchArms;
while cur != null as *MatchArm {
// Snapshot rename map so this arm's bindings don't leak to later arms
let savedMapCount: int = ctx.patMapCount;
var bindPat: *Pattern = cur.pattern;
var guardExpr: *Expr = null as *Expr;
if cur.pattern != null as *Pattern && cur.pattern.kind == pkGuarded {
bindPat = cur.pattern.patChild1;
guardExpr = cur.pattern.patGuardExpr;
}
// Bindings before guard/body so renames are active and allocas precede uses
let bindHead: *HirNode = Lcx_PatternBindings(ctx, subject, bindPat, subjectEnumName, subjectHasData, line, col);
var guardHirEarly: *HirNode = null as *HirNode;
if guardExpr != null as *Expr {
guardHirEarly = Lcx_LowerExpr(ctx, guardExpr);
}
let bodyHir: *HirNode = Lcx_LowerExpr(ctx, cur.body);
// result = body (expand block/match yield: run stmts then store result var)
// Pop this arm's renames (nested matches already restored themselves)
ctx.patMapCount = savedMapCount;
// store result = body
let storeNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
storeNode.kind = hStore;
storeNode.line = line;
@@ -992,59 +1037,95 @@ func Lcx_LowerMatch(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
storeNode.child2 = bodyHir;
}
// armBlock = bindings → body stmts → store
let armBlock: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
armBlock.kind = hBlock;
armBlock.line = line;
armBlock.column = col;
var chainHead: *HirNode = bindHead;
if chainHead == null as *HirNode {
chainHead = bodyPrefix;
} else if bodyPrefix != null as *HirNode {
var bt0: *HirNode = chainHead;
while bt0.child3 != null as *HirNode { bt0 = bt0.child3; }
bt0.child3 = bodyPrefix;
}
if chainHead != null as *HirNode {
armBlock.child1 = chainHead;
var bt: *HirNode = chainHead;
while bt.child3 != null as *HirNode { bt = bt.child3; }
bt.child3 = storeNode;
// found = true
let foundSet: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
foundSet.kind = hStore;
foundSet.line = line;
foundSet.column = col;
let foundVar1: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
foundVar1.kind = hVar;
foundVar1.strValue = foundName;
foundSet.child1 = foundVar1;
foundSet.child2 = Lcx_MakeLitHir(tkBoolLiteral, "true", line, col);
storeNode.child3 = foundSet;
// success block: bodyPrefix → store → found=true
let successBlock: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
successBlock.kind = hBlock;
successBlock.line = line;
successBlock.column = col;
if bodyPrefix != null as *HirNode {
successBlock.child1 = bodyPrefix;
var sbt: *HirNode = bodyPrefix;
while sbt.child3 != null as *HirNode { sbt = sbt.child3; }
sbt.child3 = storeNode;
} else {
armBlock.child1 = storeNode;
successBlock.child1 = storeNode;
}
let cond: *HirNode = Lcx_PatternCond(ctx, subject, cur.pattern, subjectEnumName, subjectHasData, line, col);
if cond == null as *HirNode {
// Always-true arm
if ifChain == null as *HirNode {
ifChain = armBlock;
} else {
let ifNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
ifNode.kind = hIf;
ifNode.line = line;
ifNode.column = col;
ifNode.child1 = Lcx_MakeTrueHir(line, col);
ifNode.child2 = armBlock;
ifNode.extraData = ifChain as *void;
ifChain = ifNode;
}
} else {
let ifNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
ifNode.kind = hIf;
ifNode.line = line;
ifNode.column = col;
ifNode.child1 = cond;
ifNode.child2 = armBlock;
ifNode.extraData = ifChain as *void;
ifChain = ifNode;
// Optional guard wraps success (already lowered with renames active)
var afterBinds: *HirNode = successBlock;
if guardHirEarly != null as *HirNode {
let guardIf: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
guardIf.kind = hIf;
guardIf.line = line;
guardIf.column = col;
guardIf.child1 = guardHirEarly;
guardIf.child2 = successBlock;
afterBinds = guardIf;
}
ai = ai - 1;
// armInner: binds → afterBinds
let armInner: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
armInner.kind = hBlock;
armInner.line = line;
armInner.column = col;
if bindHead != null as *HirNode {
armInner.child1 = bindHead;
var ibt: *HirNode = bindHead;
while ibt.child3 != null as *HirNode { ibt = ibt.child3; }
ibt.child3 = afterBinds;
} else {
armInner.child1 = afterBinds;
}
// Optional pattern condition
let cond: *HirNode = Lcx_PatternCond(ctx, subject, bindPat, subjectEnumName, subjectHasData, line, col);
var armBody: *HirNode = armInner;
if cond != null as *HirNode {
let condIf: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
condIf.kind = hIf;
condIf.line = line;
condIf.column = col;
condIf.child1 = cond;
condIf.child2 = armInner;
armBody = condIf;
}
// if (!found) armBody
let foundLoad: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
foundLoad.kind = hVar;
foundLoad.strValue = foundName;
foundLoad.typeName = "bool";
let notFound: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
notFound.kind = hUnary;
notFound.line = line;
notFound.column = col;
notFound.intValue = tkBang;
notFound.child1 = foundLoad;
let tryIf: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
tryIf.kind = hIf;
tryIf.line = line;
tryIf.column = col;
tryIf.child1 = notFound;
tryIf.child2 = armBody;
tail.child3 = tryIf;
tail = tryIf;
cur = cur.next;
}
// Chain: alloca → ifChain
allocaNode.child3 = ifChain;
let block: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
block.kind = hBlock;
block.line = line;
@@ -1153,6 +1234,18 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
// Identifier → variable reference
if kind == ekIdent {
// Pattern binding rename: source name → unique C local
let ren: String = Lcx_PatLookup(ctx, expr.strValue);
if !String_Eq(ren, "") {
n.kind = hVar;
n.strValue = ren;
let rsym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
n.typeKind = rsym.typeKind;
if rsym.typeName != null as String && !String_Eq(rsym.typeName, "") {
n.typeName = rsym.typeName;
}
return n;
}
// Capture rewriting: if inside closure body and this ident is captured,
// emit field access on env instance instead of bare variable
if ctx.closureDepth > 0 && ctx.currentClosureExpr != null as *Expr && !String_Eq(ctx.envInstanceName, "") {
@@ -1564,18 +1657,65 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
n.kind = hCall;
n.strValue = expr.child1.strValue;
// Generic call monomorphization
if expr.child1 != null as *Expr && expr.child1.genericTypeArgCount > 0 {
let genDecl: *Decl = Lcx_FindGenericFunc(ctx, expr.child1.strValue);
if genDecl != null as *Decl {
var typeArg0: String = expr.child1.genericTypeArg0;
var typeArg1: String = expr.child1.genericTypeArg1;
if String_Eq(typeArg0, ctx.substParam0) { typeArg0 = ctx.substArg0; }
if String_Eq(typeArg0, ctx.substParam1) { typeArg0 = ctx.substArg1; }
if String_Eq(typeArg1, ctx.substParam0) { typeArg1 = ctx.substArg0; }
if String_Eq(typeArg1, ctx.substParam1) { typeArg1 = ctx.substArg1; }
let mangled: String = Lcx_GenerateFuncInstance(ctx, genDecl, typeArg0, typeArg1, expr.child1.genericTypeArgCount);
n.strValue = mangled;
// Generic call monomorphization (explicit / inferred type args)
if expr.child1 != null as *Expr {
var argc: int = expr.child1.genericTypeArgCount;
var typeArg0: String = expr.child1.genericTypeArg0;
var typeArg1: String = expr.child1.genericTypeArg1;
// Fallback: infer T from first *Array/*Iter arg when sema left count=0
if argc == 0 {
let genTry: *Decl = Lcx_FindGenericFunc(ctx, expr.child1.strValue);
if genTry != null as *Decl && genTry.typeParamCount > 0 {
if expr.callArgs != null as *ExprList && expr.callArgs.expr != null as *Expr {
var a0: *Expr = expr.callArgs.expr;
var te: *TypeExpr = a0.refType;
if te == null as *TypeExpr && a0.kind == ekUnary && a0.intValue == tkAmp {
if a0.child1 != null as *Expr { te = a0.child1.refType; }
}
if te != null as *TypeExpr {
// Unwrap pointer
if (te.kind == tekPointer || te.kind == tekRef || te.kind == tekMutRef)
&& te.pointerPointee != null as *TypeExpr {
te = te.pointerPointee;
}
var elem: String = "";
if te.typeArgCount > 0 {
elem = te.typeArgName0;
} else {
// Mangled Array_int / Iter_String
elem = Lcx_ExtractElemFromName(te.typeName);
}
if !String_Eq(elem, "") {
typeArg0 = elem;
argc = 1;
// Second type arg from func-typed second argument if needed
if genTry.typeParamCount >= 2 && expr.callArgs.next != null as *ExprList {
let a1e: *Expr = expr.callArgs.next.expr;
if a1e != null as *Expr && a1e.kind == ekIdent {
let fsym: Symbol = Scope_Lookup(ctx.scope, a1e.strValue);
if fsym.kind == skFunc && fsym.decl != null as *Decl
&& fsym.decl.retType != null as *TypeExpr
&& fsym.decl.retType.kind == tekNamed {
typeArg1 = fsym.decl.retType.typeName;
argc = 2;
}
}
}
}
}
}
}
}
if argc > 0 {
let genDecl: *Decl = Lcx_FindGenericFunc(ctx, expr.child1.strValue);
if genDecl != null as *Decl {
if String_Eq(typeArg0, ctx.substParam0) { typeArg0 = ctx.substArg0; }
if String_Eq(typeArg0, ctx.substParam1) { typeArg0 = ctx.substArg1; }
if String_Eq(typeArg1, ctx.substParam0) { typeArg1 = ctx.substArg0; }
if String_Eq(typeArg1, ctx.substParam1) { typeArg1 = ctx.substArg1; }
let mangled: String = Lcx_GenerateFuncInstance(ctx, genDecl, typeArg0, typeArg1, argc);
n.strValue = mangled;
}
}
}
@@ -2476,10 +2616,33 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
// Return
if kind == skReturn {
n.kind = hReturn;
if stmt.child1 != null as *Expr {
n.child1 = Lcx_LowerExpr(ctx, stmt.child1);
let retVal: *HirNode = Lcx_LowerExpr(ctx, stmt.child1);
// `return match { ... }` / other multi-stmt yields: expand stmts then return result var
if retVal != null as *HirNode && Lcx_IsMatchYield(retVal) {
let retVar: *HirNode = Lcx_YieldVarOf(retVal);
let retNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
retNode.kind = hReturn;
retNode.line = line;
retNode.column = col;
retNode.child1 = retVar;
retVal.strValue = "";
var lastIn: *HirNode = retVal.child1;
if lastIn == null as *HirNode {
retVal.child1 = retNode;
} else {
while lastIn.child3 != null as *HirNode {
lastIn = lastIn.child3;
}
lastIn.child3 = retNode;
}
return retVal;
}
n.kind = hReturn;
n.child1 = retVal;
return n;
}
n.kind = hReturn;
return n;
}