diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 860eeb1a10b91a..dfa9444c59aa97 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 &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 46ed90a20264fb..aa9c8f96f9664e 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(); - const CXXRecordDecl *RD = cast(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 abbc34c88719ef..a1f7806877c524 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 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 90ccd7c055a5d3..c1429492a51ae4 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 struct X {}; + + const int a = a + 1; // expected-warning {{uninitialized}} expected-note {{here}} expected-note 0-1{{outside its lifetime}} + X 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@-2 {{read of non-const variable}} expected-note@-2 {{declared here}} +#else + // expected-note@-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 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@-2 {{read of non-const variable}} expected-note@-2 {{declared here}} +#else + // expected-note@-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 87fb195604d3b9..34dd741bdc242d 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 af46a92c7dc2db..f47707555098b3 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 5584b4b55614b6..32fc7f3eec99ec 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@-1 {{required by 'require_constant_initialization' attribute here}} -// expected-note@-2 {{non-constexpr constructor 'PODType' cannot be used in a constant expression}} +// expected-note@-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@-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@-2 {{variable does not have a constant initializer}} -// expected-note@-3 {{non-constexpr constructor 'PODType' cannot be used in a constant expression}} +// expected-note@-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 7d8b67daa40334..44b636a5b80810 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 614b39533dfcd1..5a414799f7552b 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(n); // expected-error {{constant expression}} expected-note {{modification}} + constexpr int n = ++const_cast(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 ebd91b8a3ca392..08c80a0bb7fdb0 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 53271e0da3fae9..0d9ca8c55cd565 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 struct Z : T { }; constexpr int n = Z().c; // expected-error {{constant expression}} expected-note {{non-literal type 'Z'}} -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 916a01e63f11b7..7ada925d2d423e 100644 --- a/clang/test/SemaTemplate/instantiate-self.cpp +++ b/clang/test/SemaTemplate/instantiate-self.cpp @@ -109,7 +109,8 @@ namespace test11 { int k = var; template struct X { - static const int k = X::k; + static const int b = false; + static const int k = X::b ? X::k : 0; }; template const int X::k; int q = X::k; @@ -117,6 +118,7 @@ namespace test11 { template struct Y { static const int k; }; + // OK (but not constant initialization). template const int Y::k = Y::k; int r = Y::k; } diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 1a00b4e06860cb..d4c6175a8c1b59 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -11971,7 +11971,7 @@

C++ defect report implementation status

2026 CD4 Zero-initialization and constexpr - Unknown + Clang 11 2027