Skip to content

fix(parser): parenthesize unary minus operand to avoid C++ pre-decrement pasting - #20

Open
AlessioGiacobbe wants to merge 1 commit into
swoole:masterfrom
AlessioGiacobbe:fix/unary-minus-parens
Open

fix(parser): parenthesize unary minus operand to avoid C++ pre-decrement pasting#20
AlessioGiacobbe wants to merge 1 commit into
swoole:masterfrom
AlessioGiacobbe:fix/unary-minus-parens

Conversation

@AlessioGiacobbe

@AlessioGiacobbe AlessioGiacobbe commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Problem

parseUnaryMinus emits '-' . $code without guarding against an operand that itself starts with -, so a nested unary minus pastes into the C++ pre-decrement token: - -$x compiles to --x.

  • On a php::Var operand the generated translation unit fails to build: error: expression is not assignable.
  • On a native int operand it builds and silently corrupts the value:
function negNative(int $x): int {
    return - -$x;   // compiles to `return --x;` → returns 8 for input 9
}

Fix

Parenthesize the operand exactly when its emitted code starts with - (a nested unary minus or a negative literal): - -$x-(-(x)). Plain literals keep their compact form (-7L), so existing generated output is unchanged everywhere the old code was correct. Binary operands are already self-wrapped in parentheses, and unary plus needs no change since it returns the operand unchanged.

Tests

  • New tests/compiler/operator/unary-minus-nested.phpt: nested minus on php::Var, float, constant (-(-7)), triple chain, and the native-int function case above. Expected output verified against PHP 8.4.
  • operator + float_edge + bigint suites: 58/59 pass; the single failure (runtime-int-overflow-return.phpt) is a pre-existing environment issue (php::newClosureWithParameters missing from the local phpx build) and fails identically on master.
  • LocalVariableInitializerTest literal expectations (php::Var negative = -7L;) are preserved — negative literals still emit without parentheses. No new PHPStan errors.

parseUnaryMinus emitted '-' . $code without guarding against an operand
that itself starts with '-', pasting into the C++ pre-decrement token:
`- -$x` compiled to `--x`. On a php::Var operand the generated
translation unit fails to build ("expression is not assignable"); on a
native int operand it builds and silently decrements: a function
`(int $x) => - -$x` returned 8 for input 9.

Parenthesize the operand exactly when its emitted code starts with '-'
(a nested unary minus or a negative literal), so plain literals keep
their compact form (`-7L`). Binary operands are already self-wrapped in
parentheses, and unary plus needs no change since it returns the
operand unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant