r239356 - Consider unsigned long for non-u/U decimal literals (C90/C++03)

Hubert Tong hubert.reinterpretcast at gmail.com
Mon Jun 8 14:59:59 PDT 2015


Author: hubert.reinterpretcast
Date: Mon Jun  8 16:59:59 2015
New Revision: 239356

URL: http://llvm.org/viewvc/llvm-project?rev=239356&view=rev
Log:
Consider unsigned long for non-u/U decimal literals (C90/C++03)

Summary:
This modifies Clang to reflect that under pre-C99 ISO C, decimal
constants may have type `unsigned long` even if they do not contain `u`
or `U` in their suffix (C90 subclause 6.1.3.2 paragraph 5). The same is
done for C++ without C++11 which--because of undefined behaviour--allows
for behaviour compatible with ISO C90 in the case of an unsuffixed
decimal literal and is otherwise identical to C90 in its treatment of
integer literals (C++03 subclause 2.13.1 [lex.icon] paragraph 2).

Messages are added to the `c99-compat` and `c++11-compat` groups to warn
on such literals, since they behave differently under the newer
standards.

Fixes PR 16678.

Test Plan:
A new test file is added to exercise both pre-C99/C++11 and C99/C++11-up
on decimal literals with no suffix or suffixes `l`/`L` for both 32-bit
and 64-bit `long`.

In the file, 2^31 (being `INT_MAX+1`) is tested for the expected type
using `__typeof__` and multiple declarations of the same entity. 2^63
is similarly tested when it is within the range of `unsigned long`.

Preprocessor arithmetic tests are added to ensure consistency given
that Clang (like GCC) uses greater than 32 bits for preprocessor
arithmetic even when `long` and `unsigned long` is 32 bits and a
pre-C99/C++11 mode is in effect.

Tests added:
  test/Sema/PR16678.c

Reviewers: fraggamuffin, rsmith

Reviewed By: rsmith

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D9794

Added:
    cfe/trunk/test/Sema/PR16678.c
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/lib/Sema/SemaExpr.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td?rev=239356&r1=239355&r2=239356&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td Mon Jun  8 16:59:59 2015
@@ -115,7 +115,23 @@ def err_integer_literal_too_large : Erro
 def ext_integer_literal_too_large_for_signed : ExtWarn<
   "integer literal is too large to be represented in a signed integer type, "
   "interpreting as unsigned">,
-  InGroup<DiagGroup<"implicitly-unsigned-literal">>;
+  InGroup<ImplicitlyUnsignedLiteral>;
+def warn_old_implicitly_unsigned_long : Warning<
+  "integer literal is too large to be represented in type 'long', "
+  "interpreting as 'unsigned long' per C89; this literal will "
+  "%select{have type 'long long'|be ill-formed}0 in C99 onwards">,
+  InGroup<C99Compat>;
+def warn_old_implicitly_unsigned_long_cxx : Warning<
+  "integer literal is too large to be represented in type 'long', "
+  "interpreting as 'unsigned long' per C++98; this literal will "
+  "%select{have type 'long long'|be ill-formed}0 in C++11 onwards">,
+  InGroup<CXX11Compat>;
+def ext_old_implicitly_unsigned_long_cxx : ExtWarn<
+  "integer literal is too large to be represented in type 'long' and is "
+  "subject to undefined behavior under C++98, interpreting as 'unsigned long'; "
+  "this literal will %select{have type 'long long'|be ill-formed}0 "
+  "in C++11 onwards">,
+  InGroup<CXX11Compat>;
 
 // SEH
 def err_seh_expected_handler : Error<

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=239356&r1=239355&r2=239356&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Mon Jun  8 16:59:59 2015
@@ -239,6 +239,7 @@ def MultiChar : DiagGroup<"multichar">;
 def : DiagGroup<"nested-externs">;
 def CXX11LongLong : DiagGroup<"c++11-long-long">;
 def LongLong : DiagGroup<"long-long", [CXX11LongLong]>;
+def ImplicitlyUnsignedLiteral : DiagGroup<"implicitly-unsigned-literal">;
 def MethodSignatures : DiagGroup<"method-signatures">;
 def MismatchedParameterTypes : DiagGroup<"mismatched-parameter-types">;
 def MismatchedReturnTypes : DiagGroup<"mismatched-return-types">;

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=239356&r1=239355&r2=239356&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Mon Jun  8 16:59:59 2015
@@ -3425,6 +3425,22 @@ ExprResult Sema::ActOnNumericConstant(co
             Ty = Context.LongTy;
           else if (AllowUnsigned)
             Ty = Context.UnsignedLongTy;
+          // Check according to the rules of C90 6.1.3.2p5. C++03 [lex.icon]p2
+          // is compatible.
+          else if (!getLangOpts().C99 && !getLangOpts().CPlusPlus11) {
+            const unsigned LongLongSize =
+                Context.getTargetInfo().getLongLongWidth();
+            Diag(Tok.getLocation(),
+                 getLangOpts().CPlusPlus
+                     ? Literal.isLong
+                           ? diag::warn_old_implicitly_unsigned_long_cxx
+                           : /*C++98 UB*/ diag::
+                                 ext_old_implicitly_unsigned_long_cxx
+                     : diag::warn_old_implicitly_unsigned_long)
+                << (LongLongSize > LongSize ? /*will have type 'long long'*/ 0
+                                            : /*will be ill-formed*/ 1);
+            Ty = Context.UnsignedLongTy;
+          }
           Width = LongSize;
         }
       }

