Skip to content

Preserve the L suffix on converted long literals - #175

Merged
paulirwin merged 1 commit into
masterfrom
fix/128-long-literal-suffix
Aug 17, 2026
Merged

Preserve the L suffix on converted long literals#175
paulirwin merged 1 commit into
masterfrom
fix/128-long-literal-suffix

Conversation

@paulirwin

Copy link
Copy Markdown
Owner

Problem

LongLiteralExpressionVisitor stripped the trailing L/l from a Java long literal and emitted the remaining text verbatim, so the generated C# lost the suffix that makes it a long.

That produced code which does not compile. C# types a bare numeric literal as int, so:

long allOnes = 0xFFFFFFFFFFFFFFFFL;

converted to:

long allOnes = 0xFFFFFFFFFFFFFFFF;   // error CS0266: cannot implicitly convert 'ulong' to 'long'

Fix

  • Re-append the L suffix when building the literal token.
  • Strip the incoming L/l as a suffix rather than via a blanket Replace over the whole string, which was scanning every character of a value we echo back into the generated source.
  • A hex literal above long.MaxValue stays invalid in C# even with the suffix, because the literal is typed by its magnitude. For that case emit the wrapped decimal value, which is the number Java means: 0xFFFFFFFFFFFFFFFFL-1L.

Why it went unnoticed

The existing long-literal tests asserted only the parsed numeric Value, never the emitted token text — so a literal with a correct value but uncompilable text passed. The new cases assert Text.

Testing

  • New VisitLiteralExpression_Long_PreservesSuffixInText theory covering hex, binary, octal, underscore separators, lowercase l, and both overflow boundaries.
  • Extra VisitLiteralExpression_Long cases for the two's-complement and above-int.MaxValue values.
  • Resources/LongLiterals.java wired into FullIntegrationTests, which compiles the generated C# and asserts its runtime output matches Java's.

Full suite: 378 passed, 0 failed (was 363).

Note on #128

The binary-literal crash originally reported in #128 (0b10, 0b100) no longer reproduces on master — it was fixed sometime after the 3.0.0 build in that report. Investigating it surfaced this adjacent defect in the long-literal path, which this PR fixes. Worth confirming whether #128 should now be closed.

🤖 Generated with Claude Code

LongLiteralExpressionVisitor stripped the trailing L/l from a Java long
literal and emitted the remaining text verbatim, so the generated C# lost
the suffix that makes it a long.

That produced code which does not compile. C# types a bare numeric literal
as int, so `long a = 0xFFFFFFFFFFFFFFFFL` became `long a = 0xFFFFFFFFFFFFFFFF`
-- a ulong that will not implicitly convert to long (CS0266).

Re-append the suffix when building the literal token, and strip the incoming
L/l as a suffix rather than via a blanket Replace over the whole string.

A hex literal above long.MaxValue stays invalid in C# even with the suffix,
since the literal is typed by its magnitude. For that case emit the wrapped
decimal value, which is the number Java means: 0xFFFFFFFFFFFFFFFFL -> -1L.

The existing long-literal tests only asserted the parsed numeric Value, which
is why this went unnoticed; the new cases assert the emitted token text.
LongLiterals.java covers it end to end through the compile-and-run harness.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@paulirwin
paulirwin enabled auto-merge (squash) August 17, 2026 19:38
@paulirwin
paulirwin merged commit 539f413 into master Aug 17, 2026
5 checks passed
@paulirwin
paulirwin deleted the fix/128-long-literal-suffix branch August 17, 2026 19:41
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