Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -1089,8 +1089,9 @@ /// declaration, and produce notes explaining why it cannot be evaluated or is /// not a constant expression. Returns a pointer to the value if evaluation /// succeeded, 0 otherwise. - APValue *evaluateValue() const; - APValue *evaluateValue(SmallVectorImpl &Notes) const; + APValue *evaluateValue(bool RespectMicrosoftABIIfNeeded = false) const; + APValue *evaluateValue(SmallVectorImpl &Notes, + bool RespectMicrosoftABIIfNeeded = false) const; /// \brief Return the already-evaluated value of this variable's /// initializer, or NULL if the value is not yet known. Returns pointer Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -630,7 +630,8 @@ /// notes will be produced if the expression is not a constant expression. bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx, const VarDecl *VD, - SmallVectorImpl &Notes) const; + SmallVectorImpl &Notes, + bool RespectMicrosoftABIIfNeeded) const; /// EvaluateWithSubstitution - Evaluate an expression as if from the context /// of a call to the given function with the given arguments, inside an 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< + "in the Microsoft ABI static local variables cannot contain references to " + "variables">; def warn_integer_constant_overflow : Warning< "overflow in expression; result is %0 with type %1">, Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -2137,9 +2137,9 @@ return Eval; } -APValue *VarDecl::evaluateValue() const { +APValue *VarDecl::evaluateValue(bool RespectMicrosoftABIIfNeeded) const { SmallVector Notes; - return evaluateValue(Notes); + return evaluateValue(Notes, RespectMicrosoftABIIfNeeded); } namespace { @@ -2150,13 +2150,17 @@ } // namespace APValue *VarDecl::evaluateValue( - SmallVectorImpl &Notes) const { + SmallVectorImpl &Notes, + bool RespectMicrosoftABIIfNeeded) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); // We only produce notes indicating why an initializer is non-constant the // first time it is evaluated. FIXME: The notes won't always be emitted the // first time we try evaluation, so might not be produced at all. - if (Eval->WasEvaluated) + // In case the caller asked us to respect Microsoft ABI requirements, we + // never cache the result in Eval, so we have to perform the evaluation + // every time. + if (Eval->WasEvaluated && !RespectMicrosoftABIIfNeeded) return Eval->Evaluated.isUninit() ? nullptr : &Eval->Evaluated; const auto *Init = cast(Eval->Value); @@ -2172,7 +2176,7 @@ Eval->IsEvaluating = true; bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, getASTContext(), - this, Notes); + this, Notes, RespectMicrosoftABIIfNeeded); // Ensure the computed APValue is cleaned up later if evaluation succeeded, // or that it's empty (so that there's nothing to clean up) if evaluation @@ -2183,13 +2187,15 @@ getASTContext().AddDeallocation(DestroyAPValue, &Eval->Evaluated); Eval->IsEvaluating = false; - Eval->WasEvaluated = true; - - // In C++11, we have determined whether the initializer was a constant - // expression as a side-effect. - if (getASTContext().getLangOpts().CPlusPlus11 && !Eval->CheckedICE) { - Eval->CheckedICE = true; - Eval->IsICE = Result && Notes.empty(); + if (!RespectMicrosoftABIIfNeeded) { + Eval->WasEvaluated = true; + + // In C++11, we have determined whether the initializer was a constant + // expression as a side-effect. + if (getASTContext().getLangOpts().CPlusPlus11 && !Eval->CheckedICE) { + Eval->CheckedICE = true; + Eval->IsICE = Result && Notes.empty(); + } } return Result ? &Eval->Evaluated : nullptr; 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 @@ -4050,6 +4094,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 +4115,31 @@ bool ZeroInitialization(const Expr *E) { return Error(E); } + // 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) { + bool Result = Error(E, diag::note_constexpr_microsoft_abi_declrefexpr); + Info.addNotes(Diag); + return Result; + } + return true; + } + + // 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 +4702,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 +6147,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 +7266,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); @@ -7710,6 +7804,11 @@ return Success(~Result.getInt(), E); } case UO_LNot: { + // In the Microsoft ABI, a DeclRefExpr on either side of some binary + // operators will prevent us from evaluating the expression to a constant. + if (Info.IsMicrosoftABI && + !CheckPotentialExpressionContainingDeclRefExpr(E->getSubExpr())) + return false; bool bres; if (!EvaluateAsBooleanCondition(E->getSubExpr(), bres, Info)) return false; @@ -8900,7 +8999,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, const VarDecl *VD, - SmallVectorImpl &Notes) const { + SmallVectorImpl &Notes, + bool RespectMicrosoftABIIfNeeded) const { // FIXME: Evaluating initializers for large array and record types can cause // performance problems. Only do so in C++11 for now. if (isRValue() && (getType()->isArrayType() || getType()->isRecordType()) && @@ -8915,6 +9015,11 @@ : EvalInfo::EM_ConstantFold); InitInfo.setEvaluatingDecl(VD, Value); + if (RespectMicrosoftABIIfNeeded && + Ctx.getTargetInfo().getCXXABI().isMicrosoft() && + VD->isStaticLocal()) + InitInfo.useMicrosoftABI(); + LValue LVal; LVal.set(VD); Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -1190,8 +1190,8 @@ return EmitNullConstant(D.getType()); } } - - if (const APValue *Value = D.evaluateValue()) + + if (const APValue *Value = D.evaluateValue(true)) return EmitConstantValueForMemory(*Value, D.getType(), CGF); // FIXME: Implement C++11 [basic.start.init]p2: if the initializer of a Index: test/CodeGenCXX/static-init-msvc.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/static-init-msvc.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -triple i686-windows-msvc -std=c++11 -fms-extensions -emit-llvm -O0 -w -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=c++11 -fms-extensions -emit-llvm -O0 -w -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i686-windows-gnu -std=c++11 -fms-extensions -emit-llvm -O0 -w -o - %s | FileCheck %s --check-prefix GNU +// RUN: %clang_cc1 -triple x86_64-windows-gnu -std=c++11 -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_not() { +// CHECK-LABEL: @"\01?fun_not@@YAXXZ"() +// GNU-LABEL: @_Z7fun_notv() + static int k; + static const bool foo = !&k; +// CHECK: init: +// CHECK-NEXT: store i8 0, i8* @"\01?foo@?1??fun_not@@YAXXZ@4_NB", align 1 +// 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 +} + +void fun_constexpr() { + // Here we are willfully violating the Microsoft ABI, since the user has + // asked for constexpr explicitly... + +// CHECK-LABEL: @"\01?fun_constexpr@@YAXXZ"() +// GNU-LABEL: @_Z13fun_constexprv() + static int k; + static constexpr int *p = true ? 0 : &k; +// GNU: entry: +// GNU-NEXT: ret void +}