ehsan created this revision.
ehsan added a reviewer: rnk.
ehsan added a subscriber: cfe-commits.

In the Microsoft ABI, some expressions containing references to variables
cannot be evaluated as a constant.  These are expressions containing a
conditional, logical and/or, or comma operator with an operand that is a
variable name.

This is observable at the ABI level by whether the compiler would emit a
static initializer for such expressions where normally it would emit a
readonly constant data.  See PR26210 for more details.

http://reviews.llvm.org/D16465

Files:
  include/clang/Basic/DiagnosticASTKinds.td
  lib/AST/ExprConstant.cpp
  test/CodeGenCXX/static-init-msvc.cpp

Index: test/CodeGenCXX/static-init-msvc.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/static-init-msvc.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -triple i686-windows-msvc -fms-extensions -emit-llvm -O0 -w -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions -emit-llvm -O0 -w -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple i686-windows-gnu -fms-extensions -emit-llvm -O0 -w -o - %s | FileCheck %s --check-prefix GNU
+// RUN: %clang_cc1 -triple x86_64-windows-gnu -fms-extensions -emit-llvm -O0 -w -o - %s | FileCheck %s --check-prefix GNU
+
+void fun_and() {
+// CHECK-LABEL: @"\01?fun_and@@YAXXZ"()
+// GNU-LABEL: @_Z7fun_andv()
+  static int k;
+  static const int foo = 0 && k;
+// CHECK: init:
+// CHECK-NEXT: store i32 0, i32* @"\01?foo@?1??fun_and@@YAXXZ@4HB", align 4
+// GNU: entry:
+// GNU-NEXT: ret void
+}
+
+void fun_or() {
+// CHECK-LABEL: @"\01?fun_or@@YAXXZ"()
+// GNU-LABEL: @_Z6fun_orv()
+  static int k;
+  static const int foo = 1 || k;
+// CHECK: init:
+// CHECK-NEXT: store i32 1, i32* @"\01?foo@?1??fun_or@@YAXXZ@4HB", align 4
+// GNU: entry:
+// GNU-NEXT: ret void
+}
+
+void fun_comma() {
+// CHECK-LABEL: @"\01?fun_comma@@YAXXZ"()
+// GNU-LABEL: @_Z9fun_commav()
+  static int k;
+  static const int foo = (k, 0);
+// CHECK: init:
+// CHECK-NEXT: store i32 0, i32* @"\01?foo@?1??fun_comma@@YAXXZ@4HB", align 4
+// GNU: entry:
+// GNU-NEXT: ret void
+}
+
+void fun_cond() {
+// CHECK-LABEL: @"\01?fun_cond@@YAXXZ"()
+// GNU-LABEL: @_Z8fun_condv()
+  static int k;
+  static const int foo = true ? 0 : k;
+// CHECK: init:
+// CHECK-NEXT: store i32 0, i32* @"\01?foo@?1??fun_cond@@YAXXZ@4HB", align 4
+// GNU: entry:
+// GNU-NEXT: ret void
+}
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -477,6 +477,19 @@
     /// fold (not just why it's not strictly a constant expression)?
     bool HasFoldFailureDiagnostic;
 
+    /// \brief True if we need to obey the Microsoft ABI.  This affects whether
+    /// some expressions can be evaluated as a constant if they refer to
+    /// variables in some cases.  See PR26210 for the discussion.
+    bool IsMicrosoftABI;
+
+    /// \brief True if we are looking for a DeclRefExpr.
+    bool LookingForDeclRefExpr;
+
+    /// \brief True if we have observed a DeclRefExpr since the last time that
+    /// awaitDeclRefExpr() was called.  This is used in order to handle Microsoft
+    /// ABI requirements.
+    bool HaveSeenDeclRefExpr;
+
     enum EvaluationMode {
       /// Evaluate as a constant expression. Stop if we find that the expression
       /// is not a constant expression.
@@ -541,7 +554,9 @@
         BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr),
         EvaluatingDecl((const ValueDecl *)nullptr),
         EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
-        HasFoldFailureDiagnostic(false), EvalMode(Mode) {}
+        HasFoldFailureDiagnostic(false), IsMicrosoftABI(false),
+        LookingForDeclRefExpr(false), HaveSeenDeclRefExpr(false),
+        EvalMode(Mode) {}
 
     void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
       EvaluatingDecl = Base;
@@ -766,6 +781,22 @@
     bool allowInvalidBaseExpr() const {
       return EvalMode == EM_DesignatorFold;
     }
+
+    /// Use the Microsoft ABI during evaluation.
+    void useMicrosoftABI() {
+      IsMicrosoftABI = true;
+    }
+
+    /// Start watching for a DeclRefExpr.
+    void awaitDeclRefExpr() {
+      LookingForDeclRefExpr = true;
+      HaveSeenDeclRefExpr = false;
+    }
+
+    /// Remember that we have observed a DeclRefExpr.
+    void noteDeclRefExpr() {
+      HaveSeenDeclRefExpr = true;
+    }
   };
 
   /// Object used to treat all foldable expressions as constant expressions.
