closures with captures: env struct + global instance + body rewriting
- AST: captureCount + captureName0..7 + captureType0..7 in Expr - Sema: Scope_LookupUpTo + closureScope for capture analysis - HIR Lower: env struct generation, capture assignments in skLet, body rewriting (ekIdent -> hFieldAccess on env instance) - C Backend: env struct def + global instance emission for closures - Bootstrap: full capture support in sema, hir_lower, lir_lower, lir_c_backend - Selfhost loop remains deterministic
This commit is contained in:
Binary file not shown.
Binary file not shown.
+4799
-46
File diff suppressed because it is too large
Load Diff
@@ -233,6 +233,9 @@ type
|
|||||||
exprClosureParams*: seq[Param]
|
exprClosureParams*: seq[Param]
|
||||||
exprClosureBody*: Block
|
exprClosureBody*: Block
|
||||||
exprClosureReturnType*: TypeExpr
|
exprClosureReturnType*: TypeExpr
|
||||||
|
captureCount*: int
|
||||||
|
captureNames*: seq[string]
|
||||||
|
captureTypeKinds*: seq[int]
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Statements
|
# Statements
|
||||||
|
|||||||
@@ -272,6 +272,10 @@ proc emitExpr(be: var CBackend, node: HirNode): string =
|
|||||||
let base = be.emitExpr(node.fieldPtrBase)
|
let base = be.emitExpr(node.fieldPtrBase)
|
||||||
return &"(&({base}.{node.fieldName}))"
|
return &"(&({base}.{node.fieldName}))"
|
||||||
|
|
||||||
|
of hFieldAccess:
|
||||||
|
let base = be.emitExpr(node.fieldAccessBase)
|
||||||
|
return &"({base}.{node.fieldAccessName})"
|
||||||
|
|
||||||
of hArrowField:
|
of hArrowField:
|
||||||
let base = be.emitExpr(node.arrowFieldBase)
|
let base = be.emitExpr(node.arrowFieldBase)
|
||||||
return &"(&({base}->{node.arrowFieldName}))"
|
return &"(&({base}->{node.arrowFieldName}))"
|
||||||
@@ -476,6 +480,18 @@ proc emitStmt(be: var CBackend, node: HirNode) =
|
|||||||
be.emitLine(&"{expr};")
|
be.emitLine(&"{expr};")
|
||||||
|
|
||||||
proc emitFunc*(be: var CBackend, hfunc: HirFunc) =
|
proc emitFunc*(be: var CBackend, hfunc: HirFunc) =
|
||||||
|
# Emit env struct and global instance for closures with captures
|
||||||
|
if hfunc.captureNames.len > 0 and hfunc.envStructName != "":
|
||||||
|
be.emitLine(&"struct {hfunc.envStructName} {{")
|
||||||
|
inc be.indent
|
||||||
|
for i in 0 ..< hfunc.captureNames.len:
|
||||||
|
let capName = hfunc.captureNames[i]
|
||||||
|
let capType = if i < hfunc.captureTypes.len: typeToC(be, hfunc.captureTypes[i]) else: "int"
|
||||||
|
be.emitLine(&"{capType} {capName};")
|
||||||
|
dec be.indent
|
||||||
|
be.emitLine("};")
|
||||||
|
be.emitLine(&"static struct {hfunc.envStructName} {hfunc.envInstanceName};")
|
||||||
|
be.emitLine("")
|
||||||
let retType = typeToC(be, hfunc.retType)
|
let retType = typeToC(be, hfunc.retType)
|
||||||
var params: seq[string] = @[]
|
var params: seq[string] = @[]
|
||||||
for p in hfunc.params:
|
for p in hfunc.params:
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type
|
|||||||
hLoad
|
hLoad
|
||||||
hStore
|
hStore
|
||||||
hFieldPtr
|
hFieldPtr
|
||||||
|
hFieldAccess
|
||||||
hArrowField
|
hArrowField
|
||||||
hIndexPtr
|
hIndexPtr
|
||||||
hSliceIndex
|
hSliceIndex
|
||||||
@@ -98,6 +99,9 @@ type
|
|||||||
of hFieldPtr:
|
of hFieldPtr:
|
||||||
fieldPtrBase*: HirNode
|
fieldPtrBase*: HirNode
|
||||||
fieldName*: string
|
fieldName*: string
|
||||||
|
of hFieldAccess:
|
||||||
|
fieldAccessBase*: HirNode
|
||||||
|
fieldAccessName*: string
|
||||||
of hArrowField:
|
of hArrowField:
|
||||||
arrowFieldBase*: HirNode
|
arrowFieldBase*: HirNode
|
||||||
arrowFieldName*: string
|
arrowFieldName*: string
|
||||||
@@ -166,6 +170,11 @@ type
|
|||||||
retType*: Type
|
retType*: Type
|
||||||
body*: HirNode
|
body*: HirNode
|
||||||
isPublic*: bool
|
isPublic*: bool
|
||||||
|
# Closure capture metadata
|
||||||
|
captureNames*: seq[string]
|
||||||
|
captureTypes*: seq[Type]
|
||||||
|
envStructName*: string
|
||||||
|
envInstanceName*: string
|
||||||
|
|
||||||
HirEnumVariant* = object
|
HirEnumVariant* = object
|
||||||
name*: string
|
name*: string
|
||||||
@@ -219,6 +228,9 @@ proc hirAlloca*(name: string, typ: Type, loc: SourceLocation): HirNode =
|
|||||||
proc hirStore*(ptrNode, value: HirNode, loc: SourceLocation): HirNode =
|
proc hirStore*(ptrNode, value: HirNode, loc: SourceLocation): HirNode =
|
||||||
HirNode(kind: hStore, storePtr: ptrNode, storeValue: value, typ: makeVoid(), loc: loc)
|
HirNode(kind: hStore, storePtr: ptrNode, storeValue: value, typ: makeVoid(), loc: loc)
|
||||||
|
|
||||||
|
proc hirAssign*(target, value: HirNode, loc: SourceLocation): HirNode =
|
||||||
|
HirNode(kind: hAssign, assignOp: tkAssign, assignTarget: target, assignValue: value, typ: makeVoid(), loc: loc)
|
||||||
|
|
||||||
proc hirLoad*(ptrNode: HirNode, typ: Type, loc: SourceLocation): HirNode =
|
proc hirLoad*(ptrNode: HirNode, typ: Type, loc: SourceLocation): HirNode =
|
||||||
HirNode(kind: hLoad, loadPtr: ptrNode, typ: typ, loc: loc)
|
HirNode(kind: hLoad, loadPtr: ptrNode, typ: typ, loc: loc)
|
||||||
|
|
||||||
|
|||||||
+43
-1
@@ -22,6 +22,9 @@ type
|
|||||||
generatedFuncInsts*: Table[string, bool] # Track generated function instantiations
|
generatedFuncInsts*: Table[string, bool] # Track generated function instantiations
|
||||||
extraFuncs*: seq[HirFunc] # Monomorphized generic methods
|
extraFuncs*: seq[HirFunc] # Monomorphized generic methods
|
||||||
varTypeExprs*: Table[string, TypeExpr] # Track variable names -> type expr for generic method inference
|
varTypeExprs*: Table[string, TypeExpr] # Track variable names -> type expr for generic method inference
|
||||||
|
closureDepth*: int
|
||||||
|
currentClosureExpr*: Expr
|
||||||
|
envInstanceName*: string
|
||||||
|
|
||||||
proc freshName(ctx: var LowerCtx): string =
|
proc freshName(ctx: var LowerCtx): string =
|
||||||
inc ctx.varCounter
|
inc ctx.varCounter
|
||||||
@@ -648,6 +651,13 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
|
|
||||||
of ekIdent:
|
of ekIdent:
|
||||||
let name = expr.exprIdent
|
let name = expr.exprIdent
|
||||||
|
# Capture rewriting: if inside closure and ident is captured
|
||||||
|
if ctx.closureDepth > 0 and ctx.currentClosureExpr != nil and ctx.envInstanceName != "":
|
||||||
|
let idx = ctx.currentClosureExpr.captureNames.find(name)
|
||||||
|
if idx >= 0:
|
||||||
|
let capType = if idx < ctx.currentClosureExpr.captureTypeKinds.len: Type(kind: TypeKind(ctx.currentClosureExpr.captureTypeKinds[idx])) else: makeInt()
|
||||||
|
let base = hirVar(ctx.envInstanceName, makeNamed(""), loc)
|
||||||
|
return HirNode(kind: hFieldAccess, fieldAccessName: name, fieldAccessBase: base, typ: capType, loc: loc)
|
||||||
if ctx.importTable.hasKey(name):
|
if ctx.importTable.hasKey(name):
|
||||||
return hirVar(ctx.importTable[name], typ, loc)
|
return hirVar(ctx.importTable[name], typ, loc)
|
||||||
return hirVar(name, typ, loc)
|
return hirVar(name, typ, loc)
|
||||||
@@ -1202,6 +1212,22 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
|||||||
if initHir != nil:
|
if initHir != nil:
|
||||||
let store = hirStore(varNode, initHir, loc)
|
let store = hirStore(varNode, initHir, loc)
|
||||||
stmts.add(store)
|
stmts.add(store)
|
||||||
|
# If init is a closure with captures, emit capture assignments
|
||||||
|
if stmt.stmtLetInit != nil and stmt.stmtLetInit.kind == ekClosure and stmt.stmtLetInit.captureCount > 0:
|
||||||
|
let closureIdx = ctx.varCounter - 1
|
||||||
|
let envInst = "__closure_env_instance_" & $closureIdx
|
||||||
|
var capStmts: seq[HirNode] = @[]
|
||||||
|
for i in 0 ..< stmt.stmtLetInit.captureCount:
|
||||||
|
let capName = stmt.stmtLetInit.captureNames[i]
|
||||||
|
let capType = if i < stmt.stmtLetInit.captureTypeKinds.len: Type(kind: TypeKind(stmt.stmtLetInit.captureTypeKinds[i])) else: makeInt()
|
||||||
|
let base = hirVar(envInst, makeNamed(""), loc)
|
||||||
|
let field = HirNode(kind: hFieldAccess, fieldAccessName: capName, fieldAccessBase: base, typ: capType, loc: loc)
|
||||||
|
let val = hirVar(capName, capType, loc)
|
||||||
|
capStmts.add(hirAssign(field, val, loc))
|
||||||
|
# Prepend capture assignments before the let
|
||||||
|
var allStmts = capStmts
|
||||||
|
allStmts.add(stmts)
|
||||||
|
return hirBlock(allStmts, nil, makeVoid(), loc)
|
||||||
return hirBlock(stmts, nil, makeVoid(), loc)
|
return hirBlock(stmts, nil, makeVoid(), loc)
|
||||||
|
|
||||||
of skReturn:
|
of skReturn:
|
||||||
@@ -1491,6 +1517,13 @@ proc lowerClosureFunc(ctx: var LowerCtx, expr: Expr): HirFunc =
|
|||||||
let name = "__closure_" & $ctx.varCounter
|
let name = "__closure_" & $ctx.varCounter
|
||||||
inc ctx.varCounter
|
inc ctx.varCounter
|
||||||
var f = HirFunc(name: name, isPublic: false)
|
var f = HirFunc(name: name, isPublic: false)
|
||||||
|
# Copy capture metadata
|
||||||
|
if expr.captureCount > 0:
|
||||||
|
f.captureNames = expr.captureNames
|
||||||
|
for tk in expr.captureTypeKinds:
|
||||||
|
f.captureTypes.add(Type(kind: TypeKind(tk)))
|
||||||
|
f.envStructName = "__closure_env_" & $(ctx.varCounter - 1)
|
||||||
|
f.envInstanceName = "__closure_env_instance_" & $(ctx.varCounter - 1)
|
||||||
# Params
|
# Params
|
||||||
for p in expr.exprClosureParams:
|
for p in expr.exprClosureParams:
|
||||||
f.params.add((name: p.name, typ: if p.ptype != nil: ctx.resolveTypeExpr(p.ptype) else: makeUnknown()))
|
f.params.add((name: p.name, typ: if p.ptype != nil: ctx.resolveTypeExpr(p.ptype) else: makeUnknown()))
|
||||||
@@ -1499,9 +1532,18 @@ proc lowerClosureFunc(ctx: var LowerCtx, expr: Expr): HirFunc =
|
|||||||
f.retType = ctx.resolveTypeExpr(expr.exprClosureReturnType)
|
f.retType = ctx.resolveTypeExpr(expr.exprClosureReturnType)
|
||||||
else:
|
else:
|
||||||
f.retType = makeVoid()
|
f.retType = makeVoid()
|
||||||
# Body
|
# Body with closure rewriting
|
||||||
|
let savedDepth = ctx.closureDepth
|
||||||
|
let savedExpr = ctx.currentClosureExpr
|
||||||
|
let savedEnv = ctx.envInstanceName
|
||||||
|
ctx.closureDepth = ctx.closureDepth + 1
|
||||||
|
ctx.currentClosureExpr = expr
|
||||||
|
ctx.envInstanceName = f.envInstanceName
|
||||||
if expr.exprClosureBody != nil:
|
if expr.exprClosureBody != nil:
|
||||||
f.body = ctx.lowerBlock(expr.exprClosureBody)
|
f.body = ctx.lowerBlock(expr.exprClosureBody)
|
||||||
|
ctx.closureDepth = savedDepth
|
||||||
|
ctx.currentClosureExpr = savedExpr
|
||||||
|
ctx.envInstanceName = savedEnv
|
||||||
ctx.extraFuncs.add(f)
|
ctx.extraFuncs.add(f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|||||||
@@ -626,6 +626,20 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s
|
|||||||
be.emitLine("};")
|
be.emitLine("};")
|
||||||
be.emitLine("")
|
be.emitLine("")
|
||||||
|
|
||||||
|
# Emit env structs for closures with captures
|
||||||
|
for f in module.funcs:
|
||||||
|
if f.captureNames.len > 0 and f.envStructName != "":
|
||||||
|
be.emitLine(&"struct {f.envStructName} {{")
|
||||||
|
be.indent += 1
|
||||||
|
for i in 0 ..< f.captureNames.len:
|
||||||
|
let capName = f.captureNames[i]
|
||||||
|
let capType = if i < f.captureTypes.len: typeToCStr(f.captureTypes[i]) else: "int"
|
||||||
|
be.emitLine(&"{capType} {capName};")
|
||||||
|
be.indent -= 1
|
||||||
|
be.emitLine("};")
|
||||||
|
be.emitLine(&"static struct {f.envStructName} {f.envInstanceName};")
|
||||||
|
be.emitLine("")
|
||||||
|
|
||||||
# Emit all LIR functions
|
# Emit all LIR functions
|
||||||
for f in builder.funcs:
|
for f in builder.funcs:
|
||||||
be.emitFunc(f, funcRetTypes, funcPtrTypes)
|
be.emitFunc(f, funcRetTypes, funcPtrTypes)
|
||||||
|
|||||||
@@ -345,6 +345,14 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
b.emitRawC(&"{t.strVal} = (void*)&({lirValToC(base)}.{node.fieldName});")
|
b.emitRawC(&"{t.strVal} = (void*)&({lirValToC(base)}.{node.fieldName});")
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
of hFieldAccess:
|
||||||
|
let base = lowerExpr(ctx, node.fieldAccessBase)
|
||||||
|
let cType = hirTypeToC(ctx, node)
|
||||||
|
let t = b.freshTemp()
|
||||||
|
b.emitAlloca(t.strVal, cType)
|
||||||
|
b.emitRawC(&"{t.strVal} = {lirValToC(base)}.{node.fieldAccessName};")
|
||||||
|
return t
|
||||||
|
|
||||||
of hArrowField:
|
of hArrowField:
|
||||||
let base = lowerExpr(ctx, node.arrowFieldBase)
|
let base = lowerExpr(ctx, node.arrowFieldBase)
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
@@ -549,6 +557,9 @@ proc buildLval(ctx: var LowerToLirCtx, n: HirNode): string =
|
|||||||
of hArrowField:
|
of hArrowField:
|
||||||
let baseStr = buildLval(ctx, n.arrowFieldBase)
|
let baseStr = buildLval(ctx, n.arrowFieldBase)
|
||||||
return baseStr & "->" & n.arrowFieldName
|
return baseStr & "->" & n.arrowFieldName
|
||||||
|
of hFieldAccess:
|
||||||
|
let baseStr = buildLval(ctx, n.fieldAccessBase)
|
||||||
|
return baseStr & "." & n.fieldAccessName
|
||||||
of hIndexPtr:
|
of hIndexPtr:
|
||||||
let baseStr = buildLval(ctx, n.indexPtrBase)
|
let baseStr = buildLval(ctx, n.indexPtrBase)
|
||||||
let idx = lowerExpr(ctx, n.indexPtrIndex)
|
let idx = lowerExpr(ctx, n.indexPtrIndex)
|
||||||
@@ -669,6 +680,9 @@ proc lowerStmt(ctx: var LowerToLirCtx, node: HirNode) =
|
|||||||
of hFieldPtr:
|
of hFieldPtr:
|
||||||
let lval = buildLval(ctx, node.assignTarget)
|
let lval = buildLval(ctx, node.assignTarget)
|
||||||
b.emitRawC(&"{lval} = {lirValToC(value)};")
|
b.emitRawC(&"{lval} = {lirValToC(value)};")
|
||||||
|
of hFieldAccess:
|
||||||
|
let lval = buildLval(ctx, node.assignTarget)
|
||||||
|
b.emitRawC(&"{lval} = {lirValToC(value)};")
|
||||||
of hArrowField:
|
of hArrowField:
|
||||||
let lval = buildLval(ctx, node.assignTarget)
|
let lval = buildLval(ctx, node.assignTarget)
|
||||||
b.emitRawC(&"{lval} = {lirValToC(value)};")
|
b.emitRawC(&"{lval} = {lirValToC(value)};")
|
||||||
|
|||||||
@@ -43,4 +43,14 @@ proc lookup*(scope: Scope, name: string): Symbol =
|
|||||||
proc lookupLocal*(scope: Scope, name: string): Symbol =
|
proc lookupLocal*(scope: Scope, name: string): Symbol =
|
||||||
if scope.table.hasKey(name):
|
if scope.table.hasKey(name):
|
||||||
return scope.table[name]
|
return scope.table[name]
|
||||||
|
return nil
|
||||||
|
|
||||||
|
proc lookupUpTo*(scope: Scope, name: string, limit: Scope): Symbol =
|
||||||
|
var cur = scope
|
||||||
|
while cur != nil:
|
||||||
|
if cur.table.hasKey(name):
|
||||||
|
return cur.table[name]
|
||||||
|
if cur == limit:
|
||||||
|
break
|
||||||
|
cur = cur.parent
|
||||||
return nil
|
return nil
|
||||||
@@ -45,6 +45,9 @@ type
|
|||||||
currentFuncIsAsync*: bool ## true inside async func
|
currentFuncIsAsync*: bool ## true inside async func
|
||||||
movedVars*: seq[string] ## variables moved in current checked function
|
movedVars*: seq[string] ## variables moved in current checked function
|
||||||
currentRetType*: Type ## return type of the function being checked
|
currentRetType*: Type ## return type of the function being checked
|
||||||
|
closureDepth*: int ## nesting depth inside closures
|
||||||
|
currentClosureExpr*: Expr ## current closure being analyzed
|
||||||
|
closureScope*: Scope ## scope at which the current closure was entered
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
@@ -816,6 +819,14 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
if sym.typ == nil:
|
if sym.typ == nil:
|
||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
|
# Capture tracking
|
||||||
|
if sema.closureDepth > 0 and sema.currentClosureExpr != nil and sema.closureScope != nil:
|
||||||
|
let localSym = scope.lookupUpTo(expr.exprIdent, sema.closureScope)
|
||||||
|
if localSym == nil and sym.kind == skVar:
|
||||||
|
if expr.exprIdent notin sema.currentClosureExpr.captureNames:
|
||||||
|
sema.currentClosureExpr.captureNames.add(expr.exprIdent)
|
||||||
|
sema.currentClosureExpr.captureTypeKinds.add(sym.typ.kind.int)
|
||||||
|
sema.currentClosureExpr.captureCount = sema.currentClosureExpr.captureNames.len
|
||||||
return sym.typ
|
return sym.typ
|
||||||
of ekSelf:
|
of ekSelf:
|
||||||
let sym = scope.lookup("self")
|
let sym = scope.lookup("self")
|
||||||
@@ -1340,7 +1351,16 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
return makeStr()
|
return makeStr()
|
||||||
of ekClosure:
|
of ekClosure:
|
||||||
let savedRetType = sema.currentRetType
|
let savedRetType = sema.currentRetType
|
||||||
|
let savedClosureDepth = sema.closureDepth
|
||||||
|
let savedClosureExpr = sema.currentClosureExpr
|
||||||
|
let savedClosureScope = sema.closureScope
|
||||||
let childScope = Scope(parent: scope)
|
let childScope = Scope(parent: scope)
|
||||||
|
sema.closureDepth = sema.closureDepth + 1
|
||||||
|
sema.currentClosureExpr = expr
|
||||||
|
sema.closureScope = childScope
|
||||||
|
expr.captureCount = 0
|
||||||
|
expr.captureNames = @[]
|
||||||
|
expr.captureTypeKinds = @[]
|
||||||
sema.currentRetType = if expr.exprClosureReturnType != nil: sema.resolveType(expr.exprClosureReturnType) else: makeUnknown()
|
sema.currentRetType = if expr.exprClosureReturnType != nil: sema.resolveType(expr.exprClosureReturnType) else: makeUnknown()
|
||||||
# Register params
|
# Register params
|
||||||
for p in expr.exprClosureParams:
|
for p in expr.exprClosureParams:
|
||||||
@@ -1351,6 +1371,9 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
for stmt in expr.exprClosureBody.stmts:
|
for stmt in expr.exprClosureBody.stmts:
|
||||||
discard sema.checkStmt(stmt, childScope)
|
discard sema.checkStmt(stmt, childScope)
|
||||||
sema.currentRetType = savedRetType
|
sema.currentRetType = savedRetType
|
||||||
|
sema.closureDepth = savedClosureDepth
|
||||||
|
sema.currentClosureExpr = savedClosureExpr
|
||||||
|
sema.closureScope = savedClosureScope
|
||||||
# Build function type
|
# Build function type
|
||||||
var params: seq[Type] = @[]
|
var params: seq[Type] = @[]
|
||||||
for p in expr.exprClosureParams:
|
for p in expr.exprClosureParams:
|
||||||
|
|||||||
+18
@@ -142,6 +142,24 @@ struct Expr {
|
|||||||
structFieldCount: int,
|
structFieldCount: int,
|
||||||
// Closure params (for ekClosure)
|
// Closure params (for ekClosure)
|
||||||
closureParams: *Decl,
|
closureParams: *Decl,
|
||||||
|
// Captures (for ekClosure)
|
||||||
|
captureCount: int,
|
||||||
|
captureName0: String,
|
||||||
|
captureName1: String,
|
||||||
|
captureName2: String,
|
||||||
|
captureName3: String,
|
||||||
|
captureName4: String,
|
||||||
|
captureName5: String,
|
||||||
|
captureName6: String,
|
||||||
|
captureName7: String,
|
||||||
|
captureType0: int,
|
||||||
|
captureType1: int,
|
||||||
|
captureType2: int,
|
||||||
|
captureType3: int,
|
||||||
|
captureType4: int,
|
||||||
|
captureType5: int,
|
||||||
|
captureType6: int,
|
||||||
|
captureType7: int,
|
||||||
// Call arguments (linked list for multi-arg support)
|
// Call arguments (linked list for multi-arg support)
|
||||||
callArgs: *ExprList,
|
callArgs: *ExprList,
|
||||||
callArgCount: int,
|
callArgCount: int,
|
||||||
|
|||||||
@@ -445,6 +445,14 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Field access on value (direct dot): obj.field
|
||||||
|
if kind == hFieldAccess {
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, ".");
|
||||||
|
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Field access: obj.field — use -> if base is pointer, else .
|
// Field access: obj.field — use -> if base is pointer, else .
|
||||||
if kind == hFieldPtr {
|
if kind == hFieldPtr {
|
||||||
CBE_EmitExpr(cbe, node.child1);
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
@@ -1042,6 +1050,43 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
i = i + 1;
|
i = i + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Emit env struct and global instance for closures with captures
|
||||||
|
if mod.funcs[i].captureCount > 0 {
|
||||||
|
let envName: String = mod.funcs[i].envStructName;
|
||||||
|
let instName: String = mod.funcs[i].envInstanceName;
|
||||||
|
if !String_Eq(envName, "") {
|
||||||
|
// Emit struct definition
|
||||||
|
StringBuilder_Append(&cbe.sb, "struct ");
|
||||||
|
StringBuilder_Append(&cbe.sb, envName);
|
||||||
|
StringBuilder_Append(&cbe.sb, " {\n");
|
||||||
|
var ci: int = 0;
|
||||||
|
while ci < mod.funcs[i].captureCount {
|
||||||
|
var capName: String = "";
|
||||||
|
var capType: String = "int";
|
||||||
|
if ci == 0 { capName = mod.funcs[i].captureName0; capType = CBackend_TypeToC(mod.funcs[i].captureType0); }
|
||||||
|
else if ci == 1 { capName = mod.funcs[i].captureName1; capType = CBackend_TypeToC(mod.funcs[i].captureType1); }
|
||||||
|
else if ci == 2 { capName = mod.funcs[i].captureName2; capType = CBackend_TypeToC(mod.funcs[i].captureType2); }
|
||||||
|
else if ci == 3 { capName = mod.funcs[i].captureName3; capType = CBackend_TypeToC(mod.funcs[i].captureType3); }
|
||||||
|
else if ci == 4 { capName = mod.funcs[i].captureName4; capType = CBackend_TypeToC(mod.funcs[i].captureType4); }
|
||||||
|
else if ci == 5 { capName = mod.funcs[i].captureName5; capType = CBackend_TypeToC(mod.funcs[i].captureType5); }
|
||||||
|
else if ci == 6 { capName = mod.funcs[i].captureName6; capType = CBackend_TypeToC(mod.funcs[i].captureType6); }
|
||||||
|
else if ci == 7 { capName = mod.funcs[i].captureName7; capType = CBackend_TypeToC(mod.funcs[i].captureType7); }
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, capType);
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, capName);
|
||||||
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
ci = ci + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "};\n");
|
||||||
|
// Emit global instance
|
||||||
|
StringBuilder_Append(&cbe.sb, "static struct ");
|
||||||
|
StringBuilder_Append(&cbe.sb, envName);
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, instName);
|
||||||
|
StringBuilder_Append(&cbe.sb, ";\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||||
StringBuilder_Append(&cbe.sb, " {\n");
|
StringBuilder_Append(&cbe.sb, " {\n");
|
||||||
// Body
|
// Body
|
||||||
|
|||||||
+23
-2
@@ -18,8 +18,9 @@ const hAlloca: int = 12;
|
|||||||
const hLoad: int = 13;
|
const hLoad: int = 13;
|
||||||
const hStore: int = 14;
|
const hStore: int = 14;
|
||||||
const hFieldPtr: int = 15;
|
const hFieldPtr: int = 15;
|
||||||
const hArrowField: int = 16;
|
const hFieldAccess: int = 16;
|
||||||
const hIndexPtr: int = 17;
|
const hArrowField: int = 17;
|
||||||
|
const hIndexPtr: int = 18;
|
||||||
const hCall: int = 18;
|
const hCall: int = 18;
|
||||||
const hCallIndirect: int = 19;
|
const hCallIndirect: int = 19;
|
||||||
const hCast: int = 20;
|
const hCast: int = 20;
|
||||||
@@ -93,6 +94,26 @@ struct HirFunc {
|
|||||||
retTypeName: String;
|
retTypeName: String;
|
||||||
body: *HirNode;
|
body: *HirNode;
|
||||||
isPublic: bool;
|
isPublic: bool;
|
||||||
|
// Closure capture metadata
|
||||||
|
captureCount: int;
|
||||||
|
captureName0: String;
|
||||||
|
captureName1: String;
|
||||||
|
captureName2: String;
|
||||||
|
captureName3: String;
|
||||||
|
captureName4: String;
|
||||||
|
captureName5: String;
|
||||||
|
captureName6: String;
|
||||||
|
captureName7: String;
|
||||||
|
captureType0: int;
|
||||||
|
captureType1: int;
|
||||||
|
captureType2: int;
|
||||||
|
captureType3: int;
|
||||||
|
captureType4: int;
|
||||||
|
captureType5: int;
|
||||||
|
captureType6: int;
|
||||||
|
captureType7: int;
|
||||||
|
envStructName: String;
|
||||||
|
envInstanceName: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+167
-4
@@ -15,6 +15,10 @@ struct LowerCtx {
|
|||||||
externCount: int,
|
externCount: int,
|
||||||
varCounter: int,
|
varCounter: int,
|
||||||
tryCounter: int,
|
tryCounter: int,
|
||||||
|
closureDepth: int,
|
||||||
|
currentClosureExpr: *Expr,
|
||||||
|
envInstanceName: String,
|
||||||
|
hm: *HirModule,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -50,6 +54,29 @@ func Lcx_ResolveTypeKindFromName(name: String) -> int {
|
|||||||
return tyNamed;
|
return tyNamed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Lcx_TypeKindToName(kind: int) -> String {
|
||||||
|
if kind == tyVoid { return "void"; }
|
||||||
|
if kind == tyBool || kind == tyBool8 || kind == tyBool16 || kind == tyBool32 { return "bool"; }
|
||||||
|
if kind == tyChar8 { return "char"; }
|
||||||
|
if kind == tyChar16 { return "uint16"; }
|
||||||
|
if kind == tyChar32 { return "uint32"; }
|
||||||
|
if kind == tyStr { return "String"; }
|
||||||
|
if kind == tyInt8 { return "int8"; }
|
||||||
|
if kind == tyInt16 { return "int16"; }
|
||||||
|
if kind == tyInt32 { return "int32"; }
|
||||||
|
if kind == tyInt64 { return "int64"; }
|
||||||
|
if kind == tyInt { return "int"; }
|
||||||
|
if kind == tyUInt8 { return "uint8"; }
|
||||||
|
if kind == tyUInt16 { return "uint16"; }
|
||||||
|
if kind == tyUInt32 { return "uint32"; }
|
||||||
|
if kind == tyUInt64 { return "uint64"; }
|
||||||
|
if kind == tyUInt { return "uint"; }
|
||||||
|
if kind == tyFloat32 { return "float32"; }
|
||||||
|
if kind == tyFloat64 { return "float64"; }
|
||||||
|
if kind == tyPointer { return "void*"; }
|
||||||
|
return "int";
|
||||||
|
}
|
||||||
|
|
||||||
func Lcx_ResolveTypeKind(te: *TypeExpr) -> int {
|
func Lcx_ResolveTypeKind(te: *TypeExpr) -> int {
|
||||||
if te == null as *TypeExpr { return tyUnknown; }
|
if te == null as *TypeExpr { return tyUnknown; }
|
||||||
|
|
||||||
@@ -122,6 +149,40 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
|
|
||||||
// Identifier → variable reference
|
// Identifier → variable reference
|
||||||
if kind == ekIdent {
|
if kind == ekIdent {
|
||||||
|
// 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, "") {
|
||||||
|
let capCount: int = ctx.currentClosureExpr.captureCount;
|
||||||
|
var ci: int = 0;
|
||||||
|
var isCaptured: bool = false;
|
||||||
|
var capType: int = 0;
|
||||||
|
while ci < capCount {
|
||||||
|
var capName: String = "";
|
||||||
|
if ci == 0 { capName = ctx.currentClosureExpr.captureName0; capType = ctx.currentClosureExpr.captureType0; }
|
||||||
|
else if ci == 1 { capName = ctx.currentClosureExpr.captureName1; capType = ctx.currentClosureExpr.captureType1; }
|
||||||
|
else if ci == 2 { capName = ctx.currentClosureExpr.captureName2; capType = ctx.currentClosureExpr.captureType2; }
|
||||||
|
else if ci == 3 { capName = ctx.currentClosureExpr.captureName3; capType = ctx.currentClosureExpr.captureType3; }
|
||||||
|
else if ci == 4 { capName = ctx.currentClosureExpr.captureName4; capType = ctx.currentClosureExpr.captureType4; }
|
||||||
|
else if ci == 5 { capName = ctx.currentClosureExpr.captureName5; capType = ctx.currentClosureExpr.captureType5; }
|
||||||
|
else if ci == 6 { capName = ctx.currentClosureExpr.captureName6; capType = ctx.currentClosureExpr.captureType6; }
|
||||||
|
else if ci == 7 { capName = ctx.currentClosureExpr.captureName7; capType = ctx.currentClosureExpr.captureType7; }
|
||||||
|
if String_Eq(capName, expr.strValue) {
|
||||||
|
isCaptured = true;
|
||||||
|
}
|
||||||
|
ci = ci + 1;
|
||||||
|
}
|
||||||
|
if isCaptured {
|
||||||
|
n.kind = hFieldAccess;
|
||||||
|
n.strValue = expr.strValue;
|
||||||
|
let baseNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
|
baseNode.kind = hVar;
|
||||||
|
baseNode.strValue = ctx.envInstanceName;
|
||||||
|
n.child1 = baseNode;
|
||||||
|
n.typeKind = capType;
|
||||||
|
n.typeName = Lcx_TypeKindToName(capType);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
n.kind = hVar;
|
n.kind = hVar;
|
||||||
n.strValue = expr.strValue;
|
n.strValue = expr.strValue;
|
||||||
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
|
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
|
||||||
@@ -500,10 +561,71 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
|||||||
sym.decl = null as *Decl;
|
sym.decl = null as *Decl;
|
||||||
discard Scope_Define(ctx.scope, sym);
|
discard Scope_Define(ctx.scope, sym);
|
||||||
// store the init value
|
// store the init value
|
||||||
n.kind = hStore;
|
let storeNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
n.child1 = alloca;
|
storeNode.kind = hStore;
|
||||||
n.child2 = init;
|
storeNode.child1 = alloca;
|
||||||
return n;
|
storeNode.child2 = init;
|
||||||
|
|
||||||
|
// If init is a closure with captures, emit capture assignments before the let
|
||||||
|
if stmt.child1 != null as *Expr && stmt.child1.kind == ekClosure && stmt.child1.captureCount > 0 {
|
||||||
|
let closureIdx: int = ctx.funcCount - 1;
|
||||||
|
let envInst: String = String_Concat("__closure_env_instance_", String_FromInt(closureIdx));
|
||||||
|
// Build a block: capture assignments + let store
|
||||||
|
let blockNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
|
blockNode.kind = hBlock;
|
||||||
|
blockNode.line = line;
|
||||||
|
blockNode.column = col;
|
||||||
|
var firstStmt: *HirNode = null as *HirNode;
|
||||||
|
var lastStmt: *HirNode = null as *HirNode;
|
||||||
|
var ci: int = 0;
|
||||||
|
while ci < stmt.child1.captureCount {
|
||||||
|
var capName: String = "";
|
||||||
|
if ci == 0 { capName = stmt.child1.captureName0; }
|
||||||
|
else if ci == 1 { capName = stmt.child1.captureName1; }
|
||||||
|
else if ci == 2 { capName = stmt.child1.captureName2; }
|
||||||
|
else if ci == 3 { capName = stmt.child1.captureName3; }
|
||||||
|
else if ci == 4 { capName = stmt.child1.captureName4; }
|
||||||
|
else if ci == 5 { capName = stmt.child1.captureName5; }
|
||||||
|
else if ci == 6 { capName = stmt.child1.captureName6; }
|
||||||
|
else if ci == 7 { capName = stmt.child1.captureName7; }
|
||||||
|
// Build: envInst.capName = capName;
|
||||||
|
let assignNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
|
assignNode.kind = hAssign;
|
||||||
|
assignNode.line = line;
|
||||||
|
assignNode.column = col;
|
||||||
|
let fieldNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
|
fieldNode.kind = hFieldAccess;
|
||||||
|
fieldNode.strValue = capName;
|
||||||
|
let baseNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
|
baseNode.kind = hVar;
|
||||||
|
baseNode.strValue = envInst;
|
||||||
|
fieldNode.child1 = baseNode;
|
||||||
|
let valNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
|
valNode.kind = hVar;
|
||||||
|
valNode.strValue = capName;
|
||||||
|
assignNode.child1 = fieldNode;
|
||||||
|
assignNode.child2 = valNode;
|
||||||
|
if firstStmt == null as *HirNode {
|
||||||
|
firstStmt = assignNode;
|
||||||
|
lastStmt = assignNode;
|
||||||
|
} else {
|
||||||
|
lastStmt.child3 = assignNode;
|
||||||
|
lastStmt = assignNode;
|
||||||
|
}
|
||||||
|
ci = ci + 1;
|
||||||
|
}
|
||||||
|
// Append the let store
|
||||||
|
if firstStmt == null as *HirNode {
|
||||||
|
firstStmt = storeNode;
|
||||||
|
lastStmt = storeNode;
|
||||||
|
} else {
|
||||||
|
lastStmt.child3 = storeNode;
|
||||||
|
lastStmt = storeNode;
|
||||||
|
}
|
||||||
|
blockNode.child1 = firstStmt;
|
||||||
|
return blockNode;
|
||||||
|
}
|
||||||
|
return storeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
@@ -830,6 +952,37 @@ func Lcx_LowerClosureFunc(ctx: *LowerCtx, expr: *Expr) -> *HirFunc {
|
|||||||
f.retTypeKind = 0;
|
f.retTypeKind = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy capture metadata from AST
|
||||||
|
f.captureCount = expr.captureCount;
|
||||||
|
f.captureName0 = expr.captureName0;
|
||||||
|
f.captureName1 = expr.captureName1;
|
||||||
|
f.captureName2 = expr.captureName2;
|
||||||
|
f.captureName3 = expr.captureName3;
|
||||||
|
f.captureName4 = expr.captureName4;
|
||||||
|
f.captureName5 = expr.captureName5;
|
||||||
|
f.captureName6 = expr.captureName6;
|
||||||
|
f.captureName7 = expr.captureName7;
|
||||||
|
f.captureType0 = expr.captureType0;
|
||||||
|
f.captureType1 = expr.captureType1;
|
||||||
|
f.captureType2 = expr.captureType2;
|
||||||
|
f.captureType3 = expr.captureType3;
|
||||||
|
f.captureType4 = expr.captureType4;
|
||||||
|
f.captureType5 = expr.captureType5;
|
||||||
|
f.captureType6 = expr.captureType6;
|
||||||
|
f.captureType7 = expr.captureType7;
|
||||||
|
|
||||||
|
// Generate env struct and instance names if there are captures
|
||||||
|
var envStructName: String = "";
|
||||||
|
var envInstanceName: String = "";
|
||||||
|
if f.captureCount > 0 {
|
||||||
|
envStructName = String_Concat("__closure_env_", numStr);
|
||||||
|
envInstanceName = String_Concat("__closure_env_instance_", numStr);
|
||||||
|
f.envStructName = envStructName;
|
||||||
|
f.envInstanceName = envInstanceName;
|
||||||
|
|
||||||
|
// Env struct is emitted directly by C backend from func capture metadata
|
||||||
|
}
|
||||||
|
|
||||||
// Create function scope
|
// Create function scope
|
||||||
var funcScope: Scope = Scope_NewChild(ctx.scope);
|
var funcScope: Scope = Scope_NewChild(ctx.scope);
|
||||||
var pi: int = 0;
|
var pi: int = 0;
|
||||||
@@ -868,13 +1021,22 @@ func Lcx_LowerClosureFunc(ctx: *LowerCtx, expr: *Expr) -> *HirFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let prevScope: *Scope = ctx.scope;
|
let prevScope: *Scope = ctx.scope;
|
||||||
|
let prevClosureDepth: int = ctx.closureDepth;
|
||||||
|
let prevClosureExpr: *Expr = ctx.currentClosureExpr;
|
||||||
|
let prevEnvInstanceName: String = ctx.envInstanceName;
|
||||||
ctx.scope = &funcScope;
|
ctx.scope = &funcScope;
|
||||||
|
ctx.closureDepth = ctx.closureDepth + 1;
|
||||||
|
ctx.currentClosureExpr = expr;
|
||||||
|
ctx.envInstanceName = envInstanceName;
|
||||||
if expr.refBlock != null as *Block {
|
if expr.refBlock != null as *Block {
|
||||||
f.body = Lcx_LowerBlock(ctx, expr.refBlock, -1);
|
f.body = Lcx_LowerBlock(ctx, expr.refBlock, -1);
|
||||||
} else {
|
} else {
|
||||||
f.body = null as *HirNode;
|
f.body = null as *HirNode;
|
||||||
}
|
}
|
||||||
ctx.scope = prevScope;
|
ctx.scope = prevScope;
|
||||||
|
ctx.closureDepth = prevClosureDepth;
|
||||||
|
ctx.currentClosureExpr = prevClosureExpr;
|
||||||
|
ctx.envInstanceName = prevEnvInstanceName;
|
||||||
|
|
||||||
// Add to module functions
|
// Add to module functions
|
||||||
ctx.funcs[ctx.funcCount] = *f;
|
ctx.funcs[ctx.funcCount] = *f;
|
||||||
@@ -906,6 +1068,7 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
hm.enums = bux_alloc(64 as uint * sizeof(HirEnum)) as *HirEnum;
|
hm.enums = bux_alloc(64 as uint * sizeof(HirEnum)) as *HirEnum;
|
||||||
hm.constCount = 0;
|
hm.constCount = 0;
|
||||||
hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst;
|
hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst;
|
||||||
|
ctx.hm = hm;
|
||||||
|
|
||||||
// First pass: count structs (to allocate field arrays later)
|
// First pass: count structs (to allocate field arrays later)
|
||||||
// Second pass: actually collect them
|
// Second pass: actually collect them
|
||||||
|
|||||||
@@ -89,6 +89,25 @@ func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol {
|
|||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Scope_LookupUpTo(scope: *Scope, name: String, limit: *Scope) -> Symbol {
|
||||||
|
var cur: *Scope = scope;
|
||||||
|
while cur != null as *Scope {
|
||||||
|
var i: int = 0;
|
||||||
|
while i < cur.count {
|
||||||
|
if String_Eq(cur.symbols[i].name, name) {
|
||||||
|
return cur.symbols[i];
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if cur == limit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cur = cur.parent;
|
||||||
|
}
|
||||||
|
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl };
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
func Scope_Free(scope: *Scope) {
|
func Scope_Free(scope: *Scope) {
|
||||||
bux_free(scope.symbols as *void);
|
bux_free(scope.symbols as *void);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ struct Sema {
|
|||||||
movedName5: String;
|
movedName5: String;
|
||||||
movedName6: String;
|
movedName6: String;
|
||||||
movedName7: String;
|
movedName7: String;
|
||||||
|
closureDepth: int; // nesting depth inside closures
|
||||||
|
currentClosureExpr: *Expr; // current closure being analyzed (for capture tracking)
|
||||||
|
closureScope: *Scope; // scope at which the current closure was entered
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SemaDiag {
|
struct SemaDiag {
|
||||||
@@ -337,6 +340,42 @@ func Sema_RemoveMoved(sema: *Sema, name: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Capture tracking for closures
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func Sema_AddCapture(closureExpr: *Expr, name: String, typeKind: int) {
|
||||||
|
if closureExpr == null as *Expr { return; }
|
||||||
|
// Check if already captured
|
||||||
|
var i: int = 0;
|
||||||
|
while i < closureExpr.captureCount {
|
||||||
|
var capName: String = "";
|
||||||
|
if i == 0 { capName = closureExpr.captureName0; }
|
||||||
|
else if i == 1 { capName = closureExpr.captureName1; }
|
||||||
|
else if i == 2 { capName = closureExpr.captureName2; }
|
||||||
|
else if i == 3 { capName = closureExpr.captureName3; }
|
||||||
|
else if i == 4 { capName = closureExpr.captureName4; }
|
||||||
|
else if i == 5 { capName = closureExpr.captureName5; }
|
||||||
|
else if i == 6 { capName = closureExpr.captureName6; }
|
||||||
|
else if i == 7 { capName = closureExpr.captureName7; }
|
||||||
|
if String_Eq(capName, name) { return; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
// Add new capture (up to 8)
|
||||||
|
if closureExpr.captureCount < 8 {
|
||||||
|
let idx: int = closureExpr.captureCount;
|
||||||
|
if idx == 0 { closureExpr.captureName0 = name; closureExpr.captureType0 = typeKind; }
|
||||||
|
else if idx == 1 { closureExpr.captureName1 = name; closureExpr.captureType1 = typeKind; }
|
||||||
|
else if idx == 2 { closureExpr.captureName2 = name; closureExpr.captureType2 = typeKind; }
|
||||||
|
else if idx == 3 { closureExpr.captureName3 = name; closureExpr.captureType3 = typeKind; }
|
||||||
|
else if idx == 4 { closureExpr.captureName4 = name; closureExpr.captureType4 = typeKind; }
|
||||||
|
else if idx == 5 { closureExpr.captureName5 = name; closureExpr.captureType5 = typeKind; }
|
||||||
|
else if idx == 6 { closureExpr.captureName6 = name; closureExpr.captureType6 = typeKind; }
|
||||||
|
else if idx == 7 { closureExpr.captureName7 = name; closureExpr.captureType7 = typeKind; }
|
||||||
|
closureExpr.captureCount = closureExpr.captureCount + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Expression type checking
|
// Expression type checking
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -379,6 +418,16 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
te.typeName = sym.typeName;
|
te.typeName = sym.typeName;
|
||||||
expr.refType = te;
|
expr.refType = te;
|
||||||
}
|
}
|
||||||
|
// Capture tracking: if inside closure and identifier is not local but from parent scope
|
||||||
|
if sema.closureDepth > 0 && sema.currentClosureExpr != null as *Expr && sema.closureScope != null as *Scope {
|
||||||
|
let localSym: Symbol = Scope_LookupUpTo(sema.scope, expr.strValue, sema.closureScope);
|
||||||
|
if localSym.kind == 0 && !String_Eq(localSym.name, expr.strValue) {
|
||||||
|
// Not local to closure scope — check if it's a variable from outer scope
|
||||||
|
if sym.kind == skVar {
|
||||||
|
Sema_AddCapture(sema.currentClosureExpr, expr.strValue, sym.typeKind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return sym.typeKind;
|
return sym.typeKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,9 +617,18 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
if kind == ekClosure {
|
if kind == ekClosure {
|
||||||
let savedRetType: int = sema.currentRetType;
|
let savedRetType: int = sema.currentRetType;
|
||||||
let savedScope: *Scope = sema.scope;
|
let savedScope: *Scope = sema.scope;
|
||||||
|
let savedClosureDepth: int = sema.closureDepth;
|
||||||
|
let savedClosureExpr: *Expr = sema.currentClosureExpr;
|
||||||
|
let savedClosureScope: *Scope = sema.closureScope;
|
||||||
let childScope: Scope = Scope_NewChild(sema.scope);
|
let childScope: Scope = Scope_NewChild(sema.scope);
|
||||||
sema.scope = &childScope;
|
sema.scope = &childScope;
|
||||||
|
|
||||||
|
// Set up closure tracking
|
||||||
|
sema.closureDepth = sema.closureDepth + 1;
|
||||||
|
sema.currentClosureExpr = expr;
|
||||||
|
sema.closureScope = &childScope;
|
||||||
|
expr.captureCount = 0;
|
||||||
|
|
||||||
// Set return type from annotation, or unknown for inference
|
// Set return type from annotation, or unknown for inference
|
||||||
var closureRetType: int = tyUnknown;
|
var closureRetType: int = tyUnknown;
|
||||||
if expr.refType != null as *TypeExpr {
|
if expr.refType != null as *TypeExpr {
|
||||||
@@ -621,6 +679,9 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
// Restore
|
// Restore
|
||||||
sema.scope = savedScope;
|
sema.scope = savedScope;
|
||||||
sema.currentRetType = savedRetType;
|
sema.currentRetType = savedRetType;
|
||||||
|
sema.closureDepth = savedClosureDepth;
|
||||||
|
sema.currentClosureExpr = savedClosureExpr;
|
||||||
|
sema.closureScope = savedClosureScope;
|
||||||
|
|
||||||
// Build function type expression for later use
|
// Build function type expression for later use
|
||||||
let funcType: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
let funcType: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||||
|
|||||||
Reference in New Issue
Block a user