Added: cfe/trunk/test/Sema/PR16678.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/PR16678.c?rev=239356&view=auto
==============================================================================
--- cfe/trunk/test/Sema/PR16678.c (added)
+++ cfe/trunk/test/Sema/PR16678.c Mon Jun  8 16:59:59 2015
@@ -0,0 +1,153 @@
+// RUN: %clang_cc1 -DX32TYPE=ULONG -triple powerpc-unknown-linux-gnu -std=c89 -x c %s -verify
+// RUN: %clang_cc1 -DX32TYPE=ULONG -triple powerpc-unknown-linux-gnu -std=iso9899:199409 -x c %s -verify
+// RUN: %clang_cc1 -DX32TYPE=ULONG -triple powerpc-unknown-linux-gnu -std=c++98 -x c++ %s -verify
+// RUN: %clang_cc1 -DX32TYPE=LLONG -triple powerpc-unknown-linux-gnu -std=c99 -x c %s -verify
+// RUN: %clang_cc1 -DX32TYPE=LLONG -triple powerpc-unknown-linux-gnu -std=c11 -x c %s -verify
+// RUN: %clang_cc1 -DX32TYPE=LLONG -triple powerpc-unknown-linux-gnu -std=c++11 -x c++ %s -verify
+// RUN: %clang_cc1 -DX32TYPE=LLONG -triple powerpc-unknown-linux-gnu -std=c++1y -x c++ %s -verify
+// RUN: %clang_cc1 -DX32TYPE=LLONG -triple powerpc-unknown-linux-gnu -std=c++1z -x c++ %s -verify
+// RUN: %clang_cc1 -DX64TYPE=ULONG -triple powerpc64-unknown-linux-gnu -std=c89 -x c %s -verify
+// RUN: %clang_cc1 -DX64TYPE=ULONG -triple powerpc64-unknown-linux-gnu -std=iso9899:199409 -x c %s -verify
+// RUN: %clang_cc1 -DX64TYPE=ULONG -triple powerpc64-unknown-linux-gnu -std=c++98 -x c++ %s -verify
+// RUN: %clang_cc1 -DX64TYPE=ULLONG -triple powerpc64-unknown-linux-gnu -std=c99 -x c %s -verify
+// RUN: %clang_cc1 -DX64TYPE=ULLONG -triple powerpc64-unknown-linux-gnu -std=c11 -x c %s -verify
+// RUN: %clang_cc1 -DX64TYPE=ULLONG -triple powerpc64-unknown-linux-gnu -std=c++11 -x c++ %s -verify
+// RUN: %clang_cc1 -DX64TYPE=ULLONG -triple powerpc64-unknown-linux-gnu -std=c++1y -x c++ %s -verify
+// RUN: %clang_cc1 -DX64TYPE=ULLONG -triple powerpc64-unknown-linux-gnu -std=c++1z -x c++ %s -verify
+
+#ifdef X64TYPE
+#define X32TYPE long
+#endif
+
+#define IS_ULONG_ULONG 1
+#define IS_ULONG2(X) IS_ULONG_##X
+#define IS_ULONG(X) IS_ULONG2(X)
+
+#if !defined(X64TYPE) && !IS_ULONG(X32TYPE)
+// expected-no-diagnostics
+#endif
+
+typedef unsigned long ULONG;
+typedef long long LLONG;
+typedef unsigned long long ULLONG;
+
+
+/******************************************************************************
+ * Test 2^31 as a decimal literal with no suffix and with the "l" and "L" cases.
+ ******************************************************************************/
+extern X32TYPE x32;
+extern __typeof__(2147483648) x32;
+extern __typeof__(2147483648l) x32;
+extern __typeof__(2147483648L) x32;
+
+#if IS_ULONG(X32TYPE)
+#if !__cplusplus
+
+/******************************************************************************
+ * Under pre-C99 ISO C, unsigned long is attempted for decimal integer literals
+ * that do not have a suffix containing "u" or "U" if the literal does not fit
+ * within the range of int or long. See 6.1.3.2 paragraph 5.
+ ******************************************************************************/
+// expected-warning at 39 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C89; this literal will have type 'long long' in C99 onwards}}
+// expected-warning at 40 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C89; this literal will have type 'long long' in C99 onwards}}
+// expected-warning at 41 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C89; this literal will have type 'long long' in C99 onwards}}
+#else
+
+/******************************************************************************
+ * Under pre-C++11 ISO C++, the same holds if the literal contains an "l" or "L"
+ * in its suffix; otherwise, the behavior is undefined. See 2.13.1 [lex.icon]
+ * paragraph 2.
+ ******************************************************************************/
+// expected-warning at 39 {{integer literal is too large to be represented in type 'long' and is subject to undefined behavior under C++98, interpreting as 'unsigned long'; this literal will have type 'long long' in C++11 onwards}}
+// expected-warning at 40 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C++98; this literal will have type 'long long' in C++11 onwards}}
+// expected-warning at 41 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C++98; this literal will have type 'long long' in C++11 onwards}}
+#endif
+#endif
+
+
+#ifdef X64TYPE
+
+/******************************************************************************
+ * Test 2^63 as a decimal literal with no suffix and with the "l" and "L" cases.
+ ******************************************************************************/
+extern X64TYPE x64;
+extern __typeof__(9223372036854775808) x64;
+extern __typeof__(9223372036854775808l) x64;
+extern __typeof__(9223372036854775808L) x64;
+
+#if IS_ULONG(X64TYPE)
+
+#if !__cplusplus
+
+/******************************************************************************
+ * Under pre-C99 ISO C, unsigned long is attempted for decimal integer literals
+ * that do not have a suffix containing "u" or "U" if the literal does not fit
+ * within the range of int or long. See 6.1.3.2 paragraph 5.
+ ******************************************************************************/
+// expected-warning at 74 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C89; this literal will be ill-formed in C99 onwards}}
+// expected-warning at 75 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C89; this literal will be ill-formed in C99 onwards}}
+// expected-warning at 76 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C89; this literal will be ill-formed in C99 onwards}}
+#else
+
+/******************************************************************************
+ * Under pre-C++11 ISO C++, the same holds if the literal contains an "l" or "L"
+ * in its suffix; otherwise, the behavior is undefined. See 2.13.1 [lex.icon]
+ * paragraph 2.
+ ******************************************************************************/
+// expected-warning at 74 {{integer literal is too large to be represented in type 'long' and is subject to undefined behavior under C++98, interpreting as 'unsigned long'; this literal will be ill-formed in C++11 onwards}}
+// expected-warning at 75 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C++98; this literal will be ill-formed in C++11 onwards}}
+// expected-warning at 76 {{integer literal is too large to be represented in type 'long', interpreting as 'unsigned long' per C++98; this literal will be ill-formed in C++11 onwards}}
+#endif
+#else
+
+/******************************************************************************
+ * The status quo in C99/C++11-and-later modes for the literals in question is
+ * to interpret them as unsigned as an extension.
+ ******************************************************************************/
+// expected-warning at 74 {{integer literal is too large to be represented in a signed integer type, interpreting as unsigned}}
+// expected-warning at 75 {{integer literal is too large to be represented in a signed integer type, interpreting as unsigned}}
+// expected-warning at 76 {{integer literal is too large to be represented in a signed integer type, interpreting as unsigned}}
+#endif
+#endif
+
+
+/******************************************************************************
+ * Test preprocessor arithmetic with 2^31 as a decimal literal with no suffix
+ * and with the "l" and "L" cases.
+ ******************************************************************************/
+#if !IS_ULONG(X32TYPE)
+
+/******************************************************************************
+ * If the literal is signed without need for the modified range of the signed
+ * integer types within the controlling constant expression for conditional
+ * inclusion, then it will also be signed with said modified range.
+ ******************************************************************************/
+#define EXPR(X) ((X - X) - 1 < 0)
+#else
+
+/******************************************************************************
+ * Strictly speaking, in pre-C99/C++11 ISO C/C++, the preprocessor arithmetic is
+ * evaluated with the range of long/unsigned long; however, both Clang and GCC
+ * evaluate using 64-bits even when long/unsigned long are 32-bits outside of
+ * preprocessing.
+ *
+ * If the range used becomes 32-bits, then this test will enforce the treatment
+ * as unsigned of the literals in question.
+ *
+ * Note:
+ * Under pre-C99/C++11 ISO C/C++, whether the interpretation of the literal is
+ * affected by the modified range of the signed and unsigned integer types
+ * within the controlling constant expression for conditional inclusion is
+ * unclear.
+ ******************************************************************************/
+#define PP_LONG_MAX ((0ul - 1ul) >> 1)
+#define EXPR(X)                                                                \
+  (PP_LONG_MAX >= 0x80000000 || (X - X) - 1 > 0) // either 2^31 fits into a
+                                                 // preprocessor "long" or the
+                                                 // literals in question are
+                                                 // unsigned
+#endif
+
+#if !(EXPR(2147483648) && EXPR(2147483648l) && EXPR(2147483648L))
+#error Unexpected signedness or conversion behavior
+#endif





More information about the cfe-commits mailing list