Address review feedback: update docs and clarify comments for 128-bit support - Document the new 128-bit UInt/Int limits (and the platform-support caveat) in the language reference, and note the unchanged 64-bit limit on bits types, including anonymous bits. - Move the bits-size note out of testdata/int128_sizes.emb into the docs. - Reword test comments (drop "now"; -(2**120) instead of ambiguous -2**120) and use a hex literal in the text-format test so the value is easy to relate to the expected shifted constant. - Explain the LeastWidthInteger rounding-up recursion in a comment and make the no-int128 static_assert message say why the width is unsupported.
diff --git a/compiler/back_end/cpp/testcode/int128_sizes_test.cc b/compiler/back_end/cpp/testcode/int128_sizes_test.cc index 33091fd..3e18f87 100644 --- a/compiler/back_end/cpp/testcode/int128_sizes_test.cc +++ b/compiler/back_end/cpp/testcode/int128_sizes_test.cc
@@ -385,10 +385,11 @@ 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. + // A large negative 128-bit value, -(2**120), does not fit in 64 bits. (The + // WriteToString round-trip below covers the equivalent decimal text, + // -1329227995784915872903807060280344576.) EXPECT_TRUE(::emboss::UpdateFromText( - writer, "{sixteen_byte: -1329227995784915872903807060280344576}")); + writer, "{sixteen_byte: -0x1000000000000000000000000000000}")); EXPECT_EQ(-(static_cast<__int128_t>(1) << 120), writer.sixteen_byte().Read()); // Round-trip through WriteToString and back.
diff --git a/compiler/front_end/constraints_test.py b/compiler/front_end/constraints_test.py index eaba409..16385cb 100644 --- a/compiler/front_end/constraints_test.py +++ b/compiler/front_end/constraints_test.py
@@ -235,7 +235,7 @@ ) def test_bits_field_too_big_for_type(self): - # UInt now supports up to 128 bits, so test with 129 bits (17 bytes) + # UInt supports up to 128 bits, so test with 129 bits (17 bytes) ir = _make_ir_from_emb( "struct Foo:\n" " 0 [+17] UInt uint136\n" @@ -468,7 +468,7 @@ ) def test_explicit_size_too_big(self): - # UInt now supports up to 128 bits, so test with 256 bits (32 bytes) + # UInt supports up to 128 bits, so test with 256 bits (32 bytes) # to ensure we get the "Requirements of UInt not met" error ir = _make_ir_from_emb( "struct Foo:\n"
diff --git a/doc/language-reference.md b/doc/language-reference.md index db8682e..5b28e40 100644 --- a/doc/language-reference.md +++ b/doc/language-reference.md
@@ -873,6 +873,10 @@ as other `bits`, `UInt`, `Bcd`, `Flag`). Byte-oriented types, such as `struct`s, may not be embedded in a `bits`. +A `bits` must be fixed size, and may be no more than 64 bits in total size. +Note that this limit is smaller than the 128-bit maximum size of a `UInt` or +`Int` field in a `struct`. + For example: ``` @@ -950,6 +954,11 @@ In this case, the fields of the `bits` will be treated as though they are fields of the outer struct. +Like any other `bits`, an anonymous `bits` is limited to 64 bits in total size, +even though the enclosing `struct` may contain integer fields of up to 128 +bits. Supporting larger `bits` would require additional implementation work in +the compiler and runtime. + #### Inline `bits` @@ -1078,17 +1087,29 @@ ### `UInt` -A `UInt` is an unsigned integer. `UInt` can be anywhere from 1 to 64 bits in +A `UInt` is an unsigned integer. `UInt` can be anywhere from 1 to 128 bits in size, and may be used both in `struct`s and in `bits`. `UInt` fields may be referenced in integer expressions. +`UInt`s larger than 64 bits require 128-bit integer support from the target +platform: for the C++ backend, a compiler that provides `__uint128_t` (detected +via `EMBOSS_HAS_INT128`). On platforms without 128-bit integer support, +generated code for fields larger than 64 bits will fail to compile. Since +`bits` types are limited to 64 bits in total size, `UInt`s larger than 64 bits +can only be used in `struct`s. `UInt` fields larger than 64 bits cannot be +referenced in integer expressions. + ### `Int` An `Int` is a signed two's-complement integer. `Int` can be anywhere from 1 to -64 bits in size, and may be used both in `struct`s and in `bits`. `Int` fields +128 bits in size, and may be used both in `struct`s and in `bits`. `Int` fields may be referenced in integer expressions. +Like `UInt`, `Int`s larger than 64 bits require 128-bit integer support from +the target platform (for the C++ backend, `__int128_t`), can only be used in +`struct`s, and cannot be referenced in integer expressions. + ### `Bcd`
diff --git a/runtime/cpp/emboss_cpp_types.h b/runtime/cpp/emboss_cpp_types.h index 1ef0b58..a9dd9f7 100644 --- a/runtime/cpp/emboss_cpp_types.h +++ b/runtime/cpp/emboss_cpp_types.h
@@ -53,6 +53,14 @@ // LeastWidthInteger<n_bits>::Unsigned is the smallest uintNN_t type that can // hold n_bits or more. LeastWidthInteger<n_bits>::Signed is the corresponding // signed type. +// +// The exact-width specializations below always take precedence, so the +// unspecialized template is only ever instantiated for widths with no +// exact-width integer type. It recursively "rounds up" such widths to the +// next supported width: for example, LeastWidthInteger<12>::Unsigned +// instantiates LeastWidthInteger<13> through LeastWidthInteger<16>, yielding +// ::std::uint16_t. The static_assert bounds the recursion for widths above +// the maximum supported width. #if EMBOSS_HAS_INT128 template <int kBits> struct LeastWidthInteger final { @@ -68,7 +76,9 @@ #else template <int kBits> struct LeastWidthInteger final { - static_assert(kBits <= 64, "Only bit sizes up to 64 are supported."); + static_assert(kBits <= 64, + "Bit widths over 64 require 128-bit integer support, which is " + "not available on this platform (EMBOSS_HAS_INT128 is 0)."); using Unsigned = typename LeastWidthInteger<kBits + 1>::Unsigned; using Signed = typename LeastWidthInteger<kBits + 1>::Signed; };
diff --git a/testdata/int128_sizes.emb b/testdata/int128_sizes.emb index 692df0b..5bb0b04 100644 --- a/testdata/int128_sizes.emb +++ b/testdata/int128_sizes.emb
@@ -78,8 +78,3 @@ 110 [+28] UInt:112[2] fourteen_byte 138 [+30] UInt:120[2] fifteen_byte 168 [+32] UInt:128[2] sixteen_byte - - -# Note: Anonymous `bits` types are still constrained to 64 bits maximum. -# Supporting 128-bit `bits` types would require additional implementation -# work in the OffsetBitBlock class and the constraints checker.