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">, 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 *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 *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 @@ -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 + bool CheckPotentialExpressionContainingDeclRefExpr(const Expr *E) { + SmallVector Diag; + SpeculativeLookForDeclRefExprRAII Speculate(Info, &Diag); + + StmtVisitorTy::Visit(E); + if (Info.HaveSeenDeclRefExpr) + return Error(E, diag::note_constexpr_microsoft_abi_declrefexpr); + return true; + } template 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 + 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(E->getDecl())) return Success(FD); if (const VarDecl *VD = dyn_cast(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: 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 +}