<div dir="ltr">Hi Richard,<div><br></div><div>I have a question about this commit. It looks like it started emitting `cxa_guard_acquire/release` calls in Objective-C code, for globals with constant initializers, causing link failures for things that don't link with libc++abi. Was that intentional? I'm still working on a reduced test-case that I can post.</div><div><br></div><div>Thanks,</div><div>Alex</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, 28 Sep 2019 at 22:06, Richard Smith via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">Author: rsmith<br>
Date: Sat Sep 28 22:08:46 2019<br>
New Revision: 373159<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=373159&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=373159&view=rev</a><br>
Log:<br>
For P0784R7: compute whether a variable has constant destruction if it<br>
has a constexpr destructor.<br>
<br>
For constexpr variables, reject if the variable does not have constant<br>
destruction. In all cases, do not emit runtime calls to the destructor<br>
for variables with constant destruction.<br>
<br>
Added:<br>
cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp<br>
cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp<br>
Modified:<br>
cfe/trunk/include/clang/AST/Decl.h<br>
cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td<br>
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
cfe/trunk/lib/AST/ASTContext.cpp<br>
cfe/trunk/lib/AST/Decl.cpp<br>
cfe/trunk/lib/AST/ExprConstant.cpp<br>
cfe/trunk/lib/AST/Interp/Interp.cpp<br>
cfe/trunk/lib/AST/TextNodeDumper.cpp<br>
cfe/trunk/lib/CodeGen/CGCall.cpp<br>
cfe/trunk/lib/CodeGen/CGClass.cpp<br>
cfe/trunk/lib/CodeGen/CGDecl.cpp<br>
cfe/trunk/lib/CodeGen/CGDeclCXX.cpp<br>
cfe/trunk/lib/CodeGen/CodeGenModule.cpp<br>
cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp<br>
cfe/trunk/lib/Sema/SemaDeclCXX.cpp<br>
cfe/trunk/lib/Serialization/ASTReaderDecl.cpp<br>
cfe/trunk/lib/Serialization/ASTWriterDecl.cpp<br>
cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp<br>
cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp<br>
cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp<br>
cfe/trunk/test/CodeGenCXX/no_destroy.cpp<br>
<br>
Modified: cfe/trunk/include/clang/AST/Decl.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/AST/Decl.h (original)<br>
+++ cfe/trunk/include/clang/AST/Decl.h Sat Sep 28 22:08:46 2019<br>
@@ -808,12 +808,19 @@ struct EvaluatedStmt {<br>
/// valid if CheckedICE is true.<br>
bool IsICE : 1;<br>
<br>
+ /// Whether this variable is known to have constant destruction. That is,<br>
+ /// whether running the destructor on the initial value is a side-effect<br>
+ /// (and doesn't inspect any state that might have changed during program<br>
+ /// execution). This is currently only computed if the destructor is<br>
+ /// non-trivial.<br>
+ bool HasConstantDestruction : 1;<br>
+<br>
Stmt *Value;<br>
APValue Evaluated;<br>
<br>
- EvaluatedStmt() : WasEvaluated(false), IsEvaluating(false), CheckedICE(false),<br>
- CheckingICE(false), IsICE(false) {}<br>
-<br>
+ EvaluatedStmt()<br>
+ : WasEvaluated(false), IsEvaluating(false), CheckedICE(false),<br>
+ CheckingICE(false), IsICE(false), HasConstantDestruction(false) {}<br>
};<br>
<br>
/// Represents a variable declaration or definition.<br>
@@ -1267,6 +1274,14 @@ public:<br>
/// to untyped APValue if the value could not be evaluated.<br>
APValue *getEvaluatedValue() const;<br>
<br>
+ /// Evaluate the destruction of this variable to determine if it constitutes<br>
+ /// constant destruction.<br>
+ ///<br>
+ /// \pre isInitICE()<br>
+ /// \return \c true if this variable has constant destruction, \c false if<br>
+ /// not.<br>
+ bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes) const;<br>
+<br>
/// Determines whether it is already known whether the<br>
/// initializer is an integral constant expression or not.<br>
bool isInitKnownICE() const;<br>
@@ -1505,9 +1520,14 @@ public:<br>
// has no definition within this source file.<br>
bool isKnownToBeDefined() const;<br>
<br>
- /// Do we need to emit an exit-time destructor for this variable?<br>
+ /// Is destruction of this variable entirely suppressed? If so, the variable<br>
+ /// need not have a usable destructor at all.<br>
bool isNoDestroy(const ASTContext &) const;<br>
<br>
+ /// Do we need to emit an exit-time destructor for this variable, and if so,<br>
+ /// what kind?<br>
+ QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const;<br>
+<br>
// Implement isa/cast/dyncast/etc.<br>
static bool classof(const Decl *D) { return classofKind(D->getKind()); }<br>
static bool classofKind(Kind K) { return K >= firstVar && K <= lastVar; }<br>
<br>
Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)<br>
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Sat Sep 28 22:08:46 2019<br>
@@ -145,8 +145,10 @@ def note_constexpr_access_volatile_obj :<br>
"a constant expression">;<br>
def note_constexpr_volatile_here : Note<<br>
"volatile %select{temporary created|object declared|member declared}0 here">;<br>
-def note_constexpr_ltor_mutable : Note<<br>
- "read of mutable member %0 is not allowed in a constant expression">;<br>
+def note_constexpr_access_mutable : Note<<br>
+ "%select{read of|read of|assignment to|increment of|decrement of|"<br>
+ "member call on|dynamic_cast of|typeid applied to|destruction of}0 "<br>
+ "mutable member %1 is not allowed in a constant expression">;<br>
def note_constexpr_ltor_non_const_int : Note<<br>
"read of non-const variable %0 is not allowed in a constant expression">;<br>
def note_constexpr_ltor_non_constexpr : Note<<br>
<br>
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)<br>
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Sat Sep 28 22:08:46 2019<br>
@@ -2384,6 +2384,8 @@ def err_constexpr_var_non_literal : Erro<br>
"constexpr variable cannot have non-literal type %0">;<br>
def err_constexpr_var_requires_const_init : Error<<br>
"constexpr variable %0 must be initialized by a constant expression">;<br>
+def err_constexpr_var_requires_const_destruction : Error<<br>
+ "constexpr variable %0 must have constant destruction">;<br>
def err_constexpr_redecl_mismatch : Error<<br>
"%select{non-constexpr|constexpr|consteval}1 declaration of %0"<br>
" follows %select{non-constexpr|constexpr|consteval}2 declaration">;<br>
<br>
Modified: cfe/trunk/lib/AST/ASTContext.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/ASTContext.cpp (original)<br>
+++ cfe/trunk/lib/AST/ASTContext.cpp Sat Sep 28 22:08:46 2019<br>
@@ -10064,7 +10064,7 @@ bool ASTContext::DeclMustBeEmitted(const<br>
return false;<br>
<br>
// Variables that have destruction with side-effects are required.<br>
- if (VD->getType().isDestructedType())<br>
+ if (VD->needsDestruction(*this))<br>
return true;<br>
<br>
// Variables that have initialization with side-effects are required.<br>
<br>
Modified: cfe/trunk/lib/AST/Decl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/Decl.cpp (original)<br>
+++ cfe/trunk/lib/AST/Decl.cpp Sat Sep 28 22:08:46 2019<br>
@@ -2592,6 +2592,18 @@ bool VarDecl::isNoDestroy(const ASTConte<br>
!hasAttr<AlwaysDestroyAttr>()));<br>
}<br>
<br>
+QualType::DestructionKind<br>
+VarDecl::needsDestruction(const ASTContext &Ctx) const {<br>
+ if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())<br>
+ if (Eval->HasConstantDestruction)<br>
+ return QualType::DK_none;<br>
+<br>
+ if (isNoDestroy(Ctx))<br>
+ return QualType::DK_none;<br>
+<br>
+ return getType().isDestructedType();<br>
+}<br>
+<br>
MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const {<br>
if (isStaticDataMember())<br>
// FIXME: Remove ?<br>
<br>
Modified: cfe/trunk/lib/AST/ExprConstant.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)<br>
+++ cfe/trunk/lib/AST/ExprConstant.cpp Sat Sep 28 22:08:46 2019<br>
@@ -744,6 +744,15 @@ namespace {<br>
/// evaluated, if any.<br>
APValue::LValueBase EvaluatingDecl;<br>
<br>
+ enum class EvaluatingDeclKind {<br>
+ None,<br>
+ /// We're evaluating the construction of EvaluatingDecl.<br>
+ Ctor,<br>
+ /// We're evaluating the destruction of EvaluatingDecl.<br>
+ Dtor,<br>
+ };<br>
+ EvaluatingDeclKind IsEvaluatingDecl = EvaluatingDeclKind::None;<br>
+<br>
/// EvaluatingDeclValue - This is the value being constructed for the<br>
/// declaration whose initializer is being evaluated, if any.<br>
APValue *EvaluatingDeclValue;<br>
@@ -902,8 +911,10 @@ namespace {<br>
discardCleanups();<br>
}<br>
<br>
- void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {<br>
+ void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,<br>
+ EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {<br>
EvaluatingDecl = Base;<br>
+ IsEvaluatingDecl = EDK;<br>
EvaluatingDeclValue = &Value;<br>
}<br>
<br>
@@ -2913,8 +2924,8 @@ static bool isReadByLvalueToRvalueConver<br>
<br>
/// Diagnose an attempt to read from any unreadable field within the specified<br>
/// type, which might be a class type.<br>
-static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,<br>
- QualType T) {<br>
+static bool diagnoseMutableFields(EvalInfo &Info, const Expr *E, AccessKinds AK,<br>
+ QualType T) {<br>
CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();<br>
if (!RD)<br>
return false;<br>
@@ -2929,17 +2940,17 @@ static bool diagnoseUnreadableFields(Eva<br>
// FIXME: Add core issue number for the union case.<br>
if (Field->isMutable() &&<br>
(RD->isUnion() || isReadByLvalueToRvalueConversion(Field->getType()))) {<br>
- Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field;<br>
+ Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) << AK << Field;<br>
Info.Note(Field->getLocation(), diag::note_declared_at);<br>
return true;<br>
}<br>
<br>
- if (diagnoseUnreadableFields(Info, E, Field->getType()))<br>
+ if (diagnoseMutableFields(Info, E, AK, Field->getType()))<br>
return true;<br>
}<br>
<br>
for (auto &BaseSpec : RD->bases())<br>
- if (diagnoseUnreadableFields(Info, E, BaseSpec.getType()))<br>
+ if (diagnoseMutableFields(Info, E, AK, BaseSpec.getType()))<br>
return true;<br>
<br>
// All mutable fields were empty, and thus not actually read.<br>
@@ -2947,7 +2958,8 @@ static bool diagnoseUnreadableFields(Eva<br>
}<br>
<br>
static bool lifetimeStartedInEvaluation(EvalInfo &Info,<br>
- APValue::LValueBase Base) {<br>
+ APValue::LValueBase Base,<br>
+ bool MutableSubobject = false) {<br>
// A temporary we created.<br>
if (Base.getCallIndex())<br>
return true;<br>
@@ -2956,19 +2968,42 @@ static bool lifetimeStartedInEvaluation(<br>
if (!Evaluating)<br>
return false;<br>
<br>
- // The variable whose initializer we're evaluating.<br>
- if (auto *BaseD = Base.dyn_cast<const ValueDecl*>())<br>
- if (declaresSameEntity(Evaluating, BaseD))<br>
- return true;<br>
+ auto *BaseD = Base.dyn_cast<const ValueDecl*>();<br>
<br>
- // A temporary lifetime-extended by the variable whose initializer we're<br>
- // evaluating.<br>
- if (auto *BaseE = Base.dyn_cast<const Expr *>())<br>
- if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))<br>
- if (declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating))<br>
- return true;<br>
+ switch (Info.IsEvaluatingDecl) {<br>
+ case EvalInfo::EvaluatingDeclKind::None:<br>
+ return false;<br>
<br>
- return false;<br>
+ case EvalInfo::EvaluatingDeclKind::Ctor:<br>
+ // The variable whose initializer we're evaluating.<br>
+ if (BaseD)<br>
+ return declaresSameEntity(Evaluating, BaseD);<br>
+<br>
+ // A temporary lifetime-extended by the variable whose initializer we're<br>
+ // evaluating.<br>
+ if (auto *BaseE = Base.dyn_cast<const Expr *>())<br>
+ if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))<br>
+ return declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating);<br>
+ return false;<br>
+<br>
+ case EvalInfo::EvaluatingDeclKind::Dtor:<br>
+ // C++2a [expr.const]p6:<br>
+ // [during constant destruction] the lifetime of a and its non-mutable<br>
+ // subobjects (but not its mutable subobjects) [are] considered to start<br>
+ // within e.<br>
+ //<br>
+ // FIXME: We can meaningfully extend this to cover non-const objects, but<br>
+ // we will need special handling: we should be able to access only<br>
+ // subobjects of such objects that are themselves declared const.<br>
+ if (!BaseD ||<br>
+ !(BaseD->getType().isConstQualified() ||<br>
+ BaseD->getType()->isReferenceType()) ||<br>
+ MutableSubobject)<br>
+ return false;<br>
+ return declaresSameEntity(Evaluating, BaseD);<br>
+ }<br>
+<br>
+ llvm_unreachable("unknown evaluating decl kind");<br>
}<br>
<br>
namespace {<br>
@@ -2986,13 +3021,13 @@ struct CompleteObject {<br>
CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type)<br>
: Base(Base), Value(Value), Type(Type) {}<br>
<br>
- bool mayReadMutableMembers(EvalInfo &Info) const {<br>
+ bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) const {<br>
// In C++14 onwards, it is permitted to read a mutable member whose<br>
// lifetime began within the evaluation.<br>
// FIXME: Should we also allow this in C++11?<br>
if (!Info.getLangOpts().CPlusPlus14)<br>
return false;<br>
- return lifetimeStartedInEvaluation(Info, Base);<br>
+ return lifetimeStartedInEvaluation(Info, Base, /*MutableSubobject*/true);<br>
}<br>
<br>
explicit operator bool() const { return !Type.isNull(); }<br>
@@ -3097,9 +3132,9 @@ findSubobject(EvalInfo &Info, const Expr<br>
// things we need to check: if there are any mutable subobjects, we<br>
// cannot perform this read. (This only happens when performing a trivial<br>
// copy or assignment.)<br>
- if (ObjType->isRecordType() && isRead(handler.AccessKind) &&<br>
- !Obj.mayReadMutableMembers(Info) &&<br>
- diagnoseUnreadableFields(Info, E, ObjType))<br>
+ if (ObjType->isRecordType() &&<br>
+ !Obj.mayAccessMutableMembers(Info, handler.AccessKind) &&<br>
+ diagnoseMutableFields(Info, E, handler.AccessKind, ObjType))<br>
return handler.failed();<br>
}<br>
<br>
@@ -3167,10 +3202,10 @@ findSubobject(EvalInfo &Info, const Expr<br>
: O->getComplexFloatReal(), ObjType);<br>
}<br>
} else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {<br>
- if (Field->isMutable() && isRead(handler.AccessKind) &&<br>
- !Obj.mayReadMutableMembers(Info)) {<br>
- Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1)<br>
- << Field;<br>
+ if (Field->isMutable() &&<br>
+ !Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {<br>
+ Info.FFDiag(E, diag::note_constexpr_access_mutable, 1)<br>
+ << handler.AccessKind << Field;<br>
Info.Note(Field->getLocation(), diag::note_declared_at);<br>
return handler.failed();<br>
}<br>
@@ -3427,8 +3462,7 @@ static CompleteObject findCompleteObject<br>
// the variable we're reading must be const.<br>
if (!Frame) {<br>
if (Info.getLangOpts().CPlusPlus14 &&<br>
- declaresSameEntity(<br>
- VD, Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) {<br>
+ lifetimeStartedInEvaluation(Info, LVal.Base)) {<br>
// OK, we can read and modify an object if we're in the process of<br>
// evaluating its initializer, because its lifetime began in this<br>
// evaluation.<br>
@@ -3518,11 +3552,14 @@ static CompleteObject findCompleteObject<br>
// int x = ++r;<br>
// constexpr int k = r;<br>
// Therefore we use the C++14 rules in C++11 too.<br>
- const ValueDecl *VD = Info.EvaluatingDecl.dyn_cast<const ValueDecl*>();<br>
- const ValueDecl *ED = MTE->getExtendingDecl();<br>
+ //<br>
+ // Note that temporaries whose lifetimes began while evaluating a<br>
+ // variable's constructor are not usable while evaluating the<br>
+ // corresponding destructor, not even if they're of const-qualified<br>
+ // types.<br>
if (!(BaseType.isConstQualified() &&<br>
BaseType->isIntegralOrEnumerationType()) &&<br>
- !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) {<br>
+ !lifetimeStartedInEvaluation(Info, LVal.Base)) {<br>
if (!IsAccess)<br>
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);<br>
Info.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;<br>
@@ -13282,6 +13319,41 @@ bool Expr::EvaluateAsInitializer(APValue<br>
CheckMemoryLeaks(Info);<br>
}<br>
<br>
+bool VarDecl::evaluateDestruction(<br>
+ SmallVectorImpl<PartialDiagnosticAt> &Notes) const {<br>
+ assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() &&<br>
+ "cannot evaluate destruction of non-constant-initialized variable");<br>
+<br>
+ Expr::EvalStatus EStatus;<br>
+ EStatus.Diag = &Notes;<br>
+<br>
+ // Make a copy of the value for the destructor to mutate.<br>
+ APValue DestroyedValue = *getEvaluatedValue();<br>
+<br>
+ EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression);<br>
+ Info.setEvaluatingDecl(this, DestroyedValue,<br>
+ EvalInfo::EvaluatingDeclKind::Dtor);<br>
+ Info.InConstantContext = true;<br>
+<br>
+ SourceLocation DeclLoc = getLocation();<br>
+ QualType DeclTy = getType();<br>
+<br>
+ LValue LVal;<br>
+ LVal.set(this);<br>
+<br>
+ // FIXME: Consider storing whether this variable has constant destruction in<br>
+ // the EvaluatedStmt so that CodeGen can query it.<br>
+ if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) ||<br>
+ EStatus.HasSideEffects)<br>
+ return false;<br>
+<br>
+ if (!Info.discardCleanups())<br>
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");<br>
+<br>
+ ensureEvaluatedStmt()->HasConstantDestruction = true;<br>
+ return true;<br>
+}<br>
+<br>
/// isEvaluatable - Call EvaluateAsRValue to see if this expression can be<br>
/// constant folded, but discard the result.<br>
bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK) const {<br>
<br>
Modified: cfe/trunk/lib/AST/Interp/Interp.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Interp/Interp.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Interp/Interp.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/Interp/Interp.cpp (original)<br>
+++ cfe/trunk/lib/AST/Interp/Interp.cpp Sat Sep 28 22:08:46 2019<br>
@@ -275,7 +275,7 @@ bool CheckMutable(InterpState &S, CodePt<br>
<br>
const SourceInfo &Loc = S.Current->getSource(OpPC);<br>
const FieldDecl *Field = Ptr.getField();<br>
- S.FFDiag(Loc, diag::note_constexpr_ltor_mutable, 1) << Field;<br>
+ S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field;<br>
S.Note(Field->getLocation(), diag::note_declared_at);<br>
return false;<br>
}<br>
<br>
Modified: cfe/trunk/lib/AST/TextNodeDumper.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TextNodeDumper.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TextNodeDumper.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/TextNodeDumper.cpp (original)<br>
+++ cfe/trunk/lib/AST/TextNodeDumper.cpp Sat Sep 28 22:08:46 2019<br>
@@ -1384,6 +1384,8 @@ void TextNodeDumper::VisitVarDecl(const<br>
break;<br>
}<br>
}<br>
+ if (D->needsDestruction(D->getASTContext()))<br>
+ OS << " destroyed";<br>
if (D->isParameterPack())<br>
OS << " pack";<br>
}<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGCall.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGCall.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGCall.cpp Sat Sep 28 22:08:46 2019<br>
@@ -3093,7 +3093,7 @@ void CodeGenFunction::EmitDelegateCallAr<br>
// Deactivate the cleanup for the callee-destructed param that was pushed.<br>
if (hasAggregateEvaluationKind(type) && !CurFuncIsThunk &&<br>
type->getAs<RecordType>()->getDecl()->isParamDestroyedInCallee() &&<br>
- type.isDestructedType()) {<br>
+ param->needsDestruction(getContext())) {<br>
EHScopeStack::stable_iterator cleanup =<br>
CalleeDestructedParamCleanups.lookup(cast<ParmVarDecl>(param));<br>
assert(cleanup.isValid() &&<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGClass.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Sat Sep 28 22:08:46 2019<br>
@@ -2083,7 +2083,7 @@ static bool canEmitDelegateCallArgs(Code<br>
if (CGF.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {<br>
// If the parameters are callee-cleanup, it's not safe to forward.<br>
for (auto *P : Ctor->parameters())<br>
- if (P->getType().isDestructedType())<br>
+ if (P->needsDestruction(CGF.getContext()))<br>
return false;<br>
<br>
// Likewise if they're inalloca.<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGDecl.cpp Sat Sep 28 22:08:46 2019<br>
@@ -305,14 +305,6 @@ llvm::Constant *CodeGenModule::getOrCrea<br>
return Addr;<br>
}<br>
<br>
-/// hasNontrivialDestruction - Determine whether a type's destruction is<br>
-/// non-trivial. If so, and the variable uses static initialization, we must<br>
-/// register its destructor to run on exit.<br>
-static bool hasNontrivialDestruction(QualType T) {<br>
- CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();<br>
- return RD && !RD->hasTrivialDestructor();<br>
-}<br>
-<br>
/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the<br>
/// global variable that has already been created for it. If the initializer<br>
/// has a different type than GV does, this may free GV and return a different<br>
@@ -372,7 +364,7 @@ CodeGenFunction::AddInitializerToStaticV<br>
<br>
emitter.finalize(GV);<br>
<br>
- if (hasNontrivialDestruction(D.getType()) && HaveInsertPoint()) {<br>
+ if (D.needsDestruction(getContext()) && HaveInsertPoint()) {<br>
// We have a constant initializer, but a nontrivial destructor. We still<br>
// need to perform a guarded "initialization" in order to register the<br>
// destructor.<br>
@@ -1994,7 +1986,7 @@ void CodeGenFunction::EmitAutoVarCleanup<br>
const VarDecl &D = *emission.Variable;<br>
<br>
// Check the type for a cleanup.<br>
- if (QualType::DestructionKind dtorKind = D.getType().isDestructedType())<br>
+ if (QualType::DestructionKind dtorKind = D.needsDestruction(getContext()))<br>
emitAutoVarTypeCleanup(emission, dtorKind);<br>
<br>
// In GC mode, honor objc_precise_lifetime.<br>
@@ -2404,7 +2396,8 @@ void CodeGenFunction::EmitParmDecl(const<br>
// cleanup.<br>
if (hasAggregateEvaluationKind(Ty) && !CurFuncIsThunk &&<br>
Ty->getAs<RecordType>()->getDecl()->isParamDestroyedInCallee()) {<br>
- if (QualType::DestructionKind DtorKind = Ty.isDestructedType()) {<br>
+ if (QualType::DestructionKind DtorKind =<br>
+ D.needsDestruction(getContext())) {<br>
assert((DtorKind == QualType::DK_cxx_destructor ||<br>
DtorKind == QualType::DK_nontrivial_c_struct) &&<br>
"unexpected destructor type");<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGDeclCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGDeclCXX.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGDeclCXX.cpp Sat Sep 28 22:08:46 2019<br>
@@ -73,16 +73,10 @@ static void EmitDeclDestroy(CodeGenFunct<br>
// that isn't balanced out by a destructor call as intended by the<br>
// attribute. This also checks for -fno-c++-static-destructors and<br>
// bails even if the attribute is not present.<br>
- if (D.isNoDestroy(CGF.getContext()))<br>
- return;<br>
-<br>
- CodeGenModule &CGM = CGF.CGM;<br>
+ QualType::DestructionKind DtorKind = D.needsDestruction(CGF.getContext());<br>
<br>
// FIXME: __attribute__((cleanup)) ?<br>
<br>
- QualType Type = D.getType();<br>
- QualType::DestructionKind DtorKind = Type.isDestructedType();<br>
-<br>
switch (DtorKind) {<br>
case QualType::DK_none:<br>
return;<br>
@@ -101,6 +95,9 @@ static void EmitDeclDestroy(CodeGenFunct<br>
llvm::FunctionCallee Func;<br>
llvm::Constant *Argument;<br>
<br>
+ CodeGenModule &CGM = CGF.CGM;<br>
+ QualType Type = D.getType();<br>
+<br>
// Special-case non-array C++ destructors, if they have the right signature.<br>
// Under some ABIs, destructors return this instead of void, and cannot be<br>
// passed directly to __cxa_atexit if the target does not allow this<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Sat Sep 28 22:08:46 2019<br>
@@ -3809,9 +3809,9 @@ void CodeGenModule::EmitGlobalVarDefinit<br>
return;<br>
<br>
llvm::Constant *Init = nullptr;<br>
- CXXRecordDecl *RD = ASTTy->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();<br>
bool NeedsGlobalCtor = false;<br>
- bool NeedsGlobalDtor = RD && !RD->hasTrivialDestructor();<br>
+ bool NeedsGlobalDtor =<br>
+ D->needsDestruction(getContext()) == QualType::DK_cxx_destructor;<br>
<br>
const VarDecl *InitDecl;<br>
const Expr *InitExpr = D->getAnyInitializer(InitDecl);<br>
<br>
Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Sat Sep 28 22:08:46 2019<br>
@@ -350,7 +350,7 @@ public:<br>
// If we have the only definition, we don't need a thread wrapper if we<br>
// will emit the value as a constant.<br>
if (isUniqueGVALinkage(getContext().GetGVALinkageForVariable(VD)))<br>
- return !VD->getType().isDestructedType() && InitDecl->evaluateValue();<br>
+ return !VD->needsDestruction(getContext()) && InitDecl->evaluateValue();<br>
<br>
// Otherwise, we need a thread wrapper unless we know that every<br>
// translation unit will emit the value as a constant. We rely on<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Sat Sep 28 22:08:46 2019<br>
@@ -13398,6 +13398,19 @@ void Sema::FinalizeVarWithDestructor(Var<br>
}<br>
<br>
if (Destructor->isTrivial()) return;<br>
+<br>
+ // If the destructor is constexpr, check whether the variable has constant<br>
+ // destruction now.<br>
+ if (Destructor->isConstexpr() && VD->evaluateValue()) {<br>
+ SmallVector<PartialDiagnosticAt, 8> Notes;<br>
+ if (!VD->evaluateDestruction(Notes) && VD->isConstexpr()) {<br>
+ Diag(VD->getLocation(),<br>
+ diag::err_constexpr_var_requires_const_destruction) << VD;<br>
+ for (unsigned I = 0, N = Notes.size(); I != N; ++I)<br>
+ Diag(Notes[I].first, Notes[I].second);<br>
+ }<br>
+ }<br>
+<br>
if (!VD->hasGlobalStorage()) return;<br>
<br>
// Emit warning for non-trivial dtor in global scope (a real global,<br>
<br>
Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Sat Sep 28 22:08:46 2019<br>
@@ -1390,10 +1390,11 @@ ASTDeclReader::RedeclarableResult ASTDec<br>
<br>
if (uint64_t Val = Record.readInt()) {<br>
VD->setInit(Record.readExpr());<br>
- if (Val > 1) { // IsInitKnownICE = 1, IsInitNotICE = 2, IsInitICE = 3<br>
+ if (Val > 1) {<br>
EvaluatedStmt *Eval = VD->ensureEvaluatedStmt();<br>
Eval->CheckedICE = true;<br>
- Eval->IsICE = Val == 3;<br>
+ Eval->IsICE = (Val & 1) != 0;<br>
+ Eval->HasConstantDestruction = (Val & 4) != 0;<br>
}<br>
}<br>
<br>
<br>
Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Sat Sep 28 22:08:46 2019<br>
@@ -968,7 +968,14 @@ void ASTDeclWriter::VisitVarDecl(VarDecl<br>
Record.push_back(D->getLinkageInternal());<br>
<br>
if (D->getInit()) {<br>
- Record.push_back(!D->isInitKnownICE() ? 1 : (D->isInitICE() ? 3 : 2));<br>
+ if (!D->isInitKnownICE())<br>
+ Record.push_back(1);<br>
+ else {<br>
+ Record.push_back(<br>
+ 2 |<br>
+ (D->isInitICE() ? 1 : 0) |<br>
+ (D->ensureEvaluatedStmt()->HasConstantDestruction ? 4 : 0));<br>
+ }<br>
Record.AddStmt(D->getInit());<br>
} else {<br>
Record.push_back(0);<br>
@@ -2140,7 +2147,7 @@ void ASTWriter::WriteDeclAbbrevs() {<br>
Abv->Add(BitCodeAbbrevOp(0)); // ImplicitParamKind<br>
Abv->Add(BitCodeAbbrevOp(0)); // EscapingByref<br>
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage<br>
- Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // IsInitICE (local)<br>
+ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // IsInitICE (local)<br>
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // VarKind (local enum)<br>
// Type Source Info<br>
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));<br>
<br>
Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp (original)<br>
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp Sat Sep 28 22:08:46 2019<br>
@@ -1,4 +1,5 @@<br>
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s<br>
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s<br>
<br>
// A constexpr specifier used in an object declaration declares the object as<br>
// const.<br>
@@ -35,3 +36,19 @@ struct pixel {<br>
};<br>
constexpr pixel ur = { 1294, 1024 }; // ok<br>
constexpr pixel origin; // expected-error {{default initialization of an object of const type 'const pixel' without a user-provided default constructor}}<br>
+<br>
+#if __cplusplus > 201702L<br>
+// A constexpr variable shall have constant destruction.<br>
+struct A {<br>
+ bool ok;<br>
+ constexpr A(bool ok) : ok(ok) {}<br>
+ constexpr ~A() noexcept(false) {<br>
+ void oops(); // expected-note 2{{declared here}}<br>
+ if (!ok) oops(); // expected-note 2{{non-constexpr function}}<br>
+ }<br>
+};<br>
+<br>
+constexpr A const_dtor(true);<br>
+constexpr A non_const_dtor(false); // expected-error {{must have constant destruction}} expected-note {{in call}}<br>
+constexpr A arr_dtor[5] = {true, true, true, false, true}; // expected-error {{must have constant destruction}} expected-note {{in call to '&arr_dtor[3]->~A()'}}<br>
+#endif<br>
<br>
Added: cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp?rev=373159&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp?rev=373159&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp (added)<br>
+++ cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp Sat Sep 28 22:08:46 2019<br>
@@ -0,0 +1,43 @@<br>
+// RUN: %clang_cc1 -std=c++2a -verify %s<br>
+<br>
+constexpr int non_class = 42;<br>
+constexpr int arr_non_class[5] = {1, 2, 3};<br>
+<br>
+struct A {<br>
+ int member = 1;<br>
+ constexpr ~A() { member = member + 1; }<br>
+};<br>
+constexpr A class_ = {};<br>
+constexpr A arr_class[5] = {{}, {}};<br>
+<br>
+struct Mutable {<br>
+ mutable int member = 1; // expected-note {{declared here}}<br>
+ constexpr ~Mutable() { member = member + 1; } // expected-note {{read of mutable member}}<br>
+};<br>
+constexpr Mutable mut_member; // expected-error {{must have constant destruction}} expected-note {{in call}}<br>
+<br>
+struct MutableStore {<br>
+ mutable int member = 1; // expected-note {{declared here}}<br>
+ constexpr ~MutableStore() { member = 2; } // expected-note {{assignment to mutable member}}<br>
+};<br>
+constexpr MutableStore mut_store; // expected-error {{must have constant destruction}} expected-note {{in call}}<br>
+<br>
+// Note: the constant destruction rules disallow this example even though hcm.n is a const object.<br>
+struct MutableConst {<br>
+ struct HasConstMember {<br>
+ const int n = 4;<br>
+ };<br>
+ mutable HasConstMember hcm; // expected-note {{here}}<br>
+ constexpr ~MutableConst() {<br>
+ int q = hcm.n; // expected-note {{read of mutable}}<br>
+ }<br>
+};<br>
+constexpr MutableConst mc; // expected-error {{must have constant destruction}} expected-note {{in call}}<br>
+<br>
+struct Temporary {<br>
+ int &&temp;<br>
+ constexpr ~Temporary() {<br>
+ int n = temp; // expected-note {{outside the expression that created the temporary}}<br>
+ }<br>
+};<br>
+constexpr Temporary t = {3}; // expected-error {{must have constant destruction}} expected-note {{created here}} expected-note {{in call}}<br>
<br>
Modified: cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp (original)<br>
+++ cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp Sat Sep 28 22:08:46 2019<br>
@@ -14,6 +14,7 @@<br>
<br>
class a {<br>
public:<br>
+ a();<br>
~a();<br>
};<br>
class logger_base {<br>
<br>
Modified: cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp (original)<br>
+++ cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp Sat Sep 28 22:08:46 2019<br>
@@ -1,5 +1,58 @@<br>
-// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++2a | FileCheck %s<br>
-// expected-no-diagnostics<br>
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit<br>
+<br>
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-pch -o %t.pch %s -std=c++2a<br>
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit<br>
<br>
// CHECK: @a = global i32 123,<br>
int a = (delete new int, 123);<br>
+<br>
+struct B {<br>
+ constexpr B() {}<br>
+ constexpr ~B() { n *= 5; }<br>
+ int n = 123;<br>
+};<br>
+// CHECK: @b = global {{.*}} i32 123<br>
+extern constexpr B b = B();<br>
+<br>
+// CHECK: @_ZL1c = internal global {{.*}} i32 123<br>
+const B c;<br>
+int use_c() { return c.n; }<br>
+<br>
+struct D {<br>
+ int n;<br>
+ constexpr ~D() {}<br>
+};<br>
+D d;<br>
+// CHECK: @d = global {{.*}} zeroinitializer<br>
+<br>
+D d_arr[3];<br>
+// CHECK: @d_arr = global {{.*}} zeroinitializer<br>
+<br>
+thread_local D d_tl;<br>
+// CHECK: @d_tl = thread_local global {{.*}} zeroinitializer<br>
+<br>
+// CHECK-NOT: @llvm.global_ctors<br>
+<br>
+// CHECK-LABEL: define {{.*}} @_Z1fv(<br>
+void f() {<br>
+ // CHECK-NOT: call<br>
+ // CHECK: call {{.*}}memcpy<br>
+ // CHECK-NOT: call<br>
+ // CHECK: call {{.*}}memset<br>
+ // CHECK-NOT: call<br>
+ // CHECK: }<br>
+ constexpr B b;<br>
+ D d = D();<br>
+}<br>
+<br>
+// CHECK-LABEL: define {{.*}} @_Z1gv(<br>
+void g() {<br>
+ // CHECK-NOT: call<br>
+ // CHECK-NOT: cxa_guard<br>
+ // CHECK-NOT: _ZGV<br>
+ // CHECK: }<br>
+ static constexpr B b1;<br>
+ static const B b2;<br>
+ static D d;<br>
+ thread_local D d_tl;<br>
+}<br>
<br>
Modified: cfe/trunk/test/CodeGenCXX/no_destroy.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/no_destroy.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/no_destroy.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/no_destroy.cpp (original)<br>
+++ cfe/trunk/test/CodeGenCXX/no_destroy.cpp Sat Sep 28 22:08:46 2019<br>
@@ -5,10 +5,8 @@ struct NonTrivial {<br>
~NonTrivial();<br>
};<br>
<br>
-// CHECK-LABEL: define internal void @__cxx_global_var_init<br>
// CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev<br>
[[clang::no_destroy]] NonTrivial nt1;<br>
-// CHECK-LABEL: define internal void @__cxx_global_var_init<br>
// CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev<br>
[[clang::no_destroy]] thread_local NonTrivial nt2;<br>
<br>
@@ -16,11 +14,9 @@ struct NonTrivial2 {<br>
~NonTrivial2();<br>
};<br>
<br>
-// CHECK-LABEL: define internal void @__cxx_global_var_init<br>
-// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev<br>
+// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt21<br>
NonTrivial2 nt21;<br>
-// CHECK-LABEL: define internal void @__cxx_global_var_init<br>
-// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev<br>
+// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt22<br>
thread_local NonTrivial2 nt22;<br>
<br>
// CHECK-LABEL: define void @_Z1fv<br>
<br>
Added: cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp?rev=373159&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp?rev=373159&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp (added)<br>
+++ cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp Sat Sep 28 22:08:46 2019<br>
@@ -0,0 +1,19 @@<br>
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++2a | FileCheck %s<br>
+<br>
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-pch -o %t.pch %s -std=c++2a<br>
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s<br>
+<br>
+struct B {<br>
+ constexpr B() {}<br>
+ constexpr ~B() { n *= 5; }<br>
+ int n = 123;<br>
+};<br>
+<br>
+// We emit a dynamic destructor here because b.n might have been modified<br>
+// before b is destroyed.<br>
+//<br>
+// CHECK: @b = global {{.*}} i32 123<br>
+B b = B();<br>
+<br>
+// CHECK: define {{.*}}cxx_global_var_init<br>
+// CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN1BD1Ev {{.*}} @b<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>