fix: 64-bit integer constants — no truncation, accept full bit pattern (issue #133)
Two related defects in integer constant handling: - A large hex literal that needs more than 32 bits (e.g. $080808080808 = 8830587504648) was silently TRUNCATED to 32 bits (printed 134744072). An untyped integer constant was unconditionally typed as Integer regardless of its magnitude, so codegen treated it as a 32-bit word. - A literal above High(Int64) (e.g. $8080808080808080) was REJECTED at parse time with "Integer literal exceeds Int64 range", even though it is a valid 64-bit bit pattern and is accepted by FPC/Delphi. Fixes: - uParser: the bare-integer-literal const path now uses ParseIntOrUInt64Literal (which already existed for the tolerant case) instead of ParseIntLiteral, so a value in the UInt64 range is captured as a bit pattern and flagged via a new TConstDecl.IsUInt64. - uSemantic (AnalyseConstDecls): an untyped integer constant now picks its type by magnitude — Integer when it fits in signed 32-bit, Int64 when larger, and UInt64 when the value exceeds High(Int64) (matching Delphi, which prints $8080808080808080 as 9259542123273814144). A typed const (e.g. A: Int64 = $8080808080808080) keeps its declared type and stores the bit pattern, so it prints -9187201950435737472. Tests (cp.test.e2e.misc.pas, dual-backend): large hex not truncated, typed Int64 bit pattern, typed UInt64 bit pattern, and untyped-above-Int64 widening to UInt64. Verified: FIXPOINT_OK, NATIVE_FIXPOINT_OK, NATIVE_INTERNAL_OK; full suite green on both the QBE-built and native-built runners (3704 tests).
This commit is contained in:
parent
fe5460f79b
commit
c5d49ba03e
|
|
@ -574,7 +574,8 @@ type
|
|||
public
|
||||
Name: string;
|
||||
TypeName: string; { non-empty when a type annotation was written }
|
||||
IntVal: Int64; { used when kind = integer }
|
||||
IntVal: Int64; { used when kind = integer (bit pattern when IsUInt64) }
|
||||
IsUInt64: Boolean; { value exceeded High(Int64): IntVal is the UInt64 bit pattern }
|
||||
StrVal: string; { used when IsString = True or IsFloat = True (raw text) }
|
||||
IsString: Boolean;
|
||||
IsFloat: Boolean; { set when the rhs is a float literal }
|
||||
|
|
|
|||
|
|
@ -1395,13 +1395,18 @@ begin
|
|||
end
|
||||
else if Check(tkIntLit) then
|
||||
begin
|
||||
CD.IntVal := ParseIntLiteral(FCurrent.Value);
|
||||
{ Accept the full 64-bit unsigned range as a bit pattern (e.g.
|
||||
$8080808080808080): ParseIntOrUInt64Literal yields the bit pattern and
|
||||
flags when the value exceeded High(Int64). This is the Int64/UInt64
|
||||
hex-constant case from issue #133. }
|
||||
ParseIntOrUInt64Literal(FCurrent.Value, CD.IntVal, CD.IsUInt64);
|
||||
CD.IsString := False;
|
||||
Advance();
|
||||
if CurrentIsConstBitOp() then
|
||||
begin
|
||||
CD.IntExprTokens := CollectConstBitOpExpr(IntToStr(CD.IntVal), False);
|
||||
CD.IntVal := 0;
|
||||
CD.IsUInt64 := False;
|
||||
end;
|
||||
end
|
||||
else if Check(tkIdent) and (PeekKind() = tkLParen)
|
||||
|
|
|
|||
|
|
@ -4282,6 +4282,14 @@ begin
|
|||
TD := FTable.TypeString
|
||||
else if CD.IsFloat then
|
||||
TD := FTable.TypeDouble
|
||||
{ Untyped integer constant: pick the width by magnitude so a value that does
|
||||
not fit in 32 bits is NOT silently truncated to Integer (issue #133). A
|
||||
UInt64-range bit pattern (above High(Int64)) types as UInt64; a value
|
||||
outside the signed 32-bit range types as Int64; otherwise Integer. }
|
||||
else if CD.IsUInt64 then
|
||||
TD := FTable.TypeUInt64
|
||||
else if (CD.IntVal > 2147483647) or (CD.IntVal < -2147483648) then
|
||||
TD := FTable.TypeInt64
|
||||
else
|
||||
TD := FTable.TypeInteger;
|
||||
Sym := TSymbol.Create(CD.Name, skConstant, TD);
|
||||
|
|
|
|||
|
|
@ -122,6 +122,15 @@ type
|
|||
{ forward; in a program decl section (issue #130 bug2): the forward decl
|
||||
used to swallow the following implementation as a nested-proc body. }
|
||||
procedure TestRun_Forward_MutualRecursion;
|
||||
|
||||
{ Large / 64-bit integer constants (issue #133): a hex value above 32 bits
|
||||
was truncated (inferred Integer); a value above High(Int64) was rejected.
|
||||
Untyped consts now widen by magnitude; typed Int64/UInt64 accept the full
|
||||
64-bit bit pattern. }
|
||||
procedure TestRun_Const_LargeHex_NotTruncated;
|
||||
procedure TestRun_Const_Int64BitPattern;
|
||||
procedure TestRun_Const_UInt64BitPattern;
|
||||
procedure TestRun_Const_UntypedAboveInt64_IsUInt64;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
|
@ -1224,6 +1233,58 @@ begin
|
|||
AssertRunsOnAll(Src, '200 -7 250' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2EMiscTests.TestRun_Const_LargeHex_NotTruncated;
|
||||
const
|
||||
{ $080808080808 = 8830587504648, needs 64 bits — must not truncate to 32. }
|
||||
Src = '''
|
||||
program P;
|
||||
const DECI = $080808080808;
|
||||
begin WriteLn(DECI) end.
|
||||
''';
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnAll(Src, '8830587504648' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2EMiscTests.TestRun_Const_Int64BitPattern;
|
||||
const
|
||||
{ $8080808080808080 as Int64 is the bit pattern -9187201950435737472. }
|
||||
Src = '''
|
||||
program P;
|
||||
const A: Int64 = $8080808080808080;
|
||||
begin WriteLn(A) end.
|
||||
''';
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnAll(Src, '-9187201950435737472' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2EMiscTests.TestRun_Const_UInt64BitPattern;
|
||||
const
|
||||
{ Same bits as UInt64 = 9259542123273814144. }
|
||||
Src = '''
|
||||
program P;
|
||||
const A: UInt64 = $8080808080808080;
|
||||
begin WriteLn(A) end.
|
||||
''';
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnAll(Src, '9259542123273814144' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2EMiscTests.TestRun_Const_UntypedAboveInt64_IsUInt64;
|
||||
const
|
||||
{ An untyped literal above High(Int64) types as UInt64 (matches Delphi). }
|
||||
Src = '''
|
||||
program P;
|
||||
const A = $8080808080808080;
|
||||
begin WriteLn(A) end.
|
||||
''';
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnAll(Src, '9259542123273814144' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2EMiscTests.TestRun_Forward_MutualRecursion;
|
||||
const
|
||||
{ Mutually-recursive routines via a forward; declaration in the program's
|
||||
|
|
|
|||
Loading…
Reference in a new issue