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:
+3
-1
@@ -66,6 +66,7 @@ const pkRange: int = 3;
|
||||
const pkEnum: int = 4;
|
||||
const pkStruct: int = 5;
|
||||
const pkTuple: int = 6;
|
||||
const pkGuarded: int = 7; // `p if cond` — patChild1 = inner, patGuardExpr = condition
|
||||
|
||||
struct Pattern {
|
||||
kind: int,
|
||||
@@ -78,10 +79,11 @@ struct Pattern {
|
||||
patEnumPath: String, // for pkEnum: "Enum::Variant"
|
||||
patStructName: String, // for pkStruct (type name)
|
||||
patFieldName: String, // for struct field entry: field name in Point { x: a }
|
||||
patChild1: *Pattern, // range lo / nested
|
||||
patChild1: *Pattern, // range lo / nested / guarded inner
|
||||
patChild2: *Pattern, // range hi / nested
|
||||
patArgs: *Pattern, // pkEnum/pkTuple/pkStruct field list (head)
|
||||
patNext: *Pattern, // next sibling in patArgs list
|
||||
patGuardExpr: *Expr, // for pkGuarded: the `if` condition
|
||||
}
|
||||
|
||||
// Match arm: pattern => body
|
||||
|
||||
+91
-30
@@ -75,6 +75,8 @@ struct CEmitter {
|
||||
movedName5: String,
|
||||
movedName6: String,
|
||||
movedName7: String,
|
||||
tmpCounter: int,
|
||||
currentRetType: String,
|
||||
}
|
||||
|
||||
func CBE_PushDefer(cbe: *CEmitter, node: *HirNode) {
|
||||
@@ -155,36 +157,56 @@ func CBE_GetAutoDropVarName(node: *HirNode) -> String {
|
||||
return varNode.strValue;
|
||||
}
|
||||
|
||||
// Emit one defer slot (shared by full-stack and scope-pop emitters).
|
||||
func CBE_EmitOneDefer(cbe: *CEmitter, i: int) {
|
||||
var dn: *HirNode = null as *HirNode;
|
||||
if i == 0 { dn = cbe.defer0; }
|
||||
if i == 1 { dn = cbe.defer1; }
|
||||
if i == 2 { dn = cbe.defer2; }
|
||||
if i == 3 { dn = cbe.defer3; }
|
||||
if i == 4 { dn = cbe.defer4; }
|
||||
if i == 5 { dn = cbe.defer5; }
|
||||
if i == 6 { dn = cbe.defer6; }
|
||||
if i == 7 { dn = cbe.defer7; }
|
||||
// Skip auto-drop for moved variables
|
||||
let deferVarName: String = CBE_GetAutoDropVarName(dn);
|
||||
if !String_Eq(deferVarName, "") && CBE_IsMoved(cbe, deferVarName) {
|
||||
return;
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "\n");
|
||||
var sp: int = 0;
|
||||
while sp < cbe.indent {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
sp = sp + 1;
|
||||
}
|
||||
CBE_EmitExpr(cbe, dn);
|
||||
StringBuilder_Append(&cbe.sb, ";");
|
||||
}
|
||||
|
||||
// Emit all active defers (LIFO) without clearing the stack.
|
||||
// Must NOT clear: multiple return paths each need the full defer list.
|
||||
// (Clearing caused Early(flag) { if (0) return; return 1 } to drop only on first exit.)
|
||||
// Stack is reset at the start of each function emission.
|
||||
func CBE_EmitDefers(cbe: *CEmitter) -> int {
|
||||
if cbe.deferCount == 0 { return 0; }
|
||||
var i: int = cbe.deferCount - 1;
|
||||
while i >= 0 {
|
||||
var dn: *HirNode = null as *HirNode;
|
||||
if i == 0 { dn = cbe.defer0; }
|
||||
if i == 1 { dn = cbe.defer1; }
|
||||
if i == 2 { dn = cbe.defer2; }
|
||||
if i == 3 { dn = cbe.defer3; }
|
||||
if i == 4 { dn = cbe.defer4; }
|
||||
if i == 5 { dn = cbe.defer5; }
|
||||
if i == 6 { dn = cbe.defer6; }
|
||||
if i == 7 { dn = cbe.defer7; }
|
||||
// Skip auto-drop for moved variables
|
||||
let deferVarName: String = CBE_GetAutoDropVarName(dn);
|
||||
if !String_Eq(deferVarName, "") && CBE_IsMoved(cbe, deferVarName) {
|
||||
i = i - 1;
|
||||
continue;
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "\n");
|
||||
var sp: int = 0;
|
||||
while sp < cbe.indent {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
sp = sp + 1;
|
||||
}
|
||||
CBE_EmitExpr(cbe, dn);
|
||||
StringBuilder_Append(&cbe.sb, ";");
|
||||
CBE_EmitOneDefer(cbe, i);
|
||||
i = i - 1;
|
||||
}
|
||||
cbe.deferCount = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Emit branch/loop-local defers (indices fromIdx..count-1) then pop them.
|
||||
// Outer defers stay live so sibling branches and later returns still drop correctly.
|
||||
func CBE_EmitAndPopDefersFrom(cbe: *CEmitter, fromIdx: int) -> int {
|
||||
if cbe.deferCount <= fromIdx { return 0; }
|
||||
var i: int = cbe.deferCount - 1;
|
||||
while i >= fromIdx {
|
||||
CBE_EmitOneDefer(cbe, i);
|
||||
i = i - 1;
|
||||
}
|
||||
cbe.deferCount = fromIdx;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -381,20 +403,48 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return
|
||||
// Return — evaluate value first, then drop live locals, then return.
|
||||
// (Emitting Drop before the value used to use-after-drop on `return a.id`.)
|
||||
if kind == hReturn {
|
||||
// Track moved variables via return
|
||||
// Track moved variables via return (skip auto-drop of moved-out locals)
|
||||
if node.child1 != null as *HirNode && node.child1.kind == hVar {
|
||||
CBE_AddMoved(cbe, node.child1.strValue);
|
||||
}
|
||||
let hadDefers: int = CBE_EmitDefers(cbe);
|
||||
if hadDefers != 0 {
|
||||
if node.child1 != null as *HirNode && cbe.deferCount > 0 {
|
||||
// Materialize into a temp so Drop cannot clobber the returned value.
|
||||
// Prefer the enclosing function return type (field-access HIR often
|
||||
// carries the base struct typeName, which is wrong for `return a.id`).
|
||||
cbe.tmpCounter = cbe.tmpCounter + 1;
|
||||
let tmpName: String = String_Concat("__retdrop_", String_FromInt(cbe.tmpCounter));
|
||||
var retCt: String = "int";
|
||||
if cbe.currentRetType != null as String && !String_Eq(cbe.currentRetType, "") && !String_Eq(cbe.currentRetType, "void") {
|
||||
retCt = cbe.currentRetType;
|
||||
} else if node.child1.typeKind != 0 {
|
||||
retCt = CBackend_TypeToC(node.child1.typeKind);
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, CBE_CParamDecl(retCt, tmpName));
|
||||
StringBuilder_Append(&cbe.sb, " = ");
|
||||
CBE_EmitExpr(cbe, node.child1);
|
||||
StringBuilder_Append(&cbe.sb, ";");
|
||||
discard CBE_EmitDefers(cbe);
|
||||
StringBuilder_Append(&cbe.sb, "\n");
|
||||
var sp: int = 0;
|
||||
while sp < cbe.indent {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
sp = sp + 1;
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "return ");
|
||||
StringBuilder_Append(&cbe.sb, tmpName);
|
||||
return;
|
||||
}
|
||||
let hadDefers: int = CBE_EmitDefers(cbe);
|
||||
if hadDefers != 0 {
|
||||
StringBuilder_Append(&cbe.sb, "\n");
|
||||
var sp2: int = 0;
|
||||
while sp2 < cbe.indent {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
sp2 = sp2 + 1;
|
||||
}
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "return");
|
||||
if node.child1 != null as *HirNode {
|
||||
@@ -444,15 +494,17 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If
|
||||
// If — each branch has its own defer scope (locals do not leak)
|
||||
if kind == hIf {
|
||||
|
||||
StringBuilder_Append(&cbe.sb, "if (");
|
||||
CBE_EmitExpr(cbe, node.child1);
|
||||
StringBuilder_Append(&cbe.sb, ") {\n");
|
||||
if node.child2 != null as *HirNode {
|
||||
let savedThen: int = cbe.deferCount;
|
||||
cbe.indent = cbe.indent + 1;
|
||||
CBE_EmitExpr(cbe, node.child2);
|
||||
discard CBE_EmitAndPopDefersFrom(cbe, savedThen);
|
||||
cbe.indent = cbe.indent - 1;
|
||||
}
|
||||
var sp: int = 0;
|
||||
@@ -464,8 +516,10 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
let elseBlock: *HirNode = node.extraData as *HirNode;
|
||||
if elseBlock != null as *HirNode {
|
||||
StringBuilder_Append(&cbe.sb, " else {\n");
|
||||
let savedElse: int = cbe.deferCount;
|
||||
cbe.indent = cbe.indent + 1;
|
||||
CBE_EmitExpr(cbe, elseBlock);
|
||||
discard CBE_EmitAndPopDefersFrom(cbe, savedElse);
|
||||
cbe.indent = cbe.indent - 1;
|
||||
sp = 0;
|
||||
while sp < cbe.indent {
|
||||
@@ -479,14 +533,16 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// While
|
||||
// While — loop-body locals dropped each iteration
|
||||
if kind == hWhile {
|
||||
StringBuilder_Append(&cbe.sb, "while (");
|
||||
CBE_EmitExpr(cbe, node.child1);
|
||||
StringBuilder_Append(&cbe.sb, ") {\n");
|
||||
if node.child2 != null as *HirNode {
|
||||
let savedW: int = cbe.deferCount;
|
||||
cbe.indent = cbe.indent + 1;
|
||||
CBE_EmitExpr(cbe, node.child2);
|
||||
discard CBE_EmitAndPopDefersFrom(cbe, savedW);
|
||||
cbe.indent = cbe.indent - 1;
|
||||
}
|
||||
var sp: int = 0;
|
||||
@@ -502,8 +558,10 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
if kind == hLoop {
|
||||
StringBuilder_Append(&cbe.sb, "while (1) {\n");
|
||||
if node.child1 != null as *HirNode {
|
||||
let savedL: int = cbe.deferCount;
|
||||
cbe.indent = cbe.indent + 1;
|
||||
CBE_EmitExpr(cbe, node.child1);
|
||||
discard CBE_EmitAndPopDefersFrom(cbe, savedL);
|
||||
cbe.indent = cbe.indent - 1;
|
||||
}
|
||||
var sp: int = 0;
|
||||
@@ -1266,6 +1324,7 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
||||
cbe.mod = mod;
|
||||
cbe.deferCount = 0;
|
||||
cbe.movedCount = 0;
|
||||
cbe.tmpCounter = 0;
|
||||
|
||||
// Header
|
||||
StringBuilder_Append(&cbe.sb, "// Generated by Bux C Backend v2\n");
|
||||
@@ -1566,6 +1625,8 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
||||
cbe.checkedFunc = mod.funcs[i].checkedFunc;
|
||||
cbe.deferCount = 0;
|
||||
cbe.movedCount = 0;
|
||||
cbe.tmpCounter = 0;
|
||||
cbe.currentRetType = mod.funcs[i].retTypeName;
|
||||
var hasReturn: bool = false;
|
||||
cbe.indent = 1;
|
||||
CBE_EmitExpr(cbe, body);
|
||||
|
||||
+380
-217
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+34
-2
@@ -58,14 +58,27 @@ func parserPeek(p: *Parser, ahead: int) -> int {
|
||||
return tkEndOfFile;
|
||||
}
|
||||
|
||||
// Lookahead to determine if '<' starts a type argument list.
|
||||
// Lookahead to determine if '<' starts a type argument list (`Foo<int>`).
|
||||
// Must not treat value comparisons `x < 0` as generics when a later `x > 0`
|
||||
// exists (e.g. multiple match arm guards).
|
||||
func parserIsTypeArgListAhead(p: *Parser) -> bool {
|
||||
if !parserCheck(p, tkLt) { return false; }
|
||||
var depth: int = 0;
|
||||
var ahead: int = 0;
|
||||
while true {
|
||||
let kind: int = parserPeek(p, ahead);
|
||||
if kind == tkEndOfFile || kind == tkLBrace || kind == tkSemicolon {
|
||||
// Hard stops: cannot appear inside <...> type args
|
||||
if kind == tkEndOfFile || kind == tkLBrace || kind == tkRBrace || kind == tkSemicolon
|
||||
|| kind == tkFatArrow || kind == tkIf || kind == tkElse || kind == tkWhile
|
||||
|| kind == tkFor || kind == tkMatch || kind == tkReturn || kind == tkLet || kind == tkVar
|
||||
|| kind == tkEq || kind == tkNe || kind == tkLe || kind == tkGe
|
||||
|| kind == tkAmpAmp || kind == tkPipePipe || kind == tkAssign {
|
||||
return false;
|
||||
}
|
||||
// Literals / arithmetic ⇒ value expression, not type args
|
||||
if kind == tkIntLiteral || kind == tkFloatLiteral || kind == tkStringLiteral
|
||||
|| kind == tkCharLiteral || kind == tkBoolLiteral
|
||||
|| kind == tkPlus || kind == tkMinus || kind == tkSlash || kind == tkPercent {
|
||||
return false;
|
||||
}
|
||||
if kind == tkLt {
|
||||
@@ -729,6 +742,7 @@ func parserMakePattern(kind: int, line: uint32, col: uint32) -> *Pattern {
|
||||
pat.patChild2 = null as *Pattern;
|
||||
pat.patArgs = null as *Pattern;
|
||||
pat.patNext = null as *Pattern;
|
||||
pat.patGuardExpr = null as *Expr;
|
||||
return pat;
|
||||
}
|
||||
|
||||
@@ -904,6 +918,24 @@ func parserParsePattern(p: *Parser) -> *Pattern {
|
||||
pat.patRangeInclusive = inclusive;
|
||||
pat.patChild1 = left;
|
||||
pat.patChild2 = right;
|
||||
// Range can still take a guard: `1..10 if x % 2 == 0`
|
||||
if parserCheck(p, tkIf) {
|
||||
discard parserAdvance(p);
|
||||
let guard: *Expr = parserParseExpr(p);
|
||||
let gpat: *Pattern = parserMakePattern(pkGuarded, line, col);
|
||||
gpat.patChild1 = pat;
|
||||
gpat.patGuardExpr = guard;
|
||||
return gpat;
|
||||
}
|
||||
return pat;
|
||||
}
|
||||
// Guarded pattern: `p if cond` (bindings from p visible in cond)
|
||||
if parserCheck(p, tkIf) {
|
||||
discard parserAdvance(p);
|
||||
let guard: *Expr = parserParseExpr(p);
|
||||
let pat: *Pattern = parserMakePattern(pkGuarded, line, col);
|
||||
pat.patChild1 = left;
|
||||
pat.patGuardExpr = guard;
|
||||
return pat;
|
||||
}
|
||||
return left;
|
||||
|
||||
+253
-24
@@ -388,6 +388,11 @@ func Sema_AddCapture(closureExpr: *Expr, name: String, typeKind: int) {
|
||||
// Enum payloads: Option::Some(value) → value:int (from variant field type).
|
||||
func Sema_BindPattern(sema: *Sema, pat: *Pattern, subject: *Expr) {
|
||||
if pat == null as *Pattern { return; }
|
||||
// Guarded: bind from inner pattern only (`p if cond`)
|
||||
if pat.kind == pkGuarded {
|
||||
Sema_BindPattern(sema, pat.patChild1, subject);
|
||||
return;
|
||||
}
|
||||
if pat.kind == pkIdent {
|
||||
var sym: Symbol;
|
||||
Sema_ZeroInitSymbol(&sym);
|
||||
@@ -807,8 +812,7 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
||||
ai = ai + 1;
|
||||
}
|
||||
}
|
||||
// Trait bounds checking for explicit generic calls: Max<Circle>(...)
|
||||
// Must happen before indirect/direct call returns
|
||||
// Trait bounds + inference for generic calls: Max / Iter_Map / Array_Push
|
||||
if expr.child1.kind == ekIdent {
|
||||
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
|
||||
if sym.kind == skFunc && sym.decl != null as *Decl {
|
||||
@@ -831,13 +835,21 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
||||
return tyVoid;
|
||||
}
|
||||
}
|
||||
// Direct call to named function
|
||||
// Direct call to named function — substitute return type with inferred args
|
||||
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);
|
||||
var retTe: *TypeExpr = sym.decl.retType;
|
||||
// Substitute type params in return type when we inferred args
|
||||
if expr.child1.genericTypeArgCount > 0 {
|
||||
retTe = Sema_SubstTypeExpr(sym.decl.retType,
|
||||
sym.decl.typeParam0, expr.child1.genericTypeArg0,
|
||||
sym.decl.typeParam1, expr.child1.genericTypeArg1,
|
||||
expr.child1.genericTypeArgCount);
|
||||
}
|
||||
expr.refType = retTe;
|
||||
return Sema_ResolveType(sema, retTe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -851,6 +863,10 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
||||
|
||||
// Cast — return target type
|
||||
if kind == ekCast {
|
||||
// Must type-check the operand (enables generic inference on nested calls)
|
||||
if expr.child1 != null as *Expr {
|
||||
discard Sema_CheckExpr(sema, expr.child1);
|
||||
}
|
||||
if expr.refType != null as *TypeExpr {
|
||||
return Sema_ResolveType(sema, expr.refType);
|
||||
}
|
||||
@@ -990,6 +1006,16 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
||||
let savedScope: *Scope = sema.scope;
|
||||
sema.scope = &armScope;
|
||||
Sema_BindPattern(sema, arm.pattern, expr.child1);
|
||||
// Type-check `p if guard` (must be bool; sees pattern bindings)
|
||||
if arm.pattern != null as *Pattern && arm.pattern.kind == pkGuarded {
|
||||
if arm.pattern.patGuardExpr != null as *Expr {
|
||||
let gt: int = Sema_CheckExpr(sema, arm.pattern.patGuardExpr);
|
||||
if gt != tyBool && gt != tyUnknown {
|
||||
Sema_EmitError(sema, arm.pattern.line, arm.pattern.column,
|
||||
"match guard condition must be bool");
|
||||
}
|
||||
}
|
||||
}
|
||||
let bt: int = Sema_CheckExpr(sema, arm.body);
|
||||
sema.scope = savedScope;
|
||||
if first {
|
||||
@@ -1677,19 +1703,213 @@ func Sema_ExtractElemType(te: *TypeExpr) -> String {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Infer generic type argument from call arguments.
|
||||
// Supports Array_* and Iter_* stdlib functions.
|
||||
// Substitute type params in a TypeExpr (shallow clone). Used for call return types
|
||||
// after inference: Array<U> + U=String → Array with typeArgName0=String / Array_String.
|
||||
func Sema_SubstTypeExpr(te: *TypeExpr, p0: String, a0: String, p1: String, a1: String, argc: int) -> *TypeExpr {
|
||||
if te == null as *TypeExpr { return null as *TypeExpr; }
|
||||
let r: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||
r.kind = te.kind;
|
||||
r.line = te.line;
|
||||
r.column = te.column;
|
||||
r.typeName = te.typeName;
|
||||
r.pathStr = te.pathStr;
|
||||
r.pathCount = te.pathCount;
|
||||
r.typeArgName0 = te.typeArgName0;
|
||||
r.typeArgName1 = te.typeArgName1;
|
||||
r.typeArgCount = te.typeArgCount;
|
||||
r.sliceElement = te.sliceElement;
|
||||
r.pointerPointee = te.pointerPointee;
|
||||
r.funcParams = te.funcParams;
|
||||
r.funcRet = te.funcRet;
|
||||
r.funcParamCount = te.funcParamCount;
|
||||
r.tupleElems = te.tupleElems;
|
||||
r.tupleCount = te.tupleCount;
|
||||
|
||||
if te.kind == tekNamed {
|
||||
// Bare type param → concrete named type
|
||||
if te.typeArgCount == 0 {
|
||||
if argc >= 1 && String_Eq(te.typeName, p0) {
|
||||
r.typeName = a0;
|
||||
return r;
|
||||
}
|
||||
if argc >= 2 && String_Eq(te.typeName, p1) {
|
||||
r.typeName = a1;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
// Named with type args: Array<U> → Array_String (mangled) for downstream mono
|
||||
if te.typeArgCount > 0 {
|
||||
var na0: String = te.typeArgName0;
|
||||
var na1: String = te.typeArgName1;
|
||||
if argc >= 1 && String_Eq(na0, p0) { na0 = a0; }
|
||||
if argc >= 2 && String_Eq(na0, p1) { na0 = a1; }
|
||||
if argc >= 1 && String_Eq(na1, p0) { na1 = a0; }
|
||||
if argc >= 2 && String_Eq(na1, p1) { na1 = a1; }
|
||||
r.typeArgName0 = na0;
|
||||
r.typeArgName1 = na1;
|
||||
// Mangle for monomorphized struct name (Array_int, Iter_String)
|
||||
if te.typeArgCount == 1 && !String_Eq(na0, "") {
|
||||
r.typeName = String_Concat(String_Concat(te.typeName, "_"), na0);
|
||||
r.typeArgCount = 0;
|
||||
r.typeArgName0 = "";
|
||||
} else if te.typeArgCount >= 2 {
|
||||
r.typeName = String_Concat(String_Concat(te.typeName, "_"),
|
||||
String_Concat(na0, String_Concat("_", na1)));
|
||||
r.typeArgCount = 0;
|
||||
r.typeArgName0 = "";
|
||||
r.typeArgName1 = "";
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if te.kind == tekPointer || te.kind == tekRef || te.kind == tekMutRef {
|
||||
r.pointerPointee = Sema_SubstTypeExpr(te.pointerPointee, p0, a0, p1, a1, argc);
|
||||
return r;
|
||||
}
|
||||
if te.kind == tekFunc {
|
||||
r.funcRet = Sema_SubstTypeExpr(te.funcRet, p0, a0, p1, a1, argc);
|
||||
return r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Bind a type param name on the call callee if not already set.
|
||||
func Sema_BindInferredArg(expr: *Expr, funcDecl: *Decl, tpName: String, typeName: String) {
|
||||
if String_Eq(tpName, "") || String_Eq(typeName, "") { return; }
|
||||
if expr.child1 == null as *Expr { return; }
|
||||
if funcDecl.typeParamCount >= 1 && String_Eq(tpName, funcDecl.typeParam0) {
|
||||
if String_Eq(expr.child1.genericTypeArg0, "") {
|
||||
expr.child1.genericTypeArg0 = typeName;
|
||||
}
|
||||
if expr.child1.genericTypeArgCount < 1 { expr.child1.genericTypeArgCount = 1; }
|
||||
}
|
||||
if funcDecl.typeParamCount >= 2 && String_Eq(tpName, funcDecl.typeParam1) {
|
||||
if String_Eq(expr.child1.genericTypeArg1, "") {
|
||||
expr.child1.genericTypeArg1 = typeName;
|
||||
}
|
||||
if expr.child1.genericTypeArgCount < 2 { expr.child1.genericTypeArgCount = 2; }
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve concrete type name for a value expression (for bare type-param params).
|
||||
func Sema_ArgTypeName(argExpr: *Expr) -> String {
|
||||
if argExpr == null as *Expr { return ""; }
|
||||
var argType: *TypeExpr = argExpr.refType;
|
||||
if argType == null as *TypeExpr && argExpr.kind == ekUnary && argExpr.intValue == tkAmp {
|
||||
if argExpr.child1 != null as *Expr { argType = argExpr.child1.refType; }
|
||||
}
|
||||
if argType == null as *TypeExpr { return ""; }
|
||||
if argType.kind == tekNamed { return argType.typeName; }
|
||||
if argType.kind == tekPointer && argType.pointerPointee != null as *TypeExpr {
|
||||
if argType.pointerPointee.kind == tekNamed {
|
||||
return argType.pointerPointee.typeName;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Infer type args from a param TypeExpr pattern against a concrete arg TypeExpr.
|
||||
func Sema_UnifyInfer(expr: *Expr, funcDecl: *Decl, pattern: *TypeExpr, concrete: *TypeExpr) {
|
||||
if pattern == null as *TypeExpr || concrete == null as *TypeExpr { return; }
|
||||
|
||||
// Bare type param: T / Acc / U
|
||||
if pattern.kind == tekNamed && pattern.typeArgCount == 0 {
|
||||
if String_Eq(pattern.typeName, funcDecl.typeParam0) || String_Eq(pattern.typeName, funcDecl.typeParam1) {
|
||||
var cn: String = "";
|
||||
if concrete.kind == tekNamed { cn = concrete.typeName; }
|
||||
Sema_BindInferredArg(expr, funcDecl, pattern.typeName, cn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// *T / &T — unwrap pointer/ref on both sides
|
||||
if pattern.kind == tekPointer || pattern.kind == tekRef || pattern.kind == tekMutRef {
|
||||
var conc: *TypeExpr = concrete;
|
||||
if concrete.kind == tekPointer || concrete.kind == tekRef || concrete.kind == tekMutRef {
|
||||
conc = concrete.pointerPointee;
|
||||
}
|
||||
Sema_UnifyInfer(expr, funcDecl, pattern.pointerPointee, conc);
|
||||
return;
|
||||
}
|
||||
|
||||
// Named with type args: Iter<T>, Array<U>, Map<K,V>
|
||||
if pattern.kind == tekNamed && pattern.typeArgCount > 0 {
|
||||
// concrete may be Iter with typeArgName0, or mangled Iter_int
|
||||
if concrete.kind == tekNamed {
|
||||
if concrete.typeArgCount > 0 {
|
||||
if pattern.typeArgCount >= 1 && !String_Eq(pattern.typeArgName0, "") {
|
||||
let te0: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||
te0.kind = tekNamed;
|
||||
te0.typeName = pattern.typeArgName0;
|
||||
te0.typeArgCount = 0;
|
||||
let ce0: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||
ce0.kind = tekNamed;
|
||||
ce0.typeName = concrete.typeArgName0;
|
||||
ce0.typeArgCount = 0;
|
||||
Sema_UnifyInfer(expr, funcDecl, te0, ce0);
|
||||
}
|
||||
if pattern.typeArgCount >= 2 && !String_Eq(pattern.typeArgName1, "") {
|
||||
let te1: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||
te1.kind = tekNamed;
|
||||
te1.typeName = pattern.typeArgName1;
|
||||
te1.typeArgCount = 0;
|
||||
let ce1: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||
ce1.kind = tekNamed;
|
||||
ce1.typeName = concrete.typeArgName1;
|
||||
ce1.typeArgCount = 0;
|
||||
Sema_UnifyInfer(expr, funcDecl, te1, ce1);
|
||||
}
|
||||
} else {
|
||||
// Mangled Array_int / Iter_String
|
||||
let elem: String = Sema_ExtractElemType(concrete);
|
||||
if !String_Eq(elem, "") && pattern.typeArgCount >= 1 {
|
||||
let te0: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||
te0.kind = tekNamed;
|
||||
te0.typeName = pattern.typeArgName0;
|
||||
te0.typeArgCount = 0;
|
||||
let ce0: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||
ce0.kind = tekNamed;
|
||||
ce0.typeName = elem;
|
||||
ce0.typeArgCount = 0;
|
||||
Sema_UnifyInfer(expr, funcDecl, te0, ce0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// func(T)->U vs concrete function type
|
||||
if pattern.kind == tekFunc {
|
||||
var conc: *TypeExpr = concrete;
|
||||
// Named function used as value: build tekFunc from its decl if needed — refType may already be tekFunc
|
||||
if conc.kind == tekFunc {
|
||||
// Params
|
||||
var pp: *TypeExprList = pattern.funcParams;
|
||||
var cp: *TypeExprList = conc.funcParams;
|
||||
while pp != null as *TypeExprList && cp != null as *TypeExprList {
|
||||
Sema_UnifyInfer(expr, funcDecl, pp.te, cp.te);
|
||||
pp = pp.next;
|
||||
cp = cp.next;
|
||||
}
|
||||
if pattern.funcRet != null as *TypeExpr && conc.funcRet != null as *TypeExpr {
|
||||
Sema_UnifyInfer(expr, funcDecl, pattern.funcRet, conc.funcRet);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Infer generic type arguments from call arguments (structural).
|
||||
// Handles *Array<T>, *Iter<T>, func(T)->U, bare Acc, etc.
|
||||
func Sema_InferGenericArgs(sema: *Sema, funcDecl: *Decl, expr: *Expr) {
|
||||
if expr.callArgs == null as *ExprList { return; }
|
||||
if expr.child1 == null as *Expr { 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; }
|
||||
@@ -1701,20 +1921,29 @@ func Sema_InferGenericArgs(sema: *Sema, funcDecl: *Decl, expr: *Expr) {
|
||||
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;
|
||||
var argType: *TypeExpr = argExpr.refType;
|
||||
// &x → use type of x, wrap as pointer if pattern expects pointer
|
||||
if argType == null as *TypeExpr && argExpr.kind == ekUnary && argExpr.intValue == tkAmp {
|
||||
if argExpr.child1 != null as *Expr {
|
||||
argType = argExpr.child1.refType;
|
||||
}
|
||||
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; }
|
||||
}
|
||||
// Named function as value: synthesize tekFunc from its declaration
|
||||
if (argType == null as *TypeExpr || argType.kind != tekFunc) && argExpr.kind == ekIdent {
|
||||
let fsym: Symbol = Scope_Lookup(sema.scope, argExpr.strValue);
|
||||
if fsym.kind == skFunc && fsym.decl != null as *Decl {
|
||||
argType = Sema_BuildFuncTypeExprFromDecl(fsym.decl);
|
||||
}
|
||||
}
|
||||
|
||||
if paramType != null as *TypeExpr && argType != null as *TypeExpr {
|
||||
Sema_UnifyInfer(expr, funcDecl, paramType, argType);
|
||||
// If pattern is *T and arg is bare T (from &x we unwrapped), re-wrap
|
||||
if (paramType.kind == tekPointer || paramType.kind == tekRef || paramType.kind == tekMutRef)
|
||||
&& argExpr.kind == ekUnary && argExpr.intValue == tkAmp {
|
||||
// already handled via unwrap of pattern against pointee type of variable
|
||||
if argExpr.child1 != null as *Expr && argExpr.child1.refType != null as *TypeExpr {
|
||||
Sema_UnifyInfer(expr, funcDecl, paramType.pointerPointee, argExpr.child1.refType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user