]> granicus.if.org Git - clang/commitdiff
Deal with a horrible C++11 special case. If a non-literal type has a constexpr
authorRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 13 Feb 2012 22:16:19 +0000 (22:16 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 13 Feb 2012 22:16:19 +0000 (22:16 +0000)
constructor, and that constructor is used to initialize an object of static
storage duration such that all members and bases are initialized by constant
expressions, constant initialization is performed. In this case, the object
can still have a non-trivial destructor, and if it does, we must emit a dynamic
initializer which performs no initialization and instead simply registers that
destructor.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150419 91177308-0d34-0410-b5e6-96231b3b80d8

13 files changed:
lib/AST/ExprConstant.cpp
lib/CodeGen/CGCXXABI.cpp
lib/CodeGen/CGCXXABI.h
lib/CodeGen/CGDecl.cpp
lib/CodeGen/CGDeclCXX.cpp
lib/CodeGen/CGExprConstant.cpp
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/CodeGenModule.cpp
lib/CodeGen/CodeGenModule.h
lib/CodeGen/ItaniumCXXABI.cpp
lib/Sema/SemaDecl.cpp
test/CXX/class/class.static/class.static.data/p3.cpp
test/CodeGenCXX/const-init-cxx11.cpp

index 520e3cbbafea39ad2dfd33a9b433814c1950b5d7..c0fff5e4781ef833c192261c7f30cb08d67e3fae 100644 (file)
@@ -855,7 +855,8 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E);
 static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info,
                                        const LValue &This, const Expr *E,
                                        CheckConstantExpressionKind CCEK
-                                        = CCEK_Constant);
+                                        = CCEK_Constant,
+                                       bool AllowNonLiteralTypes = false);
 static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info);
 static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info);
 static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result,
@@ -5829,8 +5830,9 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E) {
 /// which were initialized earlier.
 static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info,
                                        const LValue &This, const Expr *E,
-                                       CheckConstantExpressionKind CCEK) {
-  if (!CheckLiteralType(Info, E))
+                                       CheckConstantExpressionKind CCEK,
+                                       bool AllowNonLiteralTypes) {
+  if (!AllowNonLiteralTypes && !CheckLiteralType(Info, E))
     return false;
 
   if (E->isRValue()) {
@@ -5941,9 +5943,6 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
   EvalInfo InitInfo(Ctx, EStatus);
   InitInfo.setEvaluatingDecl(VD, Value);
 
-  if (!CheckLiteralType(InitInfo, this))
-    return false;
-
   LValue LVal;
   LVal.set(VD);
 
@@ -5954,11 +5953,13 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
   if (Ctx.getLangOptions().CPlusPlus && !VD->hasLocalStorage() &&
       !VD->getType()->isReferenceType()) {
     ImplicitValueInitExpr VIE(VD->getType());
-    if (!EvaluateConstantExpression(Value, InitInfo, LVal, &VIE))
+    if (!EvaluateConstantExpression(Value, InitInfo, LVal, &VIE, CCEK_Constant,
+                                    /*AllowNonLiteralTypes=*/true))
       return false;
   }
 
-  return EvaluateConstantExpression(Value, InitInfo, LVal, this) &&
+  return EvaluateConstantExpression(Value, InitInfo, LVal, this, CCEK_Constant,
+                                    /*AllowNonLiteralTypes=*/true) &&
          !EStatus.HasSideEffects;
 }
 
index c1b20af799fcc103b8e6d246129cc270980910f2..e5d3f2da6b6ceb25f82084bbcc8defd6cac2f7dd 100644 (file)
@@ -168,6 +168,7 @@ void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
 
 void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
                                const VarDecl &D,
-                               llvm::GlobalVariable *GV) {
+                               llvm::GlobalVariable *GV,
+                               bool PerformInit) {
   ErrorUnsupportedABI(CGF, "static local variable initialization");
 }
index 5e21bb0c9f121fd0354bd961027273cc0f47c262..4bacd014036a73cb463c1e45552dbd66a0ae20f0 100644 (file)
@@ -226,12 +226,14 @@ public:
 
   /// Emits the guarded initializer and destructor setup for the given
   /// variable, given that it couldn't be emitted as a constant.
+  /// If \p PerformInit is false, the initialization has been folded to a
+  /// constant and should not be performed.
   ///
   /// The variable may be:
   ///   - a static local variable
   ///   - a static data member of a class template instantiation
   virtual void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
-                               llvm::GlobalVariable *DeclPtr);
+                               llvm::GlobalVariable *DeclPtr, bool PerformInit);
 
 };
 
index 3d32091a4b08306374b23b36b6347b8095cb9200..2de19840e53d6f4f6b1d9258cf35cc54f8fa38f7 100644 (file)
@@ -196,6 +196,14 @@ CodeGenFunction::CreateStaticVarDecl(const VarDecl &D,
   return GV;
 }
 
+/// hasNontrivialDestruction - Determine whether a type's destruction is
+/// non-trivial. If so, and the variable uses static initialization, we must
+/// register its destructor to run on exit.
+static bool hasNontrivialDestruction(QualType T) {
+  CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
+  return RD && !RD->hasTrivialDestructor();
+}
+
 /// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
 /// global variable that has already been created for it.  If the initializer
 /// has a different type than GV does, this may free GV and return a different
@@ -215,7 +223,7 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
       // be constant.
       GV->setConstant(false);
 
-      EmitCXXGuardedInit(D, GV);
+      EmitCXXGuardedInit(D, GV, /*PerformInit*/true);
     }
     return GV;
   }
@@ -248,6 +256,16 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
   }
 
   GV->setInitializer(Init);
+
+  if (hasNontrivialDestruction(D.getType())) {
+    // We have a constant initializer, but a nontrivial destructor. We still
+    // need to perform a guarded "initialization" in order to register the
+    // destructor. Since we're running a destructor on this variable, it can't
+    // be a constant even if it's const.
+    GV->setConstant(false);
+    EmitCXXGuardedInit(D, GV, /*PerformInit*/false);
+  }
+
   return GV;
 }
 
index 8406002e5adfe665c172195eecbe0ec989802194..44acf62aa644eecf927941b1b65dd1925c06f41e 100644 (file)
@@ -102,17 +102,21 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
 }
 
 void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D,
-                                               llvm::Constant *DeclPtr) {
+                                               llvm::Constant *DeclPtr,
+                                               bool PerformInit) {
 
   const Expr *Init = D.getInit();
   QualType T = D.getType();
 
   if (!T->isReferenceType()) {
-    EmitDeclInit(*this, D, DeclPtr);
+    if (PerformInit)
+      EmitDeclInit(*this, D, DeclPtr);
     EmitDeclDestroy(*this, D, DeclPtr);
     return;
   }
 
+  assert(PerformInit && "cannot have constant initializer which needs "
+         "destruction for reference");
   unsigned Alignment = getContext().getDeclAlign(&D).getQuantity();
   RValue RV = EmitReferenceBindingToExpr(Init, &D);
   EmitStoreOfScalar(RV.getScalarVal(), DeclPtr, false, Alignment, T);
@@ -152,7 +156,8 @@ CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn,
 }
 
 void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
-                                         llvm::GlobalVariable *DeclPtr) {
+                                         llvm::GlobalVariable *DeclPtr,
+                                         bool PerformInit) {
   // If we've been asked to forbid guard variables, emit an error now.
   // This diagnostic is hard-coded for Darwin's use case;  we can find
   // better phrasing if someone else needs it.
@@ -161,7 +166,7 @@ void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
               "this initialization requires a guard variable, which "
               "the kernel does not support");
 
-  CGM.getCXXABI().EmitGuardedInit(*this, D, DeclPtr);
+  CGM.getCXXABI().EmitGuardedInit(*this, D, DeclPtr, PerformInit);
 }
 
 static llvm::Function *
