r254992 - Explicitly permit undefined behavior in constant initializers for global

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 7 19:21:47 PST 2015


Author: rsmith
Date: Mon Dec  7 21:21:47 2015
New Revision: 254992

URL: http://llvm.org/viewvc/llvm-project?rev=254992&view=rev
Log:
Explicitly permit undefined behavior in constant initializers for global
variables in C, in the cases where we can constant-fold it to a value
regardless (such as floating-point division by zero and signed integer
overflow). Strictly enforcing this rule breaks too much code.

Modified:
    cfe/trunk/include/clang/AST/Expr.h
    cfe/trunk/lib/AST/Expr.cpp
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/test/CodeGen/complex-init-list.c
    cfe/trunk/test/PCH/floating-literal.c
    cfe/trunk/test/Sema/const-eval.c
    cfe/trunk/test/Sema/integer-overflow.c
    cfe/trunk/test/Sema/switch-1.c
    cfe/trunk/test/SemaCXX/enum.cpp

Modified: cfe/trunk/include/clang/AST/Expr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=254992&r1=254991&r2=254992&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Expr.h (original)
+++ cfe/trunk/include/clang/AST/Expr.h Mon Dec  7 21:21:47 2015
@@ -529,10 +529,15 @@ public:
 
   /// EvalStatus is a struct with detailed info about an evaluation in progress.
   struct EvalStatus {
-    /// HasSideEffects - Whether the evaluated expression has side effects.
+    /// \brief Whether the evaluated expression has side effects.
     /// For example, (f() && 0) can be folded, but it still has side effects.
     bool HasSideEffects;
 
+    /// \brief Whether the evaluation hit undefined behavior.
+    /// For example, 1.0 / 0.0 can be folded to Inf, but has undefined behavior.
+    /// Likewise, INT_MAX + 1 can be folded to INT_MIN, but has UB.
+    bool HasUndefinedBehavior;
+
     /// Diag - If this is non-null, it will be filled in with a stack of notes
     /// indicating why evaluation failed (or why it failed to produce a constant
     /// expression).
@@ -542,7 +547,8 @@ public:
     /// expression *is* a constant expression, no notes will be produced.
     SmallVectorImpl<PartialDiagnosticAt> *Diag;
 
-    EvalStatus() : HasSideEffects(false), Diag(nullptr) {}
+    EvalStatus()
+        : HasSideEffects(false), HasUndefinedBehavior(false), Diag(nullptr) {}
 
     // hasSideEffects - Return true if the evaluated expression has
     // side effects.
@@ -575,7 +581,12 @@ public:
   /// side-effects.
   bool EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx) const;
 
-  enum SideEffectsKind { SE_NoSideEffects, SE_AllowSideEffects };
+  enum SideEffectsKind {
+    SE_NoSideEffects,          ///< Strictly evaluate the expression.
+    SE_AllowUndefinedBehavior, ///< Allow UB that we can give a value, but not
+                               ///< arbitrary unmodeled side effects.
+    SE_AllowSideEffects        ///< Allow any unmodeled side effect.
+  };
 
   /// EvaluateAsInt - Return true if this is a constant which we can fold and
   /// convert to an integer, using any crazy technique that we want to.
@@ -584,7 +595,8 @@ public:
 
   /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
   /// constant folded without side-effects, but discard the result.
-  bool isEvaluatable(const ASTContext &Ctx) const;
+  bool isEvaluatable(const ASTContext &Ctx,
+                     SideEffectsKind AllowSideEffects = SE_NoSideEffects) const;
 
   /// HasSideEffects - This routine returns true for all those expressions
   /// which have any effect other than producing a value. Example is a function

Modified: cfe/trunk/lib/AST/Expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Expr.cpp?rev=254992&r1=254991&r2=254992&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Expr.cpp (original)
+++ cfe/trunk/lib/AST/Expr.cpp Mon Dec  7 21:21:47 2015
@@ -2891,7 +2891,10 @@ bool Expr::isConstantInitializer(ASTCont
     return cast<CXXDefaultInitExpr>(this)->getExpr()
       ->isConstantInitializer(Ctx, false, Culprit);
   }
-  if (isEvaluatable(Ctx))
+  // Allow certain forms of UB in constant initializers: signed integer
+  // overflow and floating-point division by zero. We'll give a warning on
+  // these, but they're common enough that we have to accept them.
+  if (isEvaluatable(Ctx, SE_AllowUndefinedBehavior))
     return true;
   if (Culprit)
     *Culprit = this;

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=254992&r1=254991&r2=254992&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Mon Dec  7 21:21:47 2015
@@ -715,6 +715,32 @@ namespace {
       return keepEvaluatingAfterSideEffect();
     }
 
+    /// Should we continue evaluation after encountering undefined behavior?
+    bool keepEvaluatingAfterUndefinedBehavior() {
+      switch (EvalMode) {
+      case EM_EvaluateForOverflow:
+      case EM_IgnoreSideEffects:
+      case EM_ConstantFold:
+      case EM_DesignatorFold:
+        return true;
+
+      case EM_PotentialConstantExpression:
+      case EM_PotentialConstantExpressionUnevaluated:
+      case EM_ConstantExpression:
+      case EM_ConstantExpressionUnevaluated:
+        return false;
+      }
+      llvm_unreachable("Missed EvalMode case");
+    }
+
+    /// Note that we hit something that was technically undefined behavior, but
+    /// that we can evaluate past it (such as signed overflow or floating-point
+    /// division by zero.)
+    bool noteUndefinedBehavior() {
+      EvalStatus.HasUndefinedBehavior = true;
+      return keepEvaluatingAfterUndefinedBehavior();
+    }
+
     /// Should we continue evaluation as much as possible after encountering a
     /// construct which can't be reduced to a value?
     bool keepEvaluatingAfterFailure() {
@@ -1549,7 +1575,7 @@ static bool HandleOverflow(EvalInfo &Inf
                            const T &SrcValue, QualType DestType) {
   Info.CCEDiag(E, diag::note_constexpr_overflow)
     << SrcValue << DestType;
-  return Info.noteSideEffect();
+  return Info.noteUndefinedBehavior();
 }
 
 static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E,
@@ -1818,8 +1844,7 @@ static bool handleFloatFloatBinOp(EvalIn
 
   if (LHS.isInfinity() || LHS.isNaN()) {
     Info.CCEDiag(E, diag::note_constexpr_float_arithmetic) << LHS.isNaN();
-    // Undefined behavior is a side-effect.
-    return Info.noteSideEffect();
+    return Info.noteUndefinedBehavior();
   }
   return true;
 }
@@ -8835,6 +8860,12 @@ bool Expr::EvaluateAsBooleanCondition(bo
          HandleConversionToBool(Scratch.Val, Result);
 }
 
+static bool hasUnacceptableSideEffect(Expr::EvalStatus &Result,
+                                      Expr::SideEffectsKind SEK) {
+  return (SEK < Expr::SE_AllowSideEffects && Result.HasSideEffects) ||
+         (SEK < Expr::SE_AllowUndefinedBehavior && Result.HasUndefinedBehavior);
+}
+
 bool Expr::EvaluateAsInt(APSInt &Result, const ASTContext &Ctx,
                          SideEffectsKind AllowSideEffects) const {
   if (!getType()->isIntegralOrEnumerationType())
@@ -8842,7 +8873,7 @@ bool Expr::EvaluateAsInt(APSInt &Result,
 
   EvalResult ExprResult;
   if (!EvaluateAsRValue(ExprResult, Ctx) || !ExprResult.Val.isInt() ||
-      (!AllowSideEffects && ExprResult.HasSideEffects))
+      hasUnacceptableSideEffect(ExprResult, AllowSideEffects))
     return false;
 
   Result = ExprResult.Val.getInt();
@@ -8905,9 +8936,10 @@ bool Expr::EvaluateAsInitializer(APValue
 
 /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
 /// constant folded, but discard the result.
-bool Expr::isEvaluatable(const ASTContext &Ctx) const {
+bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK) const {
   EvalResult Result;
-  return EvaluateAsRValue(Result, Ctx) && !Result.HasSideEffects;
+  return EvaluateAsRValue(Result, Ctx) &&
+         !hasUnacceptableSideEffect(Result, SEK);
 }
 
 APSInt Expr::EvaluateKnownConstInt(const ASTContext &Ctx,

Modified: cfe/trunk/test/CodeGen/complex-init-list.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/complex-init-list.c?rev=254992&r1=254991&r2=254992&view=diff
==============================================================================
--- cfe/trunk/test/CodeGen/complex-init-list.c (original)
+++ cfe/trunk/test/CodeGen/complex-init-list.c Mon Dec  7 21:21:47 2015
@@ -4,8 +4,8 @@
 // of a complex number individually using an initialization list.  (There is a
 // extensive description and test in test/Sema/complex-init-list.c.)
 
-_Complex float x = { 1.0f, -1.0f };
-// CHECK: @x = global { float, float } { float 1.000000e+00, float -1.000000e+00 }, align 4
+_Complex float x = { 1.0f, 1.0f/0.0f };
+// CHECK: @x = global { float, float } { float 1.000000e+00, float 0x7FF0000000000000 }, align 4
 
 _Complex float f(float x, float y) { _Complex float z = { x, y }; return z; }
 // CHECK-LABEL: define <2 x float> @f

Modified: cfe/trunk/test/PCH/floating-literal.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/floating-literal.c?rev=254992&r1=254991&r2=254992&view=diff
==============================================================================
--- cfe/trunk/test/PCH/floating-literal.c (original)
+++ cfe/trunk/test/PCH/floating-literal.c Mon Dec  7 21:21:47 2015
@@ -15,5 +15,5 @@ long double foo = 1.0E4000L;
 double bar = 1.0E300;
 // CHECK: double bar = 1.0000000000000001E+300;
 
-float wibble = 2.0E38;
-// CHECK: float wibble = 2.0E+38;
+float wibble = 1.0E40;
+// CHECK: float wibble = 1.0E+40;

Modified: cfe/trunk/test/Sema/const-eval.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/const-eval.c?rev=254992&r1=254991&r2=254992&view=diff
==============================================================================
--- cfe/trunk/test/Sema/const-eval.c (original)
+++ cfe/trunk/test/Sema/const-eval.c Mon Dec  7 21:21:47 2015
@@ -129,7 +129,7 @@ extern struct Test50S Test50;
 EVAL_EXPR(50, &Test50 < (struct Test50S*)((unsigned)&Test50 + 10)) // expected-error {{must have a constant size}}
 
 // <rdar://problem/11874571>
-EVAL_EXPR(51, 0 != (float)1e38)
+EVAL_EXPR(51, 0 != (float)1e99)
 
 // PR21945
 void PR21945() { int i = (({}), 0l); }

Modified: cfe/trunk/test/Sema/integer-overflow.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/integer-overflow.c?rev=254992&r1=254991&r2=254992&view=diff
==============================================================================
--- cfe/trunk/test/Sema/integer-overflow.c (original)
+++ cfe/trunk/test/Sema/integer-overflow.c Mon Dec  7 21:21:47 2015
@@ -7,7 +7,6 @@ uint64_t f1(uint64_t, uint32_t);
 uint64_t f2(uint64_t, ...);
 
 static const uint64_t overflow = 1 * 4608 * 1024 * 1024; // expected-warning {{overflow in expression; result is 536870912 with type 'int'}}
-// expected-error at -1 {{not a compile-time constant}}
 
 uint64_t check_integer_overflows(int i) {
 // expected-warning at +1 {{overflow in expression; result is 536870912 with type 'int'}}

Modified: cfe/trunk/test/Sema/switch-1.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/switch-1.c?rev=254992&r1=254991&r2=254992&view=diff
==============================================================================
--- cfe/trunk/test/Sema/switch-1.c (original)
+++ cfe/trunk/test/Sema/switch-1.c Mon Dec  7 21:21:47 2015
@@ -55,8 +55,5 @@ int f(int i) {
 
 // rdar://18405357
 unsigned long long l = 65536 * 65536; // expected-warning {{overflow in expression; result is 0 with type 'int'}}
-#ifndef __cplusplus
-// expected-error at -2 {{not a compile-time constant}}
-#endif
 unsigned long long l2 = 65536 * (unsigned)65536;
 unsigned long long l3 = 65536 * 65536ULL;

Modified: cfe/trunk/test/SemaCXX/enum.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/enum.cpp?rev=254992&r1=254991&r2=254992&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/enum.cpp (original)
+++ cfe/trunk/test/SemaCXX/enum.cpp Mon Dec  7 21:21:47 2015
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++98 -verify -triple x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++11 -verify -triple x86_64-apple-darwin %s
 enum E { // expected-note{{previous definition is here}}
   Val1,
   Val2
@@ -88,10 +89,24 @@ typedef enum { }; // expected-warning{{t
 
 // PR7921
 enum PR7921E {
-    PR7921V = (PR7921E)(123) // expected-error {{expression is not an integral constant expression}}
+    PR7921V = (PR7921E)(123)
+#if __cplusplus < 201103L
+// expected-error at -2 {{expression is not an integral constant expression}}
+#else
+// expected-error at -4 {{must have integral or unscoped enumeration type}}
+// FIXME: The above diagnostic isn't very good; we should instead complain about the type being incomplete.
+#endif
 };
 
 void PR8089() {
   enum E; // expected-error{{ISO C++ forbids forward references to 'enum' types}}
   int a = (E)3; // expected-error{{cannot initialize a variable of type 'int' with an rvalue of type 'E'}}
 }
+
+// This is accepted as a GNU extension. In C++98, there was no provision for
+// expressions with UB to be non-constant.
+enum { overflow = 123456 * 234567 };
+#if __cplusplus >= 201103L
+// expected-warning at -2 {{not an integral constant expression}}
+// expected-note at -3 {{value 28958703552 is outside the range of representable values}}
+#endif




More information about the cfe-commits mailing list