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