@@ -186,14 +191,16 @@ CreateGlobalInitOrDestructFunction(CodeGenModule &CGM,
 
 void
 CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
-                                            llvm::GlobalVariable *Addr) {
+                                            llvm::GlobalVariable *Addr,
+                                            bool PerformInit) {
   llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
 
   // Create a variable initialization function.
   llvm::Function *Fn =
     CreateGlobalInitOrDestructFunction(*this, FTy, "__cxx_global_var_init");
 
-  CodeGenFunction(*this).GenerateCXXGlobalVarDeclInitFunc(Fn, D, Addr);
+  CodeGenFunction(*this).GenerateCXXGlobalVarDeclInitFunc(Fn, D, Addr,
+                                                          PerformInit);
 
   if (D->hasAttr<InitPriorityAttr>()) {
     unsigned int order = D->getAttr<InitPriorityAttr>()->getPriority();
@@ -267,7 +274,8 @@ void CodeGenModule::EmitCXXGlobalDtorFunc() {
 /// Emit the code necessary to initialize the given global variable.
 void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn,
                                                        const VarDecl *D,
-                                                 llvm::GlobalVariable *Addr) {
+                                                 llvm::GlobalVariable *Addr,
+                                                       bool PerformInit) {
   StartFunction(GlobalDecl(), getContext().VoidTy, Fn,
                 getTypes().getNullaryFunctionInfo(),
                 FunctionArgList(), SourceLocation());
@@ -277,9 +285,9 @@ void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn,
   // definitions explicitly marked weak.
   if (Addr->getLinkage() == llvm::GlobalValue::WeakODRLinkage ||
       Addr->getLinkage() == llvm::GlobalValue::WeakAnyLinkage) {
-    EmitCXXGuardedInit(*D, Addr);
+    EmitCXXGuardedInit(*D, Addr, PerformInit);
   } else {
-    EmitCXXGlobalVarDeclInit(*D, Addr);
+    EmitCXXGlobalVarDeclInit(*D, Addr, PerformInit);
   }
 
   FinishFunction();
@@ -357,4 +365,3 @@ CodeGenFunction::generateDestroyHelper(llvm::Constant *addr,
   
   return fn;
 }
-
index ccb4b930e678497c6374125ff00a632cc4e00efc..0b3ac9cbe5c197c34d833a65bff29c02535d2205 100644 (file)
@@ -939,6 +939,15 @@ llvm::Constant *CodeGenModule::EmitConstantInit(const VarDecl &D,
   if (const APValue *Value = D.evaluateValue())
     return EmitConstantValue(*Value, D.getType(), CGF);
 
+  // FIXME: Implement C++11 [basic.start.init]p2: if the initializer of a
+  // reference is a constant expression, and the reference binds to a temporary,
+  // then constant initialization is performed. ConstExprEmitter will
+  // incorrectly emit a prvalue constant in this case, and the calling code
+  // interprets that as the (pointer) value of the reference, rather than the
+  // desired value of the referee.
+  if (D.getType()->isReferenceType())
+    return 0;
+
   const Expr *E = D.getInit();
   assert(E && "No initializer to emit");
 
index 277640b8ce513c46de3bed67de9b94714f10ece8..ca5b88363e10dfe4c632c24ccb406b6e47502d6f 100644 (file)
@@ -2361,7 +2361,8 @@ public:
 
   /// EmitCXXGlobalVarDeclInit - Create the initializer for a C++
   /// variable with global storage.
-  void EmitCXXGlobalVarDeclInit(const VarDecl &D, llvm::Constant *DeclPtr);
+  void EmitCXXGlobalVarDeclInit(const VarDecl &D, llvm::Constant *DeclPtr,
+                                bool PerformInit);
 
   /// EmitCXXGlobalDtorRegistration - Emits a call to register the global ptr
   /// with the C++ runtime so that its destructor will be called at exit.
@@ -2373,7 +2374,8 @@ public:
   /// possible to prove that an initialization will be done exactly
   /// once, e.g. with a static local variable or a static data member
   /// of a class template.
-  void EmitCXXGuardedInit(const VarDecl &D, llvm::GlobalVariable *DeclPtr);
+  void EmitCXXGuardedInit(const VarDecl &D, llvm::GlobalVariable *DeclPtr,
+                          bool PerformInit);
 
   /// GenerateCXXGlobalInitFunc - Generates code for initializing global
   /// variables.
@@ -2389,7 +2391,8 @@ public:
 
   void GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn,
                                         const VarDecl *D,
-                                        llvm::GlobalVariable *Addr);
+                                        llvm::GlobalVariable *Addr,
+                                        bool PerformInit);
 
   void EmitCXXConstructExpr(const CXXConstructExpr *E, AggValueSlot Dest);
   
index fde217efecf8e25e8ea0064162d5619d316c4b83..2ebea592641e422e4d172dd7baeb72f336d03cd2 100644 (file)
@@ -1359,7 +1359,9 @@ CharUnits CodeGenModule::GetTargetTypeStoreSize(llvm::Type *Ty) const {
 void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
   llvm::Constant *Init = 0;
   QualType ASTTy = D->getType();
-  bool NonConstInit = false;
+  CXXRecordDecl *RD = ASTTy->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
+  bool NeedsGlobalCtor = false;
+  bool NeedsGlobalDtor = RD && !RD->hasTrivialDestructor();
 
   const VarDecl *InitDecl;
   const Expr *InitExpr = D->getAnyInitializer(InitDecl);
@@ -1385,15 +1387,16 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
 
       if (getLangOptions().CPlusPlus) {
         Init = EmitNullConstant(T);
-        NonConstInit = true;
+        NeedsGlobalCtor = true;
       } else {
         ErrorUnsupported(D, "static initializer");
         Init = llvm::UndefValue::get(getTypes().ConvertType(T));
       }
     } else {
       // We don't need an initializer, so remove the entry for the delayed
-      // initializer position (just in case this entry was delayed).
-      if (getLangOptions().CPlusPlus)
+      // initializer position (just in case this entry was delayed) if we
+      // also don't need to register a destructor.
+      if (getLangOptions().CPlusPlus && !NeedsGlobalDtor)
         DelayedCXXInitPosition.erase(D);
     }
   }
@@ -1448,7 +1451,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
 
   // If it is safe to mark the global 'constant', do so now.
   GV->setConstant(false);
-  if (!NonConstInit && DeclIsConstantGlobal(Context, D, true))
+  if (!NeedsGlobalCtor && !NeedsGlobalDtor &&
+      DeclIsConstantGlobal(Context, D, true))
     GV->setConstant(true);
 
   GV->setAlignment(getContext().getDeclAlign(D).getQuantity());
@@ -1464,8 +1468,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
   SetCommonAttributes(D, GV);
 
   // Emit the initializer function if necessary.
-  if (NonConstInit)
-    EmitCXXGlobalVarDeclInitFunc(D, GV);
+  if (NeedsGlobalCtor || NeedsGlobalDtor)
+    EmitCXXGlobalVarDeclInitFunc(D, GV, NeedsGlobalCtor);
 
   // Emit global variable debug information.
   if (CGDebugInfo *DI = getModuleDebugInfo())
index 08e1ed7fb18cd0cfc5ac90260e923da139b394a7..6813edd5f18519abe7fe19895d0c0d8474bad584 100644 (file)
@@ -900,8 +900,11 @@ private:
   /// EmitCXXGlobalDtorFunc - Emit the function that destroys C++ globals.
   void EmitCXXGlobalDtorFunc();
 
+  /// EmitCXXGlobalVarDeclInitFunc - Emit the function that initializes the
+  /// specified global (if PerformInit is true) and registers its destructor.
   void EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
-                                    llvm::GlobalVariable *Addr);
+                                    llvm::GlobalVariable *Addr,
+                                    bool PerformInit);
 
   // FIXME: Hardcoding priority here is gross.
   void AddGlobalCtor(llvm::Function *Ctor, int Priority=65535);
