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