Skip to content

[C23] No longer assert on huge enumerator values #81760

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 20, 2024

Conversation

AaronBallman
Copy link
Collaborator

C23 added the wb and uwb suffixes to generate a bit-precise integer value. These values can be larger than what is representable in intmax_t or uintmax_t.

We were asserting that an enumerator constant could not have a value larger than unsigned long long but that's now a possibility. This patch turns the assertion into a "value too large" diagnostic.

Note, we do not yet implement WG14 N3029 and so the behavior of this patch will cause the enumerator to be cast to unsigned long long, but this behavior may change in the future. GCC selects __uint128_t as the underlying type for such an enumeration and we may want to match that behavior in the future. This patch has several FIXME comments related to this and the release notes call out the possibility of a change in behavior in the future.

Fixes #69352

C23 added the wb and uwb suffixes to generate a bit-precise integer
value. These values can be larger than what is representable in
intmax_t or uintmax_t.

We were asserting that an enumerator constant could not have a value
larger than unsigned long long but that's now a possibility. This patch
turns the assertion into a "value too large" diagnostic.

Note, we do not yet implement WG14 N3029 and so the behavior of this
patch will cause the enumerator to be cast to unsigned long long, but
this behavior may change in the future. GCC selects __uint128_t as the
underlying type for such an enumeration and we may want to match that
behavior in the future. This patch has several FIXME comments related
to this and the release notes call out the possibility of a change in
behavior in the future.

Fixes llvm#69352
@AaronBallman AaronBallman added clang Clang issues not falling into any other category c23 clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Feb 14, 2024
@llvmbot
Copy link
Member

llvmbot commented Feb 14, 2024

@llvm/pr-subscribers-clang

Author: Aaron Ballman (AaronBallman)

Changes

C23 added the wb and uwb suffixes to generate a bit-precise integer value. These values can be larger than what is representable in intmax_t or uintmax_t.

We were asserting that an enumerator constant could not have a value larger than unsigned long long but that's now a possibility. This patch turns the assertion into a "value too large" diagnostic.

Note, we do not yet implement WG14 N3029 and so the behavior of this patch will cause the enumerator to be cast to unsigned long long, but this behavior may change in the future. GCC selects __uint128_t as the underlying type for such an enumeration and we may want to match that behavior in the future. This patch has several FIXME comments related to this and the release notes call out the possibility of a change in behavior in the future.

Fixes #69352


Full diff: https://github.com/llvm/llvm-project/pull/81760.diff

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+8)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+7-2)
  • (modified) clang/test/Sema/enum.c (+23)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6cf48d63dd512e..31fd161c65fc32 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -169,6 +169,14 @@ Improvements to Clang's diagnostics
 
 - Clang now diagnoses friend declarations with an ``enum`` elaborated-type-specifier in language modes after C++98.
 
+- Now diagnoses an enumeration constant whose value is larger than can be
+  represented by ``unsigned long long``, which can happen with a large constant
+  using the ``wb`` or ``uwb`` suffix. The maximal underlying type is currently
+  ``unsigned long long``, but this behavior may change in the future when Clang
+  implements
+  `WG14 N3029 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3029.htm>`_.
+  Fixes `#69352 <https://github.com/llvm/llvm-project/issues/69352>`_.
+
 Improvements to Clang's time-trace
 ----------------------------------
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 09a35fddba1954..52cb2c838c6e47 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -20317,8 +20317,13 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange,
                            ? Context.UnsignedLongTy : Context.LongTy;
     } else {
       BestWidth = Context.getTargetInfo().getLongLongWidth();
-      assert(NumPositiveBits <= BestWidth &&
-             "How could an initializer get larger than ULL?");
+      if (NumPositiveBits > BestWidth) {
+        // This can happen with bit-precise integer types, but those are not
+        // allowed as the type for an enumerator per C23 6.7.2.2p4 and p12.
+        // FIXME: GCC uses __int128_t and __uint128_t for cases that fit within
+        // a 128-bit integer, we should consider doing the same.
+        Diag(Enum->getLocation(), diag::ext_enum_too_large);
+      }
       BestType = Context.UnsignedLongLongTy;
       BestPromotionType
         = (NumPositiveBits == BestWidth || !getLangOpts().CPlusPlus)
diff --git a/clang/test/Sema/enum.c b/clang/test/Sema/enum.c
index f8e380bd62d1ea..c5255dfdf16faf 100644
--- a/clang/test/Sema/enum.c
+++ b/clang/test/Sema/enum.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -triple %itanium_abi_triple %s -fsyntax-only -verify -pedantic
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -fsyntax-only -std=c23 -verify -pedantic
 enum e {A,
         B = 42LL << 32,        // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
       C = -4, D = 12456 };
@@ -167,3 +168,25 @@ enum struct GH42372_1 { // expected-error {{expected identifier or '{'}}
 enum class GH42372_2 {
   One
 };
+
+#if __STDC_VERSION__ >= 202311L
+// FIXME: GCC picks __uint128_t as the underlying type for the enumeration
+// value and Clang picks unsigned long long.
+// FIXME: Clang does not yet implement WG14 N3029, so the warning about
+// restricting enumerator values to 'int' is not correct.
+enum GH59352 { // expected-warning {{enumeration values exceed range of largest integer}}
+ BigVal = 66666666666666666666wb // expected-warning {{ISO C restricts enumerator values to range of 'int' (66666666666666666666 is too large)}}
+};
+_Static_assert(BigVal == 66666666666666666666wb); /* expected-error {{static assertion failed due to requirement 'BigVal == 66666666666666666666wb'}}
+                                                     expected-note {{expression evaluates to '11326434445538011818 == 66666666666666666666'}}
+                                                   */
+_Static_assert(
+    _Generic(BigVal,                             // expected-error {{static assertion failed}}
+    _BitInt(67) : 0,
+    __INTMAX_TYPE__ : 0,
+    __UINTMAX_TYPE__ : 0,
+    __int128_t : 0,
+    __uint128_t : 1
+    )
+);
+#endif // __STDC_VERSION__ >= 202311L

This adds an explicit triple to ensure we don't run into problems with
intmax_t and long long being the same type (etc).
Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Note you can get a similar crash using enum x { X = (__uint128_t)(1<<64) };. I'm a little surprised we haven't run into this before C23.

@AaronBallman AaronBallman merged commit 3b7ba24 into llvm:main Feb 20, 2024
@AaronBallman AaronBallman deleted the aballman-huge-bitint-enum-values branch February 20, 2024 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c23 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

clang: Assertion `NumPositiveBits <= BestWidth && "How could an initializer get larger than ULL?"' failed.
3 participants