index 044b7f0532eda490f1014e7d404707807594ade7..8dee17a38ccce7379183fa20a7d61013ead7f455 100644 (file)
@@ -121,7 +121,7 @@ public:
                        llvm::Value *&AllocPtr, CharUnits &CookieSize);
 
   void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
-                       llvm::GlobalVariable *DeclPtr);
+                       llvm::GlobalVariable *DeclPtr, bool PerformInit);
 };
 
 class ARMCXXABI : public ItaniumCXXABI {
@@ -1016,7 +1016,8 @@ namespace {
 /// just special-case it at particular places.
 void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
                                     const VarDecl &D,
-                                    llvm::GlobalVariable *GV) {
+                                    llvm::GlobalVariable *GV,
+                                    bool PerformInit) {
   CGBuilderTy &Builder = CGF.Builder;
 
   // We only need to use thread-safe statics for local variables;
@@ -1129,7 +1130,7 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
   }
 
   // Emit the initializer and add a global destructor if appropriate.
-  CGF.EmitCXXGlobalVarDeclInit(D, GV);
+  CGF.EmitCXXGlobalVarDeclInit(D, GV, PerformInit);
 
   if (threadsafe) {
     // Pop the guard-abort cleanup if we pushed one.
index 3c73f689e71cadcaf589c9ba164ca161712cbe06..de9feccaf330efd5c01c212dd8abfe7b6ef12e59 100644 (file)
@@ -3991,15 +3991,8 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
                                            TemplateParamLists.release());
     }
 
