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: