jyknight created this revision.
jyknight added a reviewer: rsmith.

And, configure it to explicitly request equivalent behavior to
ConstantFold, instead of getting that behavior accidentally.

While it seems that diagnose_if and enable_if were intended to require
a constexpr argument, the code was actually only checking whether it
could be constant-folded, due to the confusing way the constant
evaluator code currently works.

It _probably_ should be fixed to actually check for
constexpr-ness. However, r290584 now depends on it NOT doing so -- and
it's possible that some users are, too.

I'd like to land this, to unblock the follow-on cleanup -- without
changing the semantics first -- and maybe loop back to convert it to
require a constexpr later, if it looks like users aren't depending on
the semantics it has now.

This change is a prerequisite for fixing the confusion in the
ExprConstant code which led to this situation.


https://reviews.llvm.org/D52926

Files:
  clang/lib/AST/ExprConstant.cpp
  clang/test/Sema/enable_if.c

Index: clang/test/Sema/enable_if.c
===================================================================
--- clang/test/Sema/enable_if.c
+++ clang/test/Sema/enable_if.c
@@ -170,4 +170,24 @@
   variadic_enable_if(0, m); // expected-error{{no matching}}
   variadic_enable_if(0, m, 3); // expected-error{{no matching}}
 }
+
+// Test of the current weird semantics of enable_if (and diagnose_if).
+//
+// There's two kinds of checking of the expression going on:
+//  1. Whether it could potentially be a constexpr -- without access to the
+//     parameters.
+//  2. Checking if the condition evaluates to true with concrete
+//     arguments. While #1 requires that it could potentially be a constexpr,
+//     this step does not require that it actually _IS_ a constexpr, only that
+//     it's evaluable! (Note: this looseness might be unintended)
+
+void non_constexpr() __attribute__((enable_if((int*)(char*)0 == 0, ""))); // expected-error{{'enable_if' attribute expression never produces a constant expression}} expected-note{{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
+void non_constexpr_call() {
+    non_constexpr();
+}
+
+void non_constexpr_conditional(long x) __attribute__((enable_if(x?1: (int*)(char*)0 == 0, "")));
+void non_constexpr_conditional_call() {
+    non_constexpr_conditional(0);
+}
 #endif
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -744,12 +744,11 @@
       /// can't be modeled.
       EM_IgnoreSideEffects,
 
-      /// Evaluate as a constant expression. Stop if we find that the expression
-      /// is not a constant expression. Some expressions can be retried in the
+      /// Evaluate as a constant fold. Some expressions can be retried in the
       /// optimizer if we don't constant fold them here, but in an unevaluated
-      /// context we try to fold them immediately since the optimizer never
-      /// gets a chance to look at it.
-      EM_ConstantExpressionUnevaluated,
+      /// context we try to fold them immediately since the optimizer never gets
+      /// a chance to look at it.
+      EM_ConstantFoldUnevaluated,
 
       /// Evaluate as a potential constant expression. Keep going if we hit a
       /// construct that we can't evaluate yet (because we don't yet know the
@@ -854,13 +853,13 @@
           case EM_ConstantFold:
           case EM_IgnoreSideEffects:
           case EM_EvaluateForOverflow:
+          case EM_ConstantFoldUnevaluated:
             if (!HasFoldFailureDiagnostic)
               break;
             // We've already failed to fold something. Keep that diagnostic.
             LLVM_FALLTHROUGH;
           case EM_ConstantExpression:
           case EM_PotentialConstantExpression:
-          case EM_ConstantExpressionUnevaluated:
           case EM_PotentialConstantExpressionUnevaluated:
             HasActiveDiagnostic = false;
             return OptionalDiagnostic();
@@ -951,7 +950,7 @@
         return true;
 
       case EM_ConstantExpression:
-      case EM_ConstantExpressionUnevaluated:
+      case EM_ConstantFoldUnevaluated:
       case EM_ConstantFold:
         return false;
       }
@@ -971,12 +970,12 @@
       case EM_EvaluateForOverflow:
       case EM_IgnoreSideEffects:
       case EM_ConstantFold:
+      case EM_ConstantFoldUnevaluated:
         return true;
 
       case EM_PotentialConstantExpression:
       case EM_PotentialConstantExpressionUnevaluated:
       case EM_ConstantExpression:
-      case EM_ConstantExpressionUnevaluated:
         return false;
       }
       llvm_unreachable("Missed EvalMode case");
@@ -1003,7 +1002,7 @@
         return true;
 
       case EM_ConstantExpression:
-      case EM_ConstantExpressionUnevaluated:
+      case EM_ConstantFoldUnevaluated:
       case EM_ConstantFold:
       case EM_IgnoreSideEffects:
         return false;
@@ -1063,9 +1062,7 @@
                         Info.EvalStatus.Diag->empty() &&
                         !Info.EvalStatus.HasSideEffects),
         OldMode(Info.EvalMode) {
-      if (Enabled &&
-          (Info.EvalMode == EvalInfo::EM_ConstantExpression ||
-           Info.EvalMode == EvalInfo::EM_ConstantExpressionUnevaluated))
+      if (Enabled && Info.EvalMode == EvalInfo::EM_ConstantExpression)
         Info.EvalMode = EvalInfo::EM_ConstantFold;
     }
     void keepDiagnostics() { Enabled = false; }
@@ -8103,7 +8100,7 @@
     case EvalInfo::EM_IgnoreSideEffects:
       // Leave it to IR generation.
       return Error(E);
-    case EvalInfo::EM_ConstantExpressionUnevaluated:
+    case EvalInfo::EM_ConstantFoldUnevaluated:
     case EvalInfo::EM_PotentialConstantExpressionUnevaluated:
       // Reduce it to a constant now.
       return Success((Type & 2) ? 0 : -1, E);
@@ -11388,7 +11385,7 @@
                                     ArrayRef<const Expr*> Args,
                                     const Expr *This) const {
   Expr::EvalStatus Status;
-  EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpressionUnevaluated);
+  EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFoldUnevaluated);
 
   LValue ThisVal;
   const LValue *ThisPtr = nullptr;
@@ -11400,7 +11397,7 @@
 #endif
     if (EvaluateObjectArgument(Info, This, ThisVal))
       ThisPtr = &ThisVal;
-    if (Info.EvalStatus.HasSideEffects)
+    if (Info.EvalStatus.HasSideEffects || Info.EvalStatus.HasUndefinedBehavior)
       return false;
   }
 
@@ -11411,14 +11408,14 @@
         !Evaluate(ArgValues[I - Args.begin()], Info, *I))
       // If evaluation fails, throw away the argument entirely.
       ArgValues[I - Args.begin()] = APValue();
-    if (Info.EvalStatus.HasSideEffects)
+    if (Info.EvalStatus.HasSideEffects || Info.EvalStatus.HasUndefinedBehavior)
       return false;
   }
 
   // Build fake call to Callee.
   CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
                        ArgValues.data());
-  return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects;
+  return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects && !Info.EvalStatus.HasUndefinedBehavior;
 }
 
 bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to