Skip to content

Engine: ++/-- on BigInt operand throws TypeError instead of producing BigInt #540

@frostney

Description

@frostney

Summary

++ and -- on a BigInt-valued binding throw TypeError: Cannot convert a BigInt value to a number instead of producing a BigInt result. ES2026 §13.4.4.1 (PostfixIncrement) and §13.4.5.1 (PostfixDecrement) call Type(oldValue)::add(oldValue, Type(oldValue)::unit) where unit is 1n for BigInt and 1 for Number; Goccia's compiler hard-codes OP_LOAD_INT 1 so the runtime hits BigInt-vs-Int in OP_ADD / OP_SUB and rejects.

Reproducer

let i = 10n;
i++;
console.log(i);  // expected 11n, got TypeError
./build/GocciaScriptLoaderBare --compat-all --mode=bytecode -
let i = 10n; i++; print(i);
^D
# Error: TypeError: Cannot convert a BigInt value to a number

Affected sites

  • source/units/Goccia.Compiler.Expressions.pas:2906-2916CompileIncrement non-global-backed local branch loads OP_LOAD_INT 1 regardless of operand type.
  • Symmetric branches at lines 2887-2904 (global-backed local), 2929-2946 (upvalue), and 2791-2845 (member / computed-member targets) have the same issue.

Likely fix

Two options:

  1. Compiler-level: detect operand type at runtime — compile ++ as if value is BigInt then value + 1n else value + 1. Either via a new OP_INC / OP_DEC opcode that picks the unit based on the operand's runtime type, or via existing branches.
  2. VM-level: have OP_ADD / OP_SUB widen the Int operand to BigInt automatically when the other is BigInt. Less spec-pure (5n + 1 should still throw) but localized.

Recommend (1).

Why this surfaced now

#531 enables --compat-traditional-for-loop via --compat-all, which test262 already uses. Several test262 tests (e.g., built-ins/BigInt/prototype/toString/a-z.js) use for (let i = 10n; i < radix; i++) ... to iterate over BigInt indices; previously the loop body warn-and-skipped, hiding this bug.

Scope notes

  • Surfaced by full bytecode-mode test262 run on PR Add --compat-traditional-for-loop opt-in flag #531.
  • Interpreter mode has the same divergence — EvaluateIncrement calls PerformIncrement(OldValue, true) which uses Number(1).
  • Follow-up: += 1n and i = i + 1n work correctly today; only the prefix/postfix ++/-- are broken.

Metadata

Metadata

Assignees

No one assigned

    Labels

    engineTGocciaEngine: language semantics, ECMAScript built-ins, parser, interpreter, bytecode VMspec complianceMismatch against official JavaScript/TypeScript specification

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions