[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