From c3a9eb33a4889322b4e75e0ef8c79b5d318607e1 Mon Sep 17 00:00:00 2001 From: CharlesKWON Date: Sat, 18 Apr 2026 08:42:08 +0900 Subject: [PATCH] perf(gengo): fold .NOT. at compile time `.NOT. .T.` / `.NOT. .F.` emit PushBool directly instead of pushing the source bool and calling Not(). boolLiteralValue also sees through an outer NOT, so `IF !.F.` now triggers the full dead-branch pass (no PopLogical wrapper either). FiveSql2 43/43, Harbour compat 56/56. Co-Authored-By: Claude Opus 4.7 (1M context) --- compiler/gengo/gengo.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/compiler/gengo/gengo.go b/compiler/gengo/gengo.go index c4501d1..121e42a 100644 --- a/compiler/gengo/gengo.go +++ b/compiler/gengo/gengo.go @@ -1265,9 +1265,17 @@ func (g *Generator) emitCallAsStmt(call *ast.CallExpr, locals localMap) { g.writeln(fmt.Sprintf("t.Do(%d)", len(call.Args))) } -// boolLiteralValue returns (value, true) if e is a bare .T./.F. literal. -// Used by emitIf to skip dead branches. +// boolLiteralValue returns (value, true) if e reduces to a .T./.F. +// literal at compile time. Sees through an outer `.NOT.` so expressions +// like `!.F.` also collapse. Used by emitIf to skip dead branches and +// by the AND/OR short-circuit emitter. func boolLiteralValue(e ast.Expr) (bool, bool) { + if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.NOT { + if v, ok := boolLiteralValue(u.X); ok { + return !v, true + } + return false, false + } lit, ok := e.(*ast.LiteralExpr) if !ok { return false, false @@ -1988,6 +1996,17 @@ func (g *Generator) emitExpr(expr ast.Expr) { } } } + // Fold `.NOT. .T.` → .F. and `.NOT. .F.` → .T. at compile time. + if e.Op == token.NOT { + if v, ok := boolLiteralValue(e.X); ok { + if v { + g.writeln("t.PushBool(false)") + } else { + g.writeln("t.PushBool(true)") + } + break + } + } g.emitExpr(e.X) g.emitUnaryOp(e.Op) case *ast.AssignExpr: