[clang] 96c8994 - C++ DR2026: static storage duration variables are not zeroed before

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 6 16:37:33 PST 2020


Author: Richard Smith
Date: 2020-02-06T16:37:22-08:00
New Revision: 96c899449b61b866b583560a49c4627f561336fc

URL: https://github.com/llvm/llvm-project/commit/96c899449b61b866b583560a49c4627f561336fc
DIFF: https://github.com/llvm/llvm-project/commit/96c899449b61b866b583560a49c4627f561336fc.diff

LOG: C++ DR2026: static storage duration variables are not zeroed before
constant initialization.

Removing this zeroing regressed our code generation in a few cases, also
fixed here. We now compute whether a variable has constant destruction
even if it doesn't have a constant initializer, by trying to destroy a
default-initialized value, and skip emitting a trivial default
constructor for a variable even if it has non-trivial (but perhaps
constant) destruction.

Added: 
    

Modified: 
    clang/lib/AST/ExprConstant.cpp
    clang/lib/CodeGen/CGExprConstant.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/CXX/drs/dr20xx.cpp
    clang/test/CXX/special/class.init/class.inhctor.init/p1.cpp
    clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp
    clang/test/SemaCXX/attr-require-constant-initialization.cpp
    clang/test/SemaCXX/constant-expression-cxx11.cpp
    clang/test/SemaCXX/constant-expression-cxx1y.cpp
    clang/test/SemaCXX/constexpr-printing.cpp
    clang/test/SemaCXX/constexpr-value-init.cpp
    clang/test/SemaTemplate/instantiate-self.cpp
    clang/www/cxx_dr_status.html

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 860eeb1a10b9..dfa9444c59aa 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5609,9 +5609,14 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
   }
 
   // Reserve space for the struct members.
-  if (!RD->isUnion() && !Result.hasValue())
-    Result = APValue(APValue::UninitStruct(), RD->getNumBases(),
-                     std::distance(RD->field_begin(), RD->field_end()));
+  if (!Result.hasValue()) {
+    if (!RD->isUnion())
+      Result = APValue(APValue::UninitStruct(), RD->getNumBases(),
+                       std::distance(RD->field_begin(), RD->field_end()));
+    else
+      // A union starts with no active member.
+      Result = APValue((const FieldDecl*)nullptr);
+  }
 
   if (RD->isInvalidDecl()) return false;
   const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
@@ -13909,18 +13914,6 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
     LValue LVal;
     LVal.set(VD);
 
-    // C++11 [basic.start.init]p2:
-    //  Variables with static storage duration or thread storage duration shall
-    //  be zero-initialized before any other initialization takes place.
-    // This behavior is not present in C.
-    if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() &&
-        !DeclTy->isReferenceType()) {
-      ImplicitValueInitExpr VIE(DeclTy);
-      if (!EvaluateInPlace(Value, Info, LVal, &VIE,
-                           /*AllowNonLiteralTypes=*/true))
-        return false;
-    }
-
     if (!EvaluateInPlace(Value, Info, LVal, this,
                          /*AllowNonLiteralTypes=*/true) ||
         EStatus.HasSideEffects)
@@ -13939,14 +13932,16 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
 
 bool VarDecl::evaluateDestruction(
     SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
-  assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() &&
-         "cannot evaluate destruction of non-constant-initialized variable");
-
   Expr::EvalStatus EStatus;
   EStatus.Diag = &Notes;
 
-  // Make a copy of the value for the destructor to mutate.
-  APValue DestroyedValue = *getEvaluatedValue();
+  // Make a copy of the value for the destructor to mutate, if we know it.
+  // Otherwise, treat the value as default-initialized; if the destructor works
+  // anyway, then the destruction is constant (and must be essentially empty).
+  APValue DestroyedValue =
+      (getEvaluatedValue() && !getEvaluatedValue()->isAbsent())
+          ? *getEvaluatedValue()
+          : getDefaultInitValue(getType());
 
   EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression);
   Info.setEvaluatingDecl(this, DestroyedValue,
@@ -13959,8 +13954,6 @@ bool VarDecl::evaluateDestruction(
   LValue LVal;
   LVal.set(this);
 
-  // FIXME: Consider storing whether this variable has constant destruction in
-  // the EvaluatedStmt so that CodeGen can query it.
   if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) ||
       EStatus.HasSideEffects)
     return false;

