PR16377: Allow evaluation of statement expressions in constant evaluation,
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 20 Jun 2013 03:00:05 +0000 (03:00 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 20 Jun 2013 03:00:05 +0000 (03:00 +0000)
why not. Apparently GCC supports this.

llvm-svn: 184396

clang/include/clang/Basic/DiagnosticASTKinds.td
clang/lib/AST/ExprConstant.cpp
clang/test/SemaCXX/constant-expression-cxx11.cpp
clang/test/SemaCXX/constant-expression-cxx1y.cpp

index f560069..919245e 100644 (file)
@@ -39,7 +39,7 @@ def note_constexpr_non_global : Note<
   "%select{pointer|reference}0 to %select{|subobject of }1"
   "%select{temporary|%3}2 is not a constant expression">;
 def note_constexpr_uninitialized : Note<
-  "subobject of type %0 is not initialized">;
+  "%select{|sub}0object of type %1 is not initialized">;
 def note_constexpr_array_index : Note<"cannot refer to element %0 of "
   "%select{array of %2 elements|non-array object}1 in a constant expression">;
 def note_constexpr_float_arithmetic : Note<
@@ -126,6 +126,9 @@ def note_constexpr_access_static_temporary : Note<
 def note_constexpr_modify_global : Note<
   "a constant expression cannot modify an object that is visible outside "
   "that expression">;
+def note_constexpr_stmt_expr_unsupported : Note<
+  "this use of statement expressions is not supported in a "
+  "constant expression">;
 def note_constexpr_calls_suppressed : Note<
   "(skipping %0 call%s0 in backtrace; use -fconstexpr-backtrace-limit=0 to "
   "see all)">;
index 72f1c21..67c187c 100644 (file)
@@ -1133,7 +1133,8 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E,
 static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc,
                                     QualType Type, const APValue &Value) {
   if (Value.isUninit()) {
-    Info.Diag(DiagLoc, diag::note_constexpr_uninitialized) << Type;
+    Info.Diag(DiagLoc, diag::note_constexpr_uninitialized)
+      << true << Type;
     return false;
   }
 
@@ -2888,6 +2889,13 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
     Result.set(VD, Info.CurrentCall->Index);
     APValue &Val = Info.CurrentCall->Temporaries[VD];
 
+    if (!VD->getInit()) {
+      Info.Diag(D->getLocStart(), diag::note_constexpr_uninitialized)
+        << false << VD->getType();
+      Val = APValue();
+      return false;
+    }
+
     if (!EvaluateInPlace(Val, Info, Result, VD->getInit())) {
       // Wipe out any partially-computed value, to allow tracking that this
       // evaluation failed.
@@ -2972,7 +2980,10 @@ static EvalStmtResult EvaluateSwitch(APValue &Result, EvalInfo &Info,
   case ESR_Returned:
     return ESR;
   case ESR_CaseNotFound:
-    llvm_unreachable("couldn't find switch case");
+    // This can only happen if the switch case is nested within a statement
+    // expression. We have no intention of supporting that.
+    Info.Diag(Found->getLocStart(), diag::note_constexpr_stmt_expr_unsupported);
+    return ESR_Failed;
   }
   llvm_unreachable("Invalid EvalStmtResult!");
 }
@@ -3853,6 +3864,40 @@ public:
     return DerivedSuccess(RVal, UO);
   }
 
+  RetTy VisitStmtExpr(const StmtExpr *E) {
+    // We will have checked the full-expressions inside the statement expression
+    // when they were completed, and don't need to check them again now.
+    if (Info.getIntOverflowCheckMode())
+      return Error(E);
+
+    const CompoundStmt *CS = E->getSubStmt();
+    for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
+                                           BE = CS->body_end();
+         /**/; ++BI) {
+      if (BI + 1 == BE) {
+        const Expr *FinalExpr = dyn_cast<Expr>(*BI);
+        if (!FinalExpr) {
+          Info.Diag((*BI)->getLocStart(),
+                    diag::note_constexpr_stmt_expr_unsupported);
+          return false;
+        }
+        return this->Visit(FinalExpr);
+      }
+
+      APValue ReturnValue;
+      EvalStmtResult ESR = EvaluateStmt(ReturnValue, Info, *BI);
+      if (ESR != ESR_Succeeded) {
+        // FIXME: If the statement-expression terminated due to 'return',
+        // 'break', or 'continue', it would be nice to propagate that to
+        // the outer statement evaluation rather than bailing out.
+        if (ESR != ESR_Failed)
+          Info.Diag((*BI)->getLocStart(),
+                    diag::note_constexpr_stmt_expr_unsupported);
+        return false;
+      }
+    }
+  }
+
   /// Visit a value which is evaluated, but whose value is ignored.
   void VisitIgnoredValue(const Expr *E) {
     EvaluateIgnoredValue(Info, E);
index b928887..4e2b7ed 100644 (file)
@@ -1649,3 +1649,24 @@ namespace InitializerList {
   }
   static_assert(sum({1, 2, 3, 4, 5}) == 15, "");
 }
+
+namespace StmtExpr {
+  struct A { int k; };
+  void f() {
+    static_assert(({ const int x = 5; x * 3; }) == 15, ""); // expected-warning {{extension}}
+    constexpr auto a = ({ A(); }); // expected-warning {{extension}}
+  }
+  constexpr int g(int k) {
+    return ({ // expected-warning {{extension}}
+      const int x = k;
+      x * x;
+    });
+  }
+  static_assert(g(123) == 15129, "");
+  constexpr int h() { // expected-error {{never produces a constant}}
+    return ({ // expected-warning {{extension}}
+      return 0; // expected-note {{not supported}}
+      1;
+    });
+  }
+}
index af44bf8..6043a1f 100644 (file)
@@ -765,3 +765,28 @@ namespace InitializerList {
   }
   static_assert(sum({1, 2, 3, 4, 5}) == 15, "");
 }
+
+namespace StmtExpr {
+  constexpr int f(int k) {
+    switch (k) {
+    case 0:
+      return 0;
+
+      ({
+        case 1: // expected-note {{not supported}}
+          return 1;
+      });
+    }
+  }
+  static_assert(f(1) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}}
+
+  constexpr int g() { // expected-error {{never produces a constant}}
+    return ({ int n; n; }); // expected-note {{object of type 'int' is not initialized}}
+  }
+
+  // FIXME: We should handle the void statement expression case.
+  constexpr int h() { // expected-error {{never produces a constant}}
+    ({ if (true) {} }); // expected-note {{not supported}}
+    return 0;
+  }
+}