Support text format for integer fields wider than 64 bits DecodeInteger gated its handling of a leading '-' on ::std::is_signed<IntType>, which is false for __int128_t under a strict -std=c++NN. As a result a negative value could not be parsed into a signed field wider than 64 bits (the '-' was treated as an invalid character and parsing failed), even though the same value encodes fine on the way out. Use ::std::numeric_limits<>::is_signed instead, matching the fix applied to IntView::CouldWriteValue. Add text-format round-trip tests for values that do not fit in 64 bits, including a negative value into a >64-bit signed field and rejection of an out-of-range text value.
diff --git a/compiler/back_end/cpp/testcode/int128_sizes_test.cc b/compiler/back_end/cpp/testcode/int128_sizes_test.cc index 9412b35..33091fd 100644 --- a/compiler/back_end/cpp/testcode/int128_sizes_test.cc +++ b/compiler/back_end/cpp/testcode/int128_sizes_test.cc
@@ -356,6 +356,52 @@ EXPECT_EQ(x.sixteen_byte().Read(), y.sixteen_byte().Read()); } +// Text format must round-trip values that do not fit in 64 bits, in both +// directions. Parsing a negative value into a >64-bit signed field in +// particular exercises the signedness check in DecodeInteger. +TEST(UInt128SizesView, TextFormatRoundTrip) { + ::std::uint8_t buffer[100] = {}; + auto writer = UInt128SizesWriter(buffer, sizeof buffer); + + // A 128-bit value that does not fit in 64 bits. + EXPECT_TRUE(::emboss::UpdateFromText( + writer, "{sixteen_byte: 0xfedcba98765432100123456789abcdef}")); + __uint128_t expected = + (static_cast<__uint128_t>(0xfedcba9876543210UL) << 64) | + static_cast<__uint128_t>(0x0123456789abcdefUL); + EXPECT_EQ(expected, writer.sixteen_byte().Read()); + + // Out-of-range text for a 72-bit field must be rejected, not crash. + EXPECT_FALSE(::emboss::UpdateFromText( + writer, "{nine_byte: 0x1000000000000000000}")); // 2**72 +} + +TEST(Int128SizesView, TextFormatRoundTripNegative) { + ::std::uint8_t buffer[100] = {}; + auto writer = Int128SizesWriter(buffer, sizeof buffer); + + // Negative value into a 72-bit signed field: the '-' must be honored even + // though ::std::is_signed<__int128_t> is false under a strict -std. + EXPECT_TRUE(::emboss::UpdateFromText(writer, "{nine_byte: -12345}")); + EXPECT_EQ(static_cast<__int128_t>(-12345), writer.nine_byte().Read()); + + // A large negative 128-bit value (-2**120) does not fit in 64 bits; it must + // round-trip through the decimal text format. + EXPECT_TRUE(::emboss::UpdateFromText( + writer, "{sixteen_byte: -1329227995784915872903807060280344576}")); + EXPECT_EQ(-(static_cast<__int128_t>(1) << 120), writer.sixteen_byte().Read()); + + // Round-trip through WriteToString and back. + ::std::string text = ::emboss::WriteToString( + MakeInt128SizesView(buffer, sizeof buffer)); + ::std::uint8_t buffer2[100] = {}; + auto writer2 = Int128SizesWriter(buffer2, sizeof buffer2); + EXPECT_TRUE(::emboss::UpdateFromText(writer2, text)); + EXPECT_EQ(-(static_cast<__int128_t>(1) << 120), + writer2.sixteen_byte().Read()); + EXPECT_EQ(static_cast<__int128_t>(-12345), writer2.nine_byte().Read()); +} + } // namespace } // namespace test } // namespace emboss
diff --git a/runtime/cpp/emboss_text_util.h b/runtime/cpp/emboss_text_util.h index 9cfe03d..061ea31 100644 --- a/runtime/cpp/emboss_text_util.h +++ b/runtime/cpp/emboss_text_util.h
@@ -132,7 +132,11 @@ IntType base = 10; bool negative = false; unsigned offset = 0; - if (::std::is_signed<IntType>::value && text.size() >= 1 + offset && + // ::std::numeric_limits<>::is_signed is used rather than ::std::is_signed<> + // because the latter is false for extended integer types such as __int128_t + // under a strict -std=c++NN, which would prevent parsing negative values into + // those types. numeric_limits *is* specialized for them. + if (::std::numeric_limits<IntType>::is_signed && text.size() >= 1 + offset && text[offset] == '-') { negative = true; offset += 1;