[PATCH] D130058: [Clang] Diagnose ill-formed constant expression when setting a non fixed enum to a value outside the range of the enumeration values

Shafik Yaghmour via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 18 21:10:12 PDT 2022


shafik created this revision.
shafik added reviewers: aaron.ballman, erichkeane.
Herald added a project: All.
shafik requested review of this revision.

DR2338 <https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2338> clarified that it was undefined behavior to set the value outside the range of the enumerations values for an enum without a fixed underlying type.

We should diagnose this with a constant expression context. This PR adds this diagnosis for C++20 since the DR status was CD5 and a test to verify the behavior.


https://reviews.llvm.org/D130058

Files:
  clang/include/clang/Basic/DiagnosticASTKinds.td
  clang/lib/AST/ExprConstant.cpp
  clang/test/SemaCXX/constant-expression-cxx2a.cpp


Index: clang/test/SemaCXX/constant-expression-cxx2a.cpp
===================================================================
--- clang/test/SemaCXX/constant-expression-cxx2a.cpp
+++ clang/test/SemaCXX/constant-expression-cxx2a.cpp
@@ -1473,3 +1473,10 @@
   }
   static_assert(g()); // expected-error {{constant expression}} expected-note {{in call}}
 }
+
+enum E { e1=-4, e2=4};
+void testValueInRangeOfEnumerationValues() {
+  constexpr E x1 = static_cast<E>(8); // expected-error {{must be initialized by a constant expression}}
+  // expected-note at -1 {{store of value outside of the range of unscoped enum, valid values -8 to 7}}
+  constexpr E x2 = static_cast<E>(-8);
+}
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -13509,6 +13509,46 @@
       return Info.Ctx.getTypeSize(DestType) == Info.Ctx.getTypeSize(SrcType);
     }
 
+    if (Info.Ctx.getLangOpts().CPlusPlus20)
+      if (const EnumType *ET = dyn_cast<EnumType>(DestType)) {
+        const EnumDecl *ED = ET->getDecl();
+        // Check that the value is within the range of the enumeration values.
+        //
+        // This corressponds to [expr.static.cast]p10 which says:
+        // A value of integral or enumeration type can be explicitly converted
+        // to a complete enumeration type ... If the enumeration type does not
+        // have a fixed underlying type, the value is unchanged if the original
+        // value is within the range of the enumeration values ([dcl.enum]), and
+        // otherwise, the behavior is undefined.
+        //
+        // This was resolved as part of DR2338 which has CD5 status.
+        if (!ED->isFixed()) {
+          llvm::APInt Min;
+          llvm::APInt End;
+
+          unsigned Bitwidth = Info.Ctx.getIntWidth(DestType);
+          unsigned NumNegativeBits = ED->getNumNegativeBits();
+          unsigned NumPositiveBits = ED->getNumPositiveBits();
+
+          if (NumNegativeBits) {
+            unsigned NumBits = std::max(NumNegativeBits, NumPositiveBits + 1);
+            End = llvm::APInt(Bitwidth, 1) << (NumBits - 1);
+            Min = -End;
+          } else {
+            End = llvm::APInt(Bitwidth, 1) << NumPositiveBits;
+            Min = llvm::APInt::getZero(Bitwidth);
+          }
+
+          if (NumNegativeBits &&
+              (End.sle(Result.getInt()) || Min.sgt(Result.getInt())))
+            CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range)
+                << Min.getSExtValue() << (End.getSExtValue() - 1);
+          else if (!NumNegativeBits && End.ule(Result.getInt()))
+            CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range)
+                << Min.getZExtValue() << (End.getZExtValue() - 1);
+        }
+      }
+
     return Success(HandleIntToIntCast(Info, E, DestType, SrcType,
                                       Result.getInt()), E);
   }
Index: clang/include/clang/Basic/DiagnosticASTKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticASTKinds.td
+++ clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -366,6 +366,8 @@
   "type %0 has unexpected layout">;
 def note_constexpr_unsupported_flexible_array : Note<
   "flexible array initialization is not yet supported">;
+def note_constexpr_unscoped_enum_out_of_range : Note<
+  "store of value outside of the range of unscoped enum, valid values %0 to %1">;
 def err_experimental_clang_interp_failed : Error<
   "the experimental clang interpreter failed to evaluate an expression">;
 


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D130058.445688.patch
Type: text/x-patch
Size: 3636 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20220719/01d25c90/attachment-0001.bin>


More information about the cfe-commits mailing list