@@ -138,11 +138,11 @@ class StaticOptimizer(
138138 case e @ UnaryOp (pos, op, v : Val ) => tryFoldUnaryOp(pos, op, v, e)
139139
140140 // Branch elimination: constant condition in if-else
141- case IfElse (_ , _ : Val .True , thenExpr, _) =>
142- thenExpr
143- case IfElse (_ , _ : Val .False , _, elseExpr) =>
144- if (elseExpr == null ) Val .staticNull
145- else elseExpr
141+ case IfElse (pos , _ : Val .True , thenExpr, _) =>
142+ thenExpr.pos = pos; thenExpr
143+ case IfElse (pos , _ : Val .False , _, elseExpr) =>
144+ if (elseExpr == null ) Val .Null (pos)
145+ else { elseExpr.pos = pos; elseExpr }
146146
147147 // Short-circuit elimination for And/Or with constant lhs.
148148 //
@@ -152,12 +152,10 @@ class StaticOptimizer(
152152 // into just `rhs` without the Bool guard, we silently remove that runtime type check,
153153 // causing programs like `true && "hello"` to return "hello" instead of erroring.
154154 // See: Evaluator.visitAnd / Evaluator.visitOr for the authoritative runtime semantics.
155- // The lhs-result cases return lhs itself to keep the selected boolean's position and avoid
156- // turning the already-created literal/folded lhs into garbage.
157- case And (_, _ : Val .True , rhs : Val .Bool ) => rhs
158- case And (_, lhs : Val .False , _) => lhs
159- case Or (_, lhs : Val .True , _) => lhs
160- case Or (_, _ : Val .False , rhs : Val .Bool ) => rhs
155+ case And (pos, _ : Val .True , rhs : Val .Bool ) => rhs.pos = pos; rhs
156+ case And (pos, _ : Val .False , _) => Val .False (pos)
157+ case Or (pos, _ : Val .True , _) => Val .True (pos)
158+ case Or (pos, _ : Val .False , rhs : Val .Bool ) => rhs.pos = pos; rhs
161159
162160 // Identity-equivalent function recognition. Cheap pattern match here keeps the runtime
163161 // fast path (`Val.Func.isEffectivelyIdentity`) at single-field cost.
0 commit comments