fix(bootstrap): resolve lir lower crashes and struct-copy bugs
- Fix lirValToC crash on lvkInt in hStore raw C emission - Add hAlloca handling to lowerExpr (returns &name, avoids unhandled fallback) - Fix nested comment syntax in unhandled expr fallback - Rewrite buildLval to handle hIndexPtr and nested hFieldPtr/hArrowField so assignments like arr[i].field = val emit direct C lvalues - Make &&/|| temporaries unique (__and_tmp_N/__or_tmp_N) to avoid duplicate declarations in the same function - Selfhost null-deref fix in sema.bux (unchained nested conditions)
This commit is contained in:
@@ -11,7 +11,7 @@ all: build
|
|||||||
|
|
||||||
build:
|
build:
|
||||||
$(NIM) c -o:$(OUT) -d:release --opt:size $(SRC)
|
$(NIM) c -o:$(OUT) -d:release --opt:size $(SRC)
|
||||||
strip $(OUT)
|
# strip $(OUT)
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
$(NIM) c -o:buxc_debug -d:debug --stackTrace:on --lineTrace:on $(SRC)
|
$(NIM) c -o:buxc_debug -d:debug --stackTrace:on --lineTrace:on $(SRC)
|
||||||
@@ -72,5 +72,5 @@ selfhost: build
|
|||||||
@cp src/bux.toml build/selfhost/
|
@cp src/bux.toml build/selfhost/
|
||||||
@mv build/selfhost/src/main.bux build/selfhost/src/Main.bux 2>/dev/null || true
|
@mv build/selfhost/src/main.bux build/selfhost/src/Main.bux 2>/dev/null || true
|
||||||
@cd build/selfhost && ../../$(OUT) build
|
@cd build/selfhost && ../../$(OUT) build
|
||||||
@strip build/selfhost/build/buxc2 2>/dev/null || true
|
# strip removed for debug
|
||||||
@echo "=== Self-hosted compiler built successfully ==="
|
@echo "=== Self-hosted compiler built successfully ==="
|
||||||
|
|||||||
+1
-1
@@ -488,7 +488,7 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
# Compile with cc
|
# Compile with cc
|
||||||
let outputName = if pctx.man.name != "": pctx.man.name else: "bux_out"
|
let outputName = if pctx.man.name != "": pctx.man.name else: "bux_out"
|
||||||
let outputFile = buildDir / outputName
|
let outputFile = buildDir / outputName
|
||||||
let ccCmd = &"cc -O2 -pthread -o {outputFile} {cFile} {runtimeDst} {ioDst} -lm -lcrypto 2>&1"
|
let ccCmd = &"cc -O0 -g -pthread -o {outputFile} {cFile} {runtimeDst} {ioDst} -lm -lcrypto 2>&1"
|
||||||
if opts.verbose:
|
if opts.verbose:
|
||||||
printInfo(&"running: {ccCmd}", useColor)
|
printInfo(&"running: {ccCmd}", useColor)
|
||||||
let (output, exitCode) = execCmdEx(ccCmd)
|
let (output, exitCode) = execCmdEx(ccCmd)
|
||||||
|
|||||||
@@ -203,6 +203,10 @@ proc hirReturn*(value: HirNode, loc: SourceLocation): HirNode =
|
|||||||
proc hirBlock*(stmts: seq[HirNode], expr: HirNode, typ: Type, loc: SourceLocation, isScope: bool = false): HirNode =
|
proc hirBlock*(stmts: seq[HirNode], expr: HirNode, typ: Type, loc: SourceLocation, isScope: bool = false): HirNode =
|
||||||
HirNode(kind: hBlock, blockStmts: stmts, blockExpr: expr, typ: typ, loc: loc, isScope: isScope)
|
HirNode(kind: hBlock, blockStmts: stmts, blockExpr: expr, typ: typ, loc: loc, isScope: isScope)
|
||||||
|
|
||||||
|
proc hirIf*(cond, thenBranch, elseBranch: HirNode, loc: SourceLocation): HirNode =
|
||||||
|
HirNode(kind: hIf, ifCond: cond, ifThen: thenBranch, ifElse: elseBranch, typ: makeVoid(), loc: loc)
|
||||||
|
|
||||||
|
|
||||||
proc hirAlloca*(name: string, typ: Type, loc: SourceLocation): HirNode =
|
proc hirAlloca*(name: string, typ: Type, loc: SourceLocation): HirNode =
|
||||||
HirNode(kind: hAlloca, allocaType: typ, allocaName: name, typ: makePointer(typ), loc: loc)
|
HirNode(kind: hAlloca, allocaType: typ, allocaName: name, typ: makePointer(typ), loc: loc)
|
||||||
|
|
||||||
|
|||||||
+25
-3
@@ -539,9 +539,31 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
return hirUnary(expr.exprUnaryOp, operand, typ, loc)
|
return hirUnary(expr.exprUnaryOp, operand, typ, loc)
|
||||||
|
|
||||||
of ekBinary:
|
of ekBinary:
|
||||||
let left = ctx.lowerExpr(expr.exprBinaryLeft)
|
case expr.exprBinaryOp
|
||||||
let right = ctx.lowerExpr(expr.exprBinaryRight)
|
of tkAmpAmp:
|
||||||
return hirBinary(expr.exprBinaryOp, left, right, typ, loc)
|
# Short-circuit &&: use if-then-else to avoid evaluating right when left is false
|
||||||
|
let tmp = hirAlloca("__and_tmp_" & $ctx.varCounter, makeBool(), loc)
|
||||||
|
inc ctx.varCounter
|
||||||
|
let left = ctx.lowerExpr(expr.exprBinaryLeft)
|
||||||
|
let thenBlock = hirBlock(@[hirStore(tmp, ctx.lowerExpr(expr.exprBinaryRight), loc)], nil, makeVoid(), loc)
|
||||||
|
let falseTok = Token(kind: tkBoolLiteral, text: "false", loc: loc)
|
||||||
|
let elseBlock = hirBlock(@[hirStore(tmp, hirLit(falseTok, makeBool(), loc), loc)], nil, makeVoid(), loc)
|
||||||
|
let ifNode = hirIf(left, thenBlock, elseBlock, loc)
|
||||||
|
return hirBlock(@[tmp, ifNode], hirLoad(tmp, makeBool(), loc), makeBool(), loc)
|
||||||
|
of tkPipePipe:
|
||||||
|
# Short-circuit ||: use if-then-else to avoid evaluating right when left is true
|
||||||
|
let tmp = hirAlloca("__or_tmp_" & $ctx.varCounter, makeBool(), loc)
|
||||||
|
inc ctx.varCounter
|
||||||
|
let left = ctx.lowerExpr(expr.exprBinaryLeft)
|
||||||
|
let trueTok = Token(kind: tkBoolLiteral, text: "true", loc: loc)
|
||||||
|
let thenBlock = hirBlock(@[hirStore(tmp, hirLit(trueTok, makeBool(), loc), loc)], nil, makeVoid(), loc)
|
||||||
|
let elseBlock = hirBlock(@[hirStore(tmp, ctx.lowerExpr(expr.exprBinaryRight), loc)], nil, makeVoid(), loc)
|
||||||
|
let ifNode = hirIf(left, thenBlock, elseBlock, loc)
|
||||||
|
return hirBlock(@[tmp, ifNode], hirLoad(tmp, makeBool(), loc), makeBool(), loc)
|
||||||
|
else:
|
||||||
|
let left = ctx.lowerExpr(expr.exprBinaryLeft)
|
||||||
|
let right = ctx.lowerExpr(expr.exprBinaryRight)
|
||||||
|
return hirBinary(expr.exprBinaryOp, left, right, typ, loc)
|
||||||
|
|
||||||
of ekCall:
|
of ekCall:
|
||||||
# Method call desugaring: obj.method(args) → Type_method(obj, args)
|
# Method call desugaring: obj.method(args) → Type_method(obj, args)
|
||||||
|
|||||||
+157
-25
@@ -5,6 +5,20 @@
|
|||||||
import std/[strutils, strformat, tables, sequtils]
|
import std/[strutils, strformat, tables, sequtils]
|
||||||
import ast, types, token, hir, lir
|
import ast, types, token, hir, lir
|
||||||
|
|
||||||
|
## Convert LirValue to C expression string (no % prefix)
|
||||||
|
proc lirValToC(v: LirValue): string =
|
||||||
|
case v.kind
|
||||||
|
of lvkTemp: v.strVal
|
||||||
|
of lvkVar: v.strVal
|
||||||
|
of lvkInt: $v.intVal
|
||||||
|
of lvkFloat: $v.floatVal
|
||||||
|
of lvkString: v.strVal
|
||||||
|
of lvkLabel: v.strVal
|
||||||
|
of lvkGlobal: v.strVal
|
||||||
|
of lvkField: v.strVal
|
||||||
|
of lvkVoid: ""
|
||||||
|
of lvkType: v.strVal
|
||||||
|
|
||||||
type
|
type
|
||||||
LowerToLirCtx* = object
|
LowerToLirCtx* = object
|
||||||
builder*: LirBuilder
|
builder*: LirBuilder
|
||||||
@@ -172,6 +186,16 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
return ctx.varLirValues[name]
|
return ctx.varLirValues[name]
|
||||||
return lirVar(name)
|
return lirVar(name)
|
||||||
|
|
||||||
|
# ── Alloca (address of local) ──
|
||||||
|
of hAlloca:
|
||||||
|
let cType = typeToCStr(node.allocaType)
|
||||||
|
let name = node.allocaName
|
||||||
|
if not ctx.varLirValues.hasKey(name):
|
||||||
|
ctx.varTypes[name] = cType
|
||||||
|
ctx.varLirValues[name] = lirVar(name)
|
||||||
|
b.emitAlloca(name, cType)
|
||||||
|
return lirVar("&" & name)
|
||||||
|
|
||||||
# ── Self ──
|
# ── Self ──
|
||||||
of hSelf:
|
of hSelf:
|
||||||
return lirVar("self")
|
return lirVar("self")
|
||||||
@@ -198,7 +222,37 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
b.emitLoad(t, operand)
|
b.emitLoad(t, operand)
|
||||||
return t
|
return t
|
||||||
of tkAmp:
|
of tkAmp:
|
||||||
# Address of: &var
|
# Address of: &expr
|
||||||
|
# Optimize: &struct.field → fieldPtr (no temp copy)
|
||||||
|
# &array[i] → indexPtr (no temp copy)
|
||||||
|
if node.unaryOperand.kind == hLoad and node.unaryOperand.loadPtr != nil:
|
||||||
|
let ptrNode = node.unaryOperand.loadPtr
|
||||||
|
case ptrNode.kind
|
||||||
|
of hFieldPtr:
|
||||||
|
let base = lowerExpr(ctx, ptrNode.fieldPtrBase)
|
||||||
|
let baseTyp = ptrNode.fieldPtrBase.typ
|
||||||
|
let isPtr = baseTyp != nil and baseTyp.kind in {tkPointer, tkRef, tkMutRef}
|
||||||
|
let t = b.freshTemp()
|
||||||
|
b.emitAlloca(t.strVal, "void*")
|
||||||
|
if isPtr:
|
||||||
|
b.emitRawC(&"{t.strVal} = &({lirValToC(base)}->{ptrNode.fieldName});")
|
||||||
|
else:
|
||||||
|
b.emitRawC(&"{t.strVal} = &({lirValToC(base)}.{ptrNode.fieldName});")
|
||||||
|
return t
|
||||||
|
of hArrowField:
|
||||||
|
let base = lowerExpr(ctx, ptrNode.arrowFieldBase)
|
||||||
|
let t = b.freshTemp()
|
||||||
|
b.emitAlloca(t.strVal, "void*")
|
||||||
|
b.emitRawC(&"{t.strVal} = &({lirValToC(base)}->{ptrNode.arrowFieldName});")
|
||||||
|
return t
|
||||||
|
of hIndexPtr:
|
||||||
|
let base = lowerExpr(ctx, ptrNode.indexPtrBase)
|
||||||
|
let idx = lowerExpr(ctx, ptrNode.indexPtrIndex)
|
||||||
|
let t = b.freshTemp()
|
||||||
|
b.emitAlloca(t.strVal, "void*")
|
||||||
|
b.emitRawC(&"{t.strVal} = &({lirValToC(base)}[{lirValToC(idx)}]);")
|
||||||
|
return t
|
||||||
|
else: discard
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitAddrOf(t, operand)
|
b.emitAddrOf(t, operand)
|
||||||
return t
|
return t
|
||||||
@@ -215,18 +269,30 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
b.emitCmp(cmpOpToLir(node.binaryOp), t, left, right)
|
b.emitCmp(cmpOpToLir(node.binaryOp), t, left, right)
|
||||||
return t
|
return t
|
||||||
of tkAmpAmp, tkPipePipe:
|
of tkAmpAmp, tkPipePipe:
|
||||||
# Logical and/or: lowered to select
|
# Logical and/or: lowered to branches for short-circuit evaluation
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
|
b.emitAlloca(t.strVal, "int")
|
||||||
|
let falseLbl = b.freshLabel("and_false")
|
||||||
|
let trueLbl = b.freshLabel("and_true")
|
||||||
|
let endLbl = b.freshLabel("and_end")
|
||||||
if node.binaryOp == tkAmpAmp:
|
if node.binaryOp == tkAmpAmp:
|
||||||
# left && right → left ? (right != 0) : 0
|
# left && right: if !left goto false; if !right goto false; t=1; goto end; false: t=0; end:
|
||||||
let rhsBool = b.freshTemp()
|
b.emitJz(falseLbl, left)
|
||||||
b.emitCmp(lirCmpNe, rhsBool, right, lirInt(0))
|
b.emitJz(falseLbl, right)
|
||||||
b.emitSelect(t, left, rhsBool, lirInt(0))
|
b.emitMov(t, lirInt(1))
|
||||||
|
b.emitJmp(endLbl)
|
||||||
|
b.emitLabel(falseLbl)
|
||||||
|
b.emitMov(t, lirInt(0))
|
||||||
|
b.emitLabel(endLbl)
|
||||||
else:
|
else:
|
||||||
# left || right → left ? 1 : (right != 0)
|
# left || right: if left goto true; if right goto true; t=0; goto end; true: t=1; end:
|
||||||
let rhsBool = b.freshTemp()
|
b.emitJnz(trueLbl, left)
|
||||||
b.emitCmp(lirCmpNe, rhsBool, right, lirInt(0))
|
b.emitJnz(trueLbl, right)
|
||||||
b.emitSelect(t, left, lirInt(1), rhsBool)
|
b.emitMov(t, lirInt(0))
|
||||||
|
b.emitJmp(endLbl)
|
||||||
|
b.emitLabel(trueLbl)
|
||||||
|
b.emitMov(t, lirInt(1))
|
||||||
|
b.emitLabel(endLbl)
|
||||||
return t
|
return t
|
||||||
else:
|
else:
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
@@ -269,16 +335,16 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitAlloca(t.strVal, "void*")
|
b.emitAlloca(t.strVal, "void*")
|
||||||
if isPtr:
|
if isPtr:
|
||||||
b.emitRawC(&"{t.strVal} = (void*)&({base.strVal}->{node.fieldName});")
|
b.emitRawC(&"{t.strVal} = (void*)&({lirValToC(base)}->{node.fieldName});")
|
||||||
else:
|
else:
|
||||||
b.emitRawC(&"{t.strVal} = (void*)&({base.strVal}.{node.fieldName});")
|
b.emitRawC(&"{t.strVal} = (void*)&({lirValToC(base)}.{node.fieldName});")
|
||||||
return t
|
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()
|
||||||
b.emitAlloca(t.strVal, "void*")
|
b.emitAlloca(t.strVal, "void*")
|
||||||
b.emitRawC(&"{t.strVal} = (void*)&({base.strVal}->{node.arrowFieldName});")
|
b.emitRawC(&"{t.strVal} = (void*)&({lirValToC(base)}->{node.arrowFieldName});")
|
||||||
return t
|
return t
|
||||||
|
|
||||||
of hIndexPtr:
|
of hIndexPtr:
|
||||||
@@ -286,7 +352,7 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
let idx = lowerExpr(ctx, node.indexPtrIndex)
|
let idx = lowerExpr(ctx, node.indexPtrIndex)
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitAlloca(t.strVal, "void*")
|
b.emitAlloca(t.strVal, "void*")
|
||||||
b.emitRawC(&"{t.strVal} = (void*)&({base.strVal}[{idx.strVal}]);")
|
b.emitRawC(&"{t.strVal} = (void*)&({lirValToC(base)}[{lirValToC(idx)}]);")
|
||||||
return t
|
return t
|
||||||
|
|
||||||
# ── Load ──
|
# ── Load ──
|
||||||
@@ -298,7 +364,7 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
let cType = hirTypeToC(ctx, node)
|
let cType = hirTypeToC(ctx, node)
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitAlloca(t.strVal, cType)
|
b.emitAlloca(t.strVal, cType)
|
||||||
b.emitRawC(&"{t.strVal} = {base.strVal}->{node.loadPtr.arrowFieldName};")
|
b.emitRawC(&"{t.strVal} = {lirValToC(base)}->{node.loadPtr.arrowFieldName};")
|
||||||
return t
|
return t
|
||||||
if node.loadPtr != nil and node.loadPtr.kind == hFieldPtr:
|
if node.loadPtr != nil and node.loadPtr.kind == hFieldPtr:
|
||||||
let base = lowerExpr(ctx, node.loadPtr.fieldPtrBase)
|
let base = lowerExpr(ctx, node.loadPtr.fieldPtrBase)
|
||||||
@@ -308,9 +374,9 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitAlloca(t.strVal, cType)
|
b.emitAlloca(t.strVal, cType)
|
||||||
if isPtr:
|
if isPtr:
|
||||||
b.emitRawC(&"{t.strVal} = {base.strVal}->{node.loadPtr.fieldName};")
|
b.emitRawC(&"{t.strVal} = {lirValToC(base)}->{node.loadPtr.fieldName};")
|
||||||
else:
|
else:
|
||||||
b.emitRawC(&"{t.strVal} = {base.strVal}.{node.loadPtr.fieldName};")
|
b.emitRawC(&"{t.strVal} = {lirValToC(base)}.{node.loadPtr.fieldName};")
|
||||||
return t
|
return t
|
||||||
if node.loadPtr != nil and node.loadPtr.kind == hIndexPtr:
|
if node.loadPtr != nil and node.loadPtr.kind == hIndexPtr:
|
||||||
let base = lowerExpr(ctx, node.loadPtr.indexPtrBase)
|
let base = lowerExpr(ctx, node.loadPtr.indexPtrBase)
|
||||||
@@ -318,14 +384,14 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
let cType = hirTypeToC(ctx, node)
|
let cType = hirTypeToC(ctx, node)
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitAlloca(t.strVal, cType)
|
b.emitAlloca(t.strVal, cType)
|
||||||
b.emitRawC(&"{t.strVal} = {base.strVal}[{idx.strVal}];")
|
b.emitRawC(&"{t.strVal} = {lirValToC(base)}[{lirValToC(idx)}];")
|
||||||
return t
|
return t
|
||||||
# Generic: dereference pointer
|
# Generic: dereference pointer
|
||||||
let ptrVal = lowerExpr(ctx, node.loadPtr)
|
let ptrVal = lowerExpr(ctx, node.loadPtr)
|
||||||
let cType = hirTypeToC(ctx, node)
|
let cType = hirTypeToC(ctx, node)
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitAlloca(t.strVal, cType)
|
b.emitAlloca(t.strVal, cType)
|
||||||
b.emitRawC(&"{t.strVal} = *({cType}*){ptrVal.strVal};")
|
b.emitRawC(&"{t.strVal} = *({cType}*){lirValToC(ptrVal)};")
|
||||||
return t
|
return t
|
||||||
|
|
||||||
# ── Slice Index ──
|
# ── Slice Index ──
|
||||||
@@ -335,7 +401,7 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
# Emit: base.data[idx] (with optional bounds check)
|
# Emit: base.data[idx] (with optional bounds check)
|
||||||
if node.sliceIndexBoundsCheck:
|
if node.sliceIndexBoundsCheck:
|
||||||
b.emitRawC(&"bux_bounds_check((size_t)({idx.strVal}), ({base.strVal}).len)")
|
b.emitRawC(&"bux_bounds_check((size_t)({lirValToC(idx)}), ({lirValToC(base)}).len)")
|
||||||
b.emit(LirInstr(kind: lirLoad, dst: t, src: base, src2: idx))
|
b.emit(LirInstr(kind: lirLoad, dst: t, src: base, src2: idx))
|
||||||
return t
|
return t
|
||||||
|
|
||||||
@@ -389,7 +455,7 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
for i in 1 ..< node.dynCallArgs.len:
|
for i in 1 ..< node.dynCallArgs.len:
|
||||||
args.add(lowerExpr(ctx, node.dynCallArgs[i]))
|
args.add(lowerExpr(ctx, node.dynCallArgs[i]))
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitRawC(&"{t.strVal} = {receiver.strVal}.vtable->{node.dynCallMethod}({args.mapIt($it).join(\", \")});")
|
b.emitRawC(&"{t.strVal} = {lirValToC(receiver)}.vtable->{node.dynCallMethod}({args.mapIt($it).join(\", \")});")
|
||||||
return t
|
return t
|
||||||
|
|
||||||
# ── StructInit ──
|
# ── StructInit ──
|
||||||
@@ -453,9 +519,39 @@ proc lowerExpr(ctx: var LowerToLirCtx, node: HirNode): LirValue =
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# Fallback for unhandled expression kinds
|
# Fallback for unhandled expression kinds
|
||||||
b.emitComment(&"/* unhandled expr kind: {node.kind} */")
|
b.emitComment(&"unhandled expr kind: {node.kind}")
|
||||||
return lirInt(0)
|
return lirInt(0)
|
||||||
|
|
||||||
|
# ── Build C lvalue string for direct field/index assignment ──
|
||||||
|
proc buildLval(ctx: var LowerToLirCtx, n: HirNode): string =
|
||||||
|
case n.kind
|
||||||
|
of hLoad:
|
||||||
|
if n.loadPtr != nil:
|
||||||
|
return buildLval(ctx, n.loadPtr)
|
||||||
|
else:
|
||||||
|
let v = lowerExpr(ctx, n)
|
||||||
|
return lirValToC(v)
|
||||||
|
of hVar:
|
||||||
|
return n.varName
|
||||||
|
of hSelf:
|
||||||
|
return "self"
|
||||||
|
of hFieldPtr:
|
||||||
|
let baseStr = buildLval(ctx, n.fieldPtrBase)
|
||||||
|
let baseTyp = n.fieldPtrBase.typ
|
||||||
|
let isPtr = baseTyp != nil and baseTyp.kind in {tkPointer, tkRef, tkMutRef}
|
||||||
|
let sep = if isPtr: "->" else: "."
|
||||||
|
return baseStr & sep & n.fieldName
|
||||||
|
of hArrowField:
|
||||||
|
let baseStr = buildLval(ctx, n.arrowFieldBase)
|
||||||
|
return baseStr & "->" & n.arrowFieldName
|
||||||
|
of hIndexPtr:
|
||||||
|
let baseStr = buildLval(ctx, n.indexPtrBase)
|
||||||
|
let idx = lowerExpr(ctx, n.indexPtrIndex)
|
||||||
|
return baseStr & "[" & lirValToC(idx) & "]"
|
||||||
|
else:
|
||||||
|
let v = lowerExpr(ctx, n)
|
||||||
|
return lirValToC(v)
|
||||||
|
|
||||||
# ── Lowering: Statements → void ──
|
# ── Lowering: Statements → void ──
|
||||||
|
|
||||||
proc lowerStmt(ctx: var LowerToLirCtx, node: HirNode) =
|
proc lowerStmt(ctx: var LowerToLirCtx, node: HirNode) =
|
||||||
@@ -557,24 +653,60 @@ proc lowerStmt(ctx: var LowerToLirCtx, node: HirNode) =
|
|||||||
let val = lowerExpr(ctx, node.storeValue)
|
let val = lowerExpr(ctx, node.storeValue)
|
||||||
# ptrVal is a void* address; cast and store
|
# ptrVal is a void* address; cast and store
|
||||||
let valCType = hirTypeToC(ctx, node.storeValue)
|
let valCType = hirTypeToC(ctx, node.storeValue)
|
||||||
b.emitRawC(&"*({valCType}*){ptrVal.strVal} = {val.strVal};")
|
b.emitRawC(&"*({valCType}*){lirValToC(ptrVal)} = {lirValToC(val)};")
|
||||||
|
|
||||||
# ── Assign ──
|
# ── Assign ──
|
||||||
of hAssign:
|
of hAssign:
|
||||||
let target = lowerExpr(ctx, node.assignTarget)
|
|
||||||
let value = lowerExpr(ctx, node.assignValue)
|
let value = lowerExpr(ctx, node.assignValue)
|
||||||
case node.assignOp
|
case node.assignOp
|
||||||
of tkAssign:
|
of tkAssign:
|
||||||
b.emitMov(target, value)
|
case node.assignTarget.kind
|
||||||
|
of hFieldPtr:
|
||||||
|
let lval = buildLval(ctx, node.assignTarget)
|
||||||
|
b.emitRawC(&"{lval} = {lirValToC(value)};")
|
||||||
|
of hArrowField:
|
||||||
|
let lval = buildLval(ctx, node.assignTarget)
|
||||||
|
b.emitRawC(&"{lval} = {lirValToC(value)};")
|
||||||
|
of hIndexPtr:
|
||||||
|
let base = lowerExpr(ctx, node.assignTarget.indexPtrBase)
|
||||||
|
let idx = lowerExpr(ctx, node.assignTarget.indexPtrIndex)
|
||||||
|
b.emit(LirInstr(kind: lirStore, src: value, src2: base, dst: idx))
|
||||||
|
of hLoad:
|
||||||
|
if node.assignTarget.loadPtr != nil:
|
||||||
|
let ptrNode = node.assignTarget.loadPtr
|
||||||
|
case ptrNode.kind
|
||||||
|
of hIndexPtr:
|
||||||
|
let base = lowerExpr(ctx, ptrNode.indexPtrBase)
|
||||||
|
let idx = lowerExpr(ctx, ptrNode.indexPtrIndex)
|
||||||
|
b.emit(LirInstr(kind: lirStore, src: value, src2: base, dst: idx))
|
||||||
|
of hFieldPtr:
|
||||||
|
let lval = buildLval(ctx, ptrNode)
|
||||||
|
b.emitRawC(&"{lval} = {lirValToC(value)};")
|
||||||
|
of hArrowField:
|
||||||
|
let lval = buildLval(ctx, ptrNode)
|
||||||
|
b.emitRawC(&"{lval} = {lirValToC(value)};")
|
||||||
|
else:
|
||||||
|
let ptrVal = lowerExpr(ctx, ptrNode)
|
||||||
|
let valCType = hirTypeToC(ctx, node.assignValue)
|
||||||
|
b.emitRawC(&"*({valCType}*){lirValToC(ptrVal)} = {lirValToC(value)};")
|
||||||
|
else:
|
||||||
|
let target = lowerExpr(ctx, node.assignTarget)
|
||||||
|
b.emitMov(target, value)
|
||||||
|
else:
|
||||||
|
let target = lowerExpr(ctx, node.assignTarget)
|
||||||
|
b.emitMov(target, value)
|
||||||
of tkPlusAssign:
|
of tkPlusAssign:
|
||||||
|
let target = lowerExpr(ctx, node.assignTarget)
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitBinOp(lirAdd, t, target, value)
|
b.emitBinOp(lirAdd, t, target, value)
|
||||||
b.emit(LirInstr(kind: lirStore, src: t, src2: target))
|
b.emit(LirInstr(kind: lirStore, src: t, src2: target))
|
||||||
of tkMinusAssign:
|
of tkMinusAssign:
|
||||||
|
let target = lowerExpr(ctx, node.assignTarget)
|
||||||
let t = b.freshTemp()
|
let t = b.freshTemp()
|
||||||
b.emitBinOp(lirSub, t, target, value)
|
b.emitBinOp(lirSub, t, target, value)
|
||||||
b.emit(LirInstr(kind: lirStore, src: t, src2: target))
|
b.emit(LirInstr(kind: lirStore, src: t, src2: target))
|
||||||
else:
|
else:
|
||||||
|
let target = lowerExpr(ctx, node.assignTarget)
|
||||||
b.emitMov(target, value)
|
b.emitMov(target, value)
|
||||||
|
|
||||||
# ── Call statement (void return) ──
|
# ── Call statement (void return) ──
|
||||||
|
|||||||
+5
-1
@@ -239,9 +239,13 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
// Try to resolve return type from function declaration
|
// Try to resolve return type from function declaration
|
||||||
if expr.child1.kind == ekIdent {
|
if expr.child1.kind == ekIdent {
|
||||||
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
|
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
|
||||||
if sym.kind == skFunc && sym.decl != null as *Decl && sym.decl.retType != null as *TypeExpr {
|
if sym.kind == skFunc {
|
||||||
|
if sym.decl != null as *Decl {
|
||||||
|
if sym.decl.retType != null as *TypeExpr {
|
||||||
return Sema_ResolveType(sema, sym.decl.retType);
|
return Sema_ResolveType(sema, sym.decl.retType);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return tyUnknown;
|
return tyUnknown;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user