@@ -814,13 +845,14 @@
   /// RAII object used to suppress diagnostics and side-effects from a
   /// speculative evaluation.
   class SpeculativeEvaluationRAII {
-    EvalInfo &Info;
     Expr::EvalStatus Old;
+  protected:
+    EvalInfo &Info;
 
   public:
     SpeculativeEvaluationRAII(EvalInfo &Info,
                         SmallVectorImpl<PartialDiagnosticAt> *NewDiag = nullptr)
-      : Info(Info), Old(Info.EvalStatus) {
+      : Old(Info.EvalStatus), Info(Info) {
       Info.EvalStatus.Diag = NewDiag;
       // If we're speculatively evaluating, we may have skipped over some
       // evaluations and missed out a side effect.
@@ -831,6 +863,18 @@
     }
   };
 
+  class SpeculativeLookForDeclRefExprRAII final : SpeculativeEvaluationRAII {
+  public:
+    SpeculativeLookForDeclRefExprRAII(EvalInfo &Info,
+                           SmallVectorImpl<PartialDiagnosticAt> *NewDiag = nullptr)
+      : SpeculativeEvaluationRAII(Info, NewDiag) {
+      Info.awaitDeclRefExpr();
+    }
+    ~SpeculativeLookForDeclRefExprRAII() {
+      Info.LookingForDeclRefExpr = false;
+    }
+  };
+
   /// RAII object wrapping a full-expression or block scope, and handling
   /// the ending of the lifetime of temporaries created within it.
   template<bool IsFullExpression>
@@ -4039,6 +4083,18 @@
     Error(E, diag::note_constexpr_conditional_never_const);
   }
 
+  // Check whether an expression contains a DeclRefExpr where not permitted by
+  // the Microsoft ABI.
+  template<typename Expr>
+  bool CheckPotentialExpressionContainingDeclRefExpr(const Expr *E) {
+    SmallVector<PartialDiagnosticAt, 8> Diag;
+    SpeculativeLookForDeclRefExprRAII Speculate(Info, &Diag);
+
+    StmtVisitorTy::Visit(E);
+    if (Info.HaveSeenDeclRefExpr)
+      return Error(E, diag::note_constexpr_microsoft_abi_declrefexpr);
+    return true;
+  }
 
   template<typename ConditionalOperator>
   bool HandleConditionalOperator(const ConditionalOperator *E) {
@@ -4050,6 +4106,13 @@
     }
 
     Expr *EvalExpr = BoolResult ? E->getTrueExpr() : E->getFalseExpr();
+    if (Info.IsMicrosoftABI) {
+      // In the Microsoft ABI, a DeclRefExpr on either side of the conditional
+      // operator will prevent us from evaluating the expression to a constant.
+      if (!CheckPotentialExpressionContainingDeclRefExpr(E->getTrueExpr(),
+                                                          E->getFalseExpr()))
+        return false;
+    }
     return StmtVisitorTy::Visit(EvalExpr);
   }
 
@@ -4064,6 +4127,15 @@
 
   bool ZeroInitialization(const Expr *E) { return Error(E); }
 
+  // Check whether either of the two expressions contains a DeclRefExpr where
+  // not permitted by the Microsoft ABI.
+  template<typename ExprLHS, typename ExprRHS>
+  bool CheckPotentialExpressionContainingDeclRefExpr(const ExprLHS *ELHS,
+                                                     const ExprRHS *ERHS) {
+    return CheckPotentialExpressionContainingDeclRefExpr(ELHS) &&
+           CheckPotentialExpressionContainingDeclRefExpr(ERHS);
+  }
+
 public:
   ExprEvaluatorBase(EvalInfo &Info) : Info(Info) {}
 
@@ -4626,6 +4698,8 @@
 }
 
 bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
+  Info.noteDeclRefExpr();
+
   if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getDecl()))
     return Success(FD);
   if (const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
@@ -6069,6 +6143,8 @@
 
   bool CheckReferencedDecl(const Expr *E, const Decl *D);
   bool VisitDeclRefExpr(const DeclRefExpr *E) {
+    Info.noteDeclRefExpr();
+
     if (CheckReferencedDecl(E, E->getDecl()))
       return true;
 
@@ -7186,6 +7262,20 @@
 }
 
 bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
+    if (Info.IsMicrosoftABI) {
+      switch (E->getOpcode()) {
+      case BO_Comma:
+      case BO_LAnd:
+      case BO_LOr:
+        // In the Microsoft ABI, a DeclRefExpr on either side of some binary
+        // operators will prevent us from evaluating the expression to a constant.
+        if (!CheckPotentialExpressionContainingDeclRefExpr(E->getLHS(), E->getRHS()))
+          return false;
+      default:
+        /* No need to do anything. */ ;
+      }
+    }
+
   if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp())
     return Error(E);
 
@@ -8915,6 +9005,9 @@
                                       : EvalInfo::EM_ConstantFold);
   InitInfo.setEvaluatingDecl(VD, Value);
 
+  if (Ctx.getTargetInfo().getCXXABI().isMicrosoft())
+    InitInfo.useMicrosoftABI();
+
   LValue LVal;
   LVal.set(VD);
 
Index: include/clang/Basic/DiagnosticASTKinds.td
===================================================================
--- include/clang/Basic/DiagnosticASTKinds.td
+++ include/clang/Basic/DiagnosticASTKinds.td
@@ -148,6 +148,9 @@
 def note_constexpr_baa_value_insufficient_alignment : Note<
   "value of the aligned pointer (%0) is not a multiple of the asserted %1 "
   "%plural{1:byte|:bytes}1">;
+def note_constexpr_microsoft_abi_declrefexpr : Note<
+  "the constant expression cannot contain a reference to a variable as a Microsoft "
+  "ABI extension">;
 
 def warn_integer_constant_overflow : Warning<
   "overflow in expression; result is %0 with type %1">,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to