-    if (D.getDeclSpec().isConstexprSpecified()) {
+    if (D.getDeclSpec().isConstexprSpecified())
       NewVD->setConstexpr(true);
-      SourceLocation ConstexprLoc = D.getDeclSpec().getConstexprSpecLoc();
-      if (!NewVD->isInvalidDecl() && !R->isDependentType() &&
-          RequireLiteralType(NewVD->getLocation(), R,
-                             PDiag(diag::err_constexpr_var_non_literal)
-                               << SourceRange(ConstexprLoc)))
-        NewVD->setInvalidDecl();
-    }
   }
 
   // Set the lexical context. If the declarator has a C++ scope specifier, the
@@ -4347,6 +4340,13 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD,
     return false;
   }
 
+  if (NewVD->isConstexpr() && !T->isDependentType() &&
+      RequireLiteralType(NewVD->getLocation(), T,
+                         PDiag(diag::err_constexpr_var_non_literal))) {
+    NewVD->setInvalidDecl();
+    return false;
+  }
+
   if (!Previous.empty()) {
     MergeVarDecl(NewVD, Previous);
     return true;
index fef9a7dae162c154ae10f9f428c69414d174e3e4..117997ee28393085dde112b0831244a678ffb3c2 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
 
-struct NonLit { // expected-note {{no constexpr constructors}}
+struct NonLit { // expected-note 3{{no constexpr constructors}}
   NonLit();
 };
 
@@ -30,11 +30,11 @@ struct U {
   static constexpr int a = 0;
   static constexpr int b; // expected-error {{declaration of constexpr static data member 'b' requires an initializer}}
   static constexpr NonLit h = NonLit(); // expected-error {{cannot have non-literal type 'const NonLit'}}
-  static constexpr T c = T(); // expected-error {{must be initialized by a constant expression}} expected-note {{non-literal type}}
+  static constexpr T c = T(); // expected-error {{cannot have non-literal type}}
   static const T d;
 };
 
-template<typename T> constexpr T U<T>::d = T(); // expected-error {{must be initialized by a constant expression}} expected-note {{non-literal type 'const NonLit'}}
+template<typename T> constexpr T U<T>::d = T(); // expected-error {{non-literal type 'const NonLit'}}
 
 U<int> u1;
 U<NonLit> u2; // expected-note {{here}}
index 6f13faa2fce4eadb88a3595cfd1600489d85ad9d..70fa2984faca03742ac898aa173714758342778a 100644 (file)
@@ -185,9 +185,93 @@ namespace MemberPtr {
   extern constexpr void (B2::*b2m)() = (void(B2::*)())&D::m;
 }
 
+namespace LiteralReference {
+  struct Lit {
+    constexpr Lit() : n(5) {}
+    int n;
+  };
+  // FIXME: This should have static initialization, but we do not implement
+  // that yet. For now, just check that we don't set the (pointer) value of
+  // the reference to 5!
+  //
+  // CHECK: @_ZN16LiteralReference3litE = global {{.*}} null
+  const Lit &lit = Lit();
+}
+
+namespace NonLiteralConstexpr {
+  constexpr int factorial(int n) {
+    return n ? factorial(n-1) * n : 1;
+  }
+  extern void f(int *p);
+
+  struct NonTrivialDtor {
+    constexpr NonTrivialDtor() : n(factorial(5)), p(&n) {}
+    ~NonTrivialDtor() {
+      f(p);
+    }
+
+    int n;
+    int *p;
+  };
+  static_assert(!__is_literal(NonTrivialDtor), "");
+  // CHECK: @_ZN19NonLiteralConstexpr3ntdE = global {{.*}} { i32 120, i32* getelementptr
+  NonTrivialDtor ntd;
+
+  struct VolatileMember {
+    constexpr VolatileMember() : n(5) {}
+    volatile int n;
+  };
+  static_assert(!__is_literal(VolatileMember), "");
+  // CHECK: @_ZN19NonLiteralConstexpr2vmE = global {{.*}} { i32 5 }
+  VolatileMember vm;
+
+  struct Both {
+    constexpr Both() : n(10) {}
+    ~Both();
+    volatile int n;
+  };
+  // CHECK: @_ZN19NonLiteralConstexpr1bE = global {{.*}} { i32 10 }
+  Both b;
+
+  void StaticVars() {
+    // CHECK: @_ZZN19NonLiteralConstexpr10StaticVarsEvE3ntd = {{.*}} { i32 120, i32* getelementptr {{.*}}
+    // CHECK: @_ZGVZN19NonLiteralConstexpr10StaticVarsEvE3ntd =
+    static NonTrivialDtor ntd;
+    // CHECK: @_ZZN19NonLiteralConstexpr10StaticVarsEvE2vm = {{.*}} { i32 5 }
+    // CHECK-NOT: @_ZGVZN19NonLiteralConstexpr10StaticVarsEvE2vm =
+    static VolatileMember vm;
+    // CHECK: @_ZZN19NonLiteralConstexpr10StaticVarsEvE1b = {{.*}} { i32 10 }
+    // CHECK: @_ZGVZN19NonLiteralConstexpr10StaticVarsEvE1b =
+    static Both b;
+  }
+}
+
 // Constant initialization tests go before this point,
 // dynamic initialization tests go after.
 
+// We must emit a constant initializer for NonLiteralConstexpr::ntd, but also
+// emit an initializer to register its destructor.
+// CHECK: define {{.*}}cxx_global_var_init{{.*}}
+// CHECK-NOT: NonLiteralConstexpr
+// CHECK: call {{.*}}cxa_atexit{{.*}} @_ZN19NonLiteralConstexpr14NonTrivialDtorD1Ev {{.*}} @_ZN19NonLiteralConstexpr3ntdE
+// CHECK-NEXT: ret void
+
+// We don't need to emit any dynamic initialization for NonLiteralConstexpr::vm.
+// CHECK-NOT: NonLiteralConstexpr2vm
+
+// We must emit a constant initializer for NonLiteralConstexpr::b, but also
+// emit an initializer to register its destructor.
+// CHECK: define {{.*}}cxx_global_var_init{{.*}}
+// CHECK-NOT: NonLiteralConstexpr
+// CHECK: call {{.*}}cxa_atexit{{.*}} @_ZN19NonLiteralConstexpr4BothD1Ev {{.*}} @_ZN19NonLiteralConstexpr1bE
+// CHECK-NEXT: ret void
+
+// CHECK: define {{.*}}NonLiteralConstexpr10StaticVars
+// CHECK-NOT: }
+// CHECK: call {{.*}}cxa_atexit{{.*}}@_ZN19NonLiteralConstexpr14NonTrivialDtorD1Ev
+// CHECK-NOT: }
+// CHECK: call {{.*}}cxa_atexit{{.*}}@_ZN19NonLiteralConstexpr4BothD1Ev
+
 namespace CrossFuncLabelDiff {
   // Make sure we refuse to constant-fold the variable b.
   constexpr long a(bool x) { return x ? 0 : (long)&&lbl + (0 && ({lbl: 0;})); }