diff  --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 46ed90a20264..aa9c8f96f966 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1269,19 +1269,7 @@ class ConstExprEmitter :
     if (!E->getConstructor()->isTrivial())
       return nullptr;
 
-    // FIXME: We should not have to call getBaseElementType here.
-    const auto *RT =
-        CGM.getContext().getBaseElementType(Ty)->castAs<RecordType>();
-    const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
-
-    // If the class doesn't have a trivial destructor, we can't emit it as a
-    // constant expr.
-    if (!RD->hasTrivialDestructor())
-      return nullptr;
-
-    // Only copy and default constructors can be trivial.
-
-
+    // Only default and copy/move constructors can be trivial.
     if (E->getNumArgs()) {
       assert(E->getNumArgs() == 1 && "trivial ctor with > 1 argument");
       assert(E->getConstructor()->isCopyOrMoveConstructor() &&

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index abbc34c88719..a1f7806877c5 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -14794,10 +14794,13 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) {
 
   // If the destructor is constexpr, check whether the variable has constant
   // destruction now.
-  if (Destructor->isConstexpr() && VD->getInit() &&
-      !VD->getInit()->isValueDependent() && VD->evaluateValue()) {
+  if (Destructor->isConstexpr()) {
+    bool HasConstantInit = false;
+    if (VD->getInit() && !VD->getInit()->isValueDependent())
+      HasConstantInit = VD->evaluateValue();
     SmallVector<PartialDiagnosticAt, 8> Notes;
-    if (!VD->evaluateDestruction(Notes) && VD->isConstexpr()) {
+    if (!VD->evaluateDestruction(Notes) && VD->isConstexpr() &&
+        HasConstantInit) {
       Diag(VD->getLocation(),
            diag::err_constexpr_var_requires_const_destruction) << VD;
       for (unsigned I = 0, N = Notes.size(); I != N; ++I)

diff  --git a/clang/test/CXX/drs/dr20xx.cpp b/clang/test/CXX/drs/dr20xx.cpp
index 90ccd7c055a5..c1429492a51a 100644
--- a/clang/test/CXX/drs/dr20xx.cpp
+++ b/clang/test/CXX/drs/dr20xx.cpp
@@ -2,12 +2,53 @@
 // RUN:            -Wno-variadic-macros -Wno-c11-extensions
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++2a -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
 
 #if __cplusplus < 201103L
 #define static_assert(...) _Static_assert(__VA_ARGS__)
 #endif
 
+namespace dr2026 { // dr2026: 11
+  template<int> struct X {};
+
+  const int a = a + 1; // expected-warning {{uninitialized}} expected-note {{here}} expected-note 0-1{{outside its lifetime}}
+  X<a> xa; // expected-error {{constant expression}} expected-note {{initializer of 'a'}}
+
+#if __cplusplus >= 201103L
+  constexpr int b = b; // expected-error {{constant expression}} expected-note {{outside its lifetime}}
+  [[clang::require_constant_initialization]] int c = c; // expected-error {{constant initializer}} expected-note {{attribute}}
+#if __cplusplus == 201103L
+  // expected-note at -2 {{read of non-const variable}} expected-note at -2 {{declared here}}
+#else
+  // expected-note at -4 {{outside its lifetime}}
+#endif
+#endif
+
+#if __cplusplus > 201703L
+  constinit int d = d; // expected-error {{constant initializer}} expected-note {{outside its lifetime}} expected-note {{'constinit'}}
+#endif
+
+  void f() {
+    static const int e = e + 1; // expected-warning {{suspicious}} expected-note {{here}} expected-note 0-1{{outside its lifetime}}
+    X<e> xe; // expected-error {{constant expression}} expected-note {{initializer of 'e'}}
+
+#if __cplusplus >= 201103L
+    static constexpr int f = f; // expected-error {{constant expression}} expected-note {{outside its lifetime}}
+    [[clang::require_constant_initialization]] static int g = g; // expected-error {{constant initializer}} expected-note {{attribute}}
+#if __cplusplus == 201103L
+    // expected-note at -2 {{read of non-const variable}} expected-note at -2 {{declared here}}
+#else
+    // expected-note at -4 {{outside its lifetime}}
+#endif
+#endif
+
+#if __cplusplus > 201703L
+    static constinit int h = h; // expected-error {{constant initializer}} expected-note {{outside its lifetime}} expected-note {{'constinit'}}
+#endif
+  }
+}
+
 namespace dr2083 { // dr2083: partial
 #if __cplusplus >= 201103L
   void non_const_mem_ptr() {

diff  --git a/clang/test/CXX/special/class.init/class.inhctor.init/p1.cpp b/clang/test/CXX/special/class.init/class.inhctor.init/p1.cpp
index 87fb195604d3..34dd741bdc24 100644
--- a/clang/test/CXX/special/class.init/class.inhctor.init/p1.cpp
+++ b/clang/test/CXX/special/class.init/class.inhctor.init/p1.cpp
@@ -102,7 +102,14 @@ namespace constexpr_init_order {
   };
 
   struct B : A { B(); using A::A; int b = 2; };
-  extern const B b;
+
+  // Construct a situation where a value can be observed to change during
+  // constant evaluation in C++11: value-initialization of Wrap2 performs
+  // zero-initialization and then calls the constructor.
+  struct Wrap1 : B { constexpr Wrap1(); };
+  struct Wrap2 : Wrap1 {};
+
+  extern const Wrap2 b;
 
   struct Param {
     constexpr Param(int c) : n(4 * b.a + b.b + c) {}
@@ -111,7 +118,9 @@ namespace constexpr_init_order {
 
   constexpr A::A(Param p) : a(p.n) {}
 
-  constexpr B b(1);
+  constexpr Wrap1::Wrap1() : B(1) {}
+
+  constexpr Wrap2 b = {};
   constexpr B c(1);
   static_assert(b.a == 1, "p should be initialized before B() is executed");
   static_assert(c.a == 7, "b not initialized properly");

diff  --git a/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp b/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp
index af46a92c7dc2..f47707555098 100644
--- a/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp
+++ b/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp
@@ -50,7 +50,7 @@ int d_init();
 thread_local int d = d_init();
 
 struct Destructed {
-  int n;
+  int n = 0;
   ~Destructed();
 };
 

diff  --git a/clang/test/SemaCXX/attr-require-constant-initialization.cpp b/clang/test/SemaCXX/attr-require-constant-initialization.cpp
index 5584b4b55614..32fc7f3eec99 100644
--- a/clang/test/SemaCXX/attr-require-constant-initialization.cpp
+++ b/clang/test/SemaCXX/attr-require-constant-initialization.cpp
@@ -10,7 +10,7 @@
 int ReturnInt(); // expected-note 0+ {{declared here}}
 
 struct PODType { // expected-note 0+ {{declared here}}
-  int value;
+  int value; // expected-note 0-2 {{declared here}}
   int value2;
 };
 
@@ -152,7 +152,7 @@ void test_basic_start_static_2_2() {
 #else
   ATTR static PODType pod; // expected-error {{variable does not have a constant initializer}}
 // expected-note at -1 {{required by 'require_constant_initialization' attribute here}}
-// expected-note at -2 {{non-constexpr constructor 'PODType' cannot be used in a constant expression}}
+// expected-note at -2 {{subobject of type 'int' is not initialized}}
 #endif
   ATTR static PODType pot2 = {ReturnInt()}; // expected-error {{variable does not have a constant initializer}}
                                             // expected-note at -1 {{required by 'require_constant_initialization' attribute here}}
@@ -191,7 +191,7 @@ struct TT2 {
 PODType TT2::pod_noinit; // expected-note 0+ {{declared here}}
 #if __cplusplus >= 201103L
 // expected-error at -2 {{variable does not have a constant initializer}}
-// expected-note at -3 {{non-constexpr constructor 'PODType' cannot be used in a constant expression}}
+// expected-note at -3 {{subobject of type 'int' is not initialized}}
 #endif
 PODType TT2::pod_copy_init(TT2::pod_noinit); // expected-error {{variable does not have a constant initializer}}
 #if __cplusplus >= 201103L

diff  --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp
index 7d8b67daa403..44b636a5b808 100644
--- a/clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp
@@ -603,9 +603,9 @@ namespace CopyCtor {
 }
 
 constexpr int selfref[2][2][2] = {
-  selfref[1][1][1] + 1, selfref[0][0][0] + 1,
-  selfref[1][0][1] + 1, selfref[0][1][0] + 1,
-  selfref[1][0][0] + 1, selfref[0][1][1] + 1 };
+  1, selfref[0][0][0] + 1,
+  1, selfref[0][1][0] + 1,
+  1, selfref[0][1][1] + 1 };
 static_assert(selfref[0][0][0] == 1, "");
 static_assert(selfref[0][0][1] == 2, "");
 static_assert(selfref[0][1][0] == 1, "");
@@ -615,6 +615,10 @@ static_assert(selfref[1][0][1] == 3, "");
 static_assert(selfref[1][1][0] == 0, "");
 static_assert(selfref[1][1][1] == 0, "");
 
+constexpr int badselfref[2][2][2] = { // expected-error {{constant expression}}
+  badselfref[1][0][0] // expected-note {{outside its lifetime}}
+};
+
 struct TrivialDefCtor { int n; };
 typedef TrivialDefCtor TDCArray[2][2];
 static_assert(TDCArray{}[1][1].n == 0, "");
@@ -1277,18 +1281,16 @@ namespace ExternConstexpr {
   }
 
   extern const int q;
-  constexpr int g() { return q; }
-  constexpr int q = g();
-  static_assert(q == 0, "zero-initialization should precede static initialization");
+  constexpr int g() { return q; } // expected-note {{outside its lifetime}}
+  constexpr int q = g(); // expected-error {{constant expression}} expected-note {{in call}}
 
   extern int r; // expected-note {{here}}
   constexpr int h() { return r; } // expected-error {{never produces a constant}} expected-note {{read of non-const}}
 
   struct S { int n; };
   extern const S s;
-  constexpr int x() { return s.n; }
-  constexpr S s = {x()};
-  static_assert(s.n == 0, "zero-initialization should precede static initialization");
+  constexpr int x() { return s.n; } // expected-note {{outside its lifetime}}
+  constexpr S s = {x()}; // expected-error {{constant expression}} expected-note {{in call}}
 }
 
 namespace ComplexConstexpr {
@@ -1955,11 +1957,10 @@ namespace Lifetime {
 
   struct R { // expected-note {{field init}}
     struct Inner { constexpr int f() const { return 0; } };
-    int a = b.f(); // expected-warning {{uninitialized}} expected-note {{member call on object outside its lifetime}}
+    int a = b.f(); // expected-warning {{uninitialized}} expected-note 2{{member call on object outside its lifetime}}
     Inner b;
   };
-  // FIXME: This should be rejected under DR2026.
-  constexpr R r; // expected-note {{default constructor}}
+  constexpr R r; // expected-error {{constant expression}} expected-note {{in call}} expected-note {{implicit default constructor for 'Lifetime::R' first required here}}
   void rf() {
     constexpr R r; // expected-error {{constant expression}} expected-note {{in call}}
   }

diff  --git a/clang/test/SemaCXX/constant-expression-cxx1y.cpp b/clang/test/SemaCXX/constant-expression-cxx1y.cpp
index 614b39533dfc..5a414799f755 100644
--- a/clang/test/SemaCXX/constant-expression-cxx1y.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx1y.cpp
@@ -1211,7 +1211,7 @@ namespace ObjectsUnderConstruction {
   static_assert(aggr2.x == 1 && aggr2.y == 1, "");
 
   // The lifetime of 'n' begins at the initialization, not before.
-  constexpr int n = ++const_cast<int&>(n); // expected-error {{constant expression}} expected-note {{modification}}
+  constexpr int n = ++const_cast<int&>(n); // expected-error {{constant expression}} expected-note {{increment of object outside its lifetime}}
 }
 
 namespace PR39728 {

diff  --git a/clang/test/SemaCXX/constexpr-printing.cpp b/clang/test/SemaCXX/constexpr-printing.cpp
index ebd91b8a3ca3..08c80a0bb7fd 100644
--- a/clang/test/SemaCXX/constexpr-printing.cpp
+++ b/clang/test/SemaCXX/constexpr-printing.cpp
@@ -11,7 +11,6 @@ struct S {
 
 constexpr int extract(const S &s) { return s.n; } // expected-note {{read of object outside its lifetime is not allowed in a constant expression}}
 
-constexpr S s1; // ok
 void f() {
   constexpr S s1; // expected-error {{constant expression}} expected-note {{in call to 'S()'}}
   constexpr S s2(10);

diff  --git a/clang/test/SemaCXX/constexpr-value-init.cpp b/clang/test/SemaCXX/constexpr-value-init.cpp
index 53271e0da3fa..0d9ca8c55cd5 100644
--- a/clang/test/SemaCXX/constexpr-value-init.cpp
+++ b/clang/test/SemaCXX/constexpr-value-init.cpp
@@ -1,20 +1,21 @@
 // RUN: %clang_cc1 %s -Wno-uninitialized -std=c++11 -fsyntax-only -verify
 
 struct A {
-  constexpr A() : a(b + 1), b(a + 1) {} // expected-note {{outside its lifetime}}
+  constexpr A() : a(b + 1), b(a + 1) {} // expected-note 5{{outside its lifetime}}
   int a;
   int b;
 };
-struct B {
+struct B { // expected-note {{in call to 'A()'}}
   A a;
 };
 
-constexpr A a; // ok, zero initialization precedes static initialization
+constexpr A a1; // expected-error {{constant expression}} expected-note {{in call to 'A()'}}
+constexpr A a2 = A(); // expected-error {{constant expression}} expected-note {{in call to 'A()'}}
 void f() {
   constexpr A a; // expected-error {{constant expression}} expected-note {{in call to 'A()'}}
 }
 
-constexpr B b1; // ok
+constexpr B b1; // expected-error {{constant expression}} expected-note {{in call to 'B()'}}
 constexpr B b2 = B(); // ok
 static_assert(b2.a.a == 1, "");
 static_assert(b2.a.b == 2, "");
@@ -36,11 +37,12 @@ template<typename T> struct Z : T {
 };
 constexpr int n = Z<V>().c; // expected-error {{constant expression}} expected-note {{non-literal type 'Z<V>'}}
 
-struct E {
+struct E { // expected-note {{in call to 'A()'}}
   A a[2];
 };
-constexpr E e; // ok
-static_assert(e.a[0].a == 1, "");
-static_assert(e.a[0].b == 2, "");
-static_assert(e.a[1].a == 1, "");
-static_assert(e.a[1].b == 2, "");
+constexpr E e1; // expected-error {{constant expression}} expected-note {{in call to 'E()'}}
+constexpr E e2 = E();
+static_assert(e2.a[0].a == 1, "");
+static_assert(e2.a[0].b == 2, "");
+static_assert(e2.a[1].a == 1, "");
+static_assert(e2.a[1].b == 2, "");

diff  --git a/clang/test/SemaTemplate/instantiate-self.cpp b/clang/test/SemaTemplate/instantiate-self.cpp
index 916a01e63f11..7ada925d2d42 100644
--- a/clang/test/SemaTemplate/instantiate-self.cpp
+++ b/clang/test/SemaTemplate/instantiate-self.cpp
@@ -109,7 +109,8 @@ namespace test11 {
   int k = var<int>;
 
   template<typename T> struct X {
-    static const int k = X<T>::k;
+    static const int b = false;
+    static const int k = X<T>::b ? X<T>::k : 0;
   };
   template<typename T> const int X<T>::k;
   int q = X<int>::k;
@@ -117,6 +118,7 @@ namespace test11 {
   template<typename T> struct Y {
     static const int k;
   };
+  // OK (but not constant initialization).
   template<typename T> const int Y<T>::k = Y<T>::k;
   int r = Y<int>::k;
 }

diff  --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 1a00b4e06860..d4c6175a8c1b 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -11971,7 +11971,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg2026">2026</a></td>
     <td>CD4</td>
     <td>Zero-initialization and <TT>constexpr</TT></td>
-    <td class="none" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 11</td>
   </tr>
   <tr id="2027">
     <td><a href="https://wg21.link/cwg2027">2027</a></td>


        


More information about the cfe-commits mailing list