From bee7d7acd2ca9cfefcf38124b97dd94c06c4c3ac Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 8 Mar 2016 22:17:41 +0000 Subject: [PATCH] P0017R1: In C++1z, an aggregate class can have (public non-virtual) base classes; these are initialized as if they were data members. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262963 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Initialization.h | 7 +- lib/AST/DeclCXX.cpp | 25 +++- lib/AST/ExprConstant.cpp | 27 +++- lib/CodeGen/CGExprAgg.cpp | 42 +++++- lib/CodeGen/CGExprCXX.cpp | 13 +- lib/CodeGen/CGExprConstant.cpp | 16 ++- lib/Sema/SemaInit.cpp | 131 ++++++++++++++---- .../dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp | 81 ----------- .../dcl.decl/dcl.init/dcl.init.aggr/p1.cpp | 124 +++++++++++++++++ .../cxx1z-initializer-aggregate.cpp | 114 +++++++++++++++ test/SemaCXX/constant-expression-cxx1z.cpp | 27 ++++ www/cxx_status.html | 2 +- 12 files changed, 479 insertions(+), 130 deletions(-) delete mode 100644 test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp create mode 100644 test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1.cpp create mode 100644 test/CodeGenCXX/cxx1z-initializer-aggregate.cpp create mode 100644 test/SemaCXX/constant-expression-cxx1z.cpp diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index d4f57b7011..cffbbe7930 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -284,9 +284,10 @@ public: /// \brief Create the initialization entity for a base class subobject. - static InitializedEntity InitializeBase(ASTContext &Context, - const CXXBaseSpecifier *Base, - bool IsInheritedVirtualBase); + static InitializedEntity + InitializeBase(ASTContext &Context, const CXXBaseSpecifier *Base, + bool IsInheritedVirtualBase, + const InitializedEntity *Parent = nullptr); /// \brief Create the initialization entity for a delegated constructor. static InitializedEntity InitializeDelegation(QualType Type) { diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 65bfc49dae..385ac0b83e 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -141,9 +141,11 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, C.Deallocate(data().getBases()); if (NumBases) { - // C++ [dcl.init.aggr]p1: - // An aggregate is [...] a class with [...] no base classes [...]. - data().Aggregate = false; + if (!C.getLangOpts().CPlusPlus1z) { + // C++ [dcl.init.aggr]p1: + // An aggregate is [...] a class with [...] no base classes [...]. + data().Aggregate = false; + } // C++ [class]p4: // A POD-struct is an aggregate class... @@ -188,6 +190,11 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, data().HasNoNonEmptyBases = false; } + // C++1z [dcl.init.agg]p1: + // An aggregate is a class with [...] no private or protected base classes + if (Base->getAccessSpecifier() != AS_public) + data().Aggregate = false; + // C++ [class.virtual]p1: // A class that declares or inherits a virtual function is called a // polymorphic class. @@ -218,6 +225,10 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, if (CXXRecordDecl *VBaseDecl = VBase.getType()->getAsCXXRecordDecl()) if (!VBaseDecl->hasCopyConstructorWithConstParam()) data().ImplicitCopyConstructorHasConstParam = false; + + // C++1z [dcl.init.agg]p1: + // An aggregate is a class with [...] no virtual base classes + data().Aggregate = false; } } @@ -226,11 +237,15 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, if (SeenVBaseTypes.insert(C.getCanonicalType(BaseType)).second) VBases.push_back(Base); - // C++0x [meta.unary.prop] is_empty: + // C++11 [meta.unary.prop] is_empty: // T is a class type, but not a union type, with ... no virtual base // classes data().Empty = false; + // C++1z [dcl.init.agg]p1: + // An aggregate is a class with [...] no virtual base classes + data().Aggregate = false; + // C++11 [class.ctor]p5, C++11 [class.copy]p12, C++11 [class.copy]p25: // A [default constructor, copy/move constructor, or copy/move assignment // operator for a class X] is trivial [...] if: @@ -732,7 +747,7 @@ void CXXRecordDecl::addedMember(Decl *D) { // An aggregate is a [...] class with [...] no // brace-or-equal-initializers for non-static data members. // - // This rule was removed in C++1y. + // This rule was removed in C++14. if (!getASTContext().getLangOpts().CPlusPlus14) data().Aggregate = false; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 89a21acc84..7cc9512d80 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -5428,12 +5428,33 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr); } - assert((!isa(RD) || !cast(RD)->getNumBases()) && - "initializer list for class with base classes"); - Result = APValue(APValue::UninitStruct(), 0, + auto *CXXRD = dyn_cast(RD); + Result = APValue(APValue::UninitStruct(), CXXRD ? CXXRD->getNumBases() : 0, std::distance(RD->field_begin(), RD->field_end())); unsigned ElementNo = 0; bool Success = true; + + // Initialize base classes. + if (CXXRD) { + for (const auto &Base : CXXRD->bases()) { + assert(ElementNo < E->getNumInits() && "missing init for base class"); + const Expr *Init = E->getInit(ElementNo); + + LValue Subobject = This; + if (!HandleLValueBase(Info, Init, Subobject, CXXRD, &Base)) + return false; + + APValue &FieldVal = Result.getStructBase(ElementNo); + if (!EvaluateInPlace(FieldVal, Info, Subobject, Init)) { + if (!Info.keepEvaluatingAfterFailure()) + return false; + Success = false; + } + ++ElementNo; + } + } + + // Initialize members. for (const auto *Field : RD->fields()) { // Anonymous bit-fields are not considered members of the class for // purposes of aggregate initialization. diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index 947b3d5b85..53131f7557 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -1171,6 +1171,38 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { unsigned NumInitElements = E->getNumInits(); RecordDecl *record = E->getType()->castAs()->getDecl(); + // We'll need to enter cleanup scopes in case any of the element + // initializers throws an exception. + SmallVector cleanups; + llvm::Instruction *cleanupDominator = nullptr; + + unsigned curInitIndex = 0; + + // Emit initialization of base classes. + if (auto *CXXRD = dyn_cast(record)) { + assert(E->getNumInits() >= CXXRD->getNumBases() && + "missing initializer for base class"); + for (auto &Base : CXXRD->bases()) { + assert(!Base.isVirtual() && "should not see vbases here"); + auto *BaseRD = Base.getType()->getAsCXXRecordDecl(); + Address V = CGF.GetAddressOfDirectBaseInCompleteClass( + Dest.getAddress(), CXXRD, BaseRD, + /*isBaseVirtual*/ false); + AggValueSlot AggSlot = + AggValueSlot::forAddr(V, Qualifiers(), + AggValueSlot::IsDestructed, + AggValueSlot::DoesNotNeedGCBarriers, + AggValueSlot::IsNotAliased); + CGF.EmitAggExpr(E->getInit(curInitIndex++), AggSlot); + + if (QualType::DestructionKind dtorKind = + Base.getType().isDestructedType()) { + CGF.pushDestroy(dtorKind, V, Base.getType()); + cleanups.push_back(CGF.EHStack.stable_begin()); + } + } + } + // Prepare a 'this' for CXXDefaultInitExprs. CodeGenFunction::FieldConstructionScope FCS(CGF, Dest.getAddress()); @@ -1204,14 +1236,8 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { return; } - // We'll need to enter cleanup scopes in case any of the member - // initializers throw an exception. - SmallVector cleanups; - llvm::Instruction *cleanupDominator = nullptr; - // Here we iterate over the fields; this makes it simpler to both // default-initialize fields and skip over unnamed fields. - unsigned curInitIndex = 0; for (const auto *field : record->fields()) { // We're done once we hit the flexible array member. if (field->getType()->isIncompleteArrayType()) @@ -1317,6 +1343,10 @@ static CharUnits GetNumNonZeroBytesInInit(const Expr *E, CodeGenFunction &CGF) { CharUnits NumNonZeroBytes = CharUnits::Zero(); unsigned ILEElement = 0; + if (auto *CXXRD = dyn_cast(SD)) + for (auto &Base : CXXRD->bases()) + NumNonZeroBytes += + GetNumNonZeroBytesInInit(ILE->getInit(ILEElement++), CGF); for (const auto *Field : SD->fields()) { // We're done once we hit the flexible array member or run out of // InitListExpr elements. diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 614b076923..af37097249 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -1011,15 +1011,18 @@ void CodeGenFunction::EmitNewArrayInitializer( if (auto *ILE = dyn_cast(Init)) { if (const RecordType *RType = ILE->getType()->getAs()) { if (RType->getDecl()->isStruct()) { - unsigned NumFields = 0; + unsigned NumElements = 0; + if (auto *CXXRD = dyn_cast(RType->getDecl())) + NumElements = CXXRD->getNumBases(); for (auto *Field : RType->getDecl()->fields()) if (!Field->isUnnamedBitfield()) - ++NumFields; - if (ILE->getNumInits() == NumFields) + ++NumElements; + // FIXME: Recurse into nested InitListExprs. + if (ILE->getNumInits() == NumElements) for (unsigned i = 0, e = ILE->getNumInits(); i != e; ++i) if (!isa(ILE->getInit(i))) - --NumFields; - if (ILE->getNumInits() == NumFields && TryMemsetInitialization()) + --NumElements; + if (ILE->getNumInits() == NumElements && TryMemsetInitialization()) return; } } diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index 2aed3bb06d..d64c5876ee 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -368,7 +368,14 @@ bool ConstStructBuilder::Build(InitListExpr *ILE) { unsigned FieldNo = 0; unsigned ElementNo = 0; - + + // Bail out if we have base classes. We could support these, but they only + // arise in C++1z where we will have already constant folded most interesting + // cases. FIXME: There are still a few more cases we can handle this way. + if (auto *CXXRD = dyn_cast(RD)) + if (CXXRD->getNumBases()) + return false; + for (RecordDecl::field_iterator Field = RD->field_begin(), FieldEnd = RD->field_end(); Field != FieldEnd; ++Field, ++FieldNo) { // If this is a union, skip all the fields that aren't being initialized. @@ -1124,6 +1131,13 @@ bool ConstStructBuilder::Build(ConstExprEmitter *Emitter, unsigned FieldNo = -1; unsigned ElementNo = 0; + // Bail out if we have base classes. We could support these, but they only + // arise in C++1z where we will have already constant folded most interesting + // cases. FIXME: There are still a few more cases we can handle this way. + if (auto *CXXRD = dyn_cast(RD)) + if (CXXRD->getNumBases()) + return false; + for (FieldDecl *Field : RD->fields()) { ++FieldNo; diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index afa681453d..3edabc6428 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -282,6 +282,7 @@ class InitListChecker { unsigned &StructuredIndex); void CheckStructUnionTypes(const InitializedEntity &Entity, InitListExpr *IList, QualType DeclType, + CXXRecordDecl::base_class_range Bases, RecordDecl::field_iterator Field, bool SubobjectIsDesignatorContext, unsigned &Index, InitListExpr *StructuredList, @@ -340,6 +341,10 @@ class InitListChecker { // in the InitListExpr, the "holes" in Case#1 are filled not with empty // initializers but with special "NoInitExpr" place holders, which tells the // CodeGen not to generate any initializers for these parts. + void FillInEmptyInitForBase(unsigned Init, const CXXBaseSpecifier &Base, + const InitializedEntity &ParentEntity, + InitListExpr *ILE, bool &RequiresSecondPass, + bool FillWithNoInit); void FillInEmptyInitForField(unsigned Init, FieldDecl *Field, const InitializedEntity &ParentEntity, InitListExpr *ILE, bool &RequiresSecondPass, @@ -479,6 +484,37 @@ void InitListChecker::CheckEmptyInitializable(const InitializedEntity &Entity, hadError = true; } +void InitListChecker::FillInEmptyInitForBase( + unsigned Init, const CXXBaseSpecifier &Base, + const InitializedEntity &ParentEntity, InitListExpr *ILE, + bool &RequiresSecondPass, bool FillWithNoInit) { + assert(Init < ILE->getNumInits() && "should have been expanded"); + + InitializedEntity BaseEntity = InitializedEntity::InitializeBase( + SemaRef.Context, &Base, false, &ParentEntity); + + if (!ILE->getInit(Init)) { + ExprResult BaseInit = + FillWithNoInit ? new (SemaRef.Context) NoInitExpr(Base.getType()) + : PerformEmptyInit(SemaRef, ILE->getLocEnd(), BaseEntity, + /*VerifyOnly*/ false); + if (BaseInit.isInvalid()) { + hadError = true; + return; + } + + ILE->setInit(Init, BaseInit.getAs()); + } else if (InitListExpr *InnerILE = + dyn_cast(ILE->getInit(Init))) { + FillInEmptyInitializations(BaseEntity, InnerILE, + RequiresSecondPass, FillWithNoInit); + } else if (DesignatedInitUpdateExpr *InnerDIUE = + dyn_cast(ILE->getInit(Init))) { + FillInEmptyInitializations(BaseEntity, InnerDIUE->getUpdater(), + RequiresSecondPass, /*FillWithNoInit =*/true); + } +} + void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, const InitializedEntity &ParentEntity, InitListExpr *ILE, @@ -593,14 +629,25 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, // The fields beyond ILE->getNumInits() are default initialized, so in // order to leave them uninitialized, the ILE is expanded and the extra // fields are then filled with NoInitExpr. - unsigned NumFields = 0; - for (auto *Field : RDecl->fields()) - if (!Field->isUnnamedBitfield()) - ++NumFields; - if (ILE->getNumInits() < NumFields) - ILE->resizeInits(SemaRef.Context, NumFields); + unsigned NumElems = numStructUnionElements(ILE->getType()); + if (RDecl->hasFlexibleArrayMember()) + ++NumElems; + if (ILE->getNumInits() < NumElems) + ILE->resizeInits(SemaRef.Context, NumElems); unsigned Init = 0; + + if (auto *CXXRD = dyn_cast(RDecl)) { + for (auto &Base : CXXRD->bases()) { + if (hadError) + return; + + FillInEmptyInitForBase(Init, Base, Entity, ILE, RequiresSecondPass, + FillWithNoInit); + ++Init; + } + } + for (auto *Field : RDecl->fields()) { if (Field->isUnnamedBitfield()) continue; @@ -744,6 +791,8 @@ int InitListChecker::numArrayElements(QualType DeclType) { int InitListChecker::numStructUnionElements(QualType DeclType) { RecordDecl *structDecl = DeclType->getAs()->getDecl(); int InitializableMembers = 0; + if (auto *CXXRD = dyn_cast(structDecl)) + InitializableMembers += CXXRD->getNumBases(); for (const auto *Field : structDecl->fields()) if (!Field->isUnnamedBitfield()) ++InitializableMembers; @@ -991,10 +1040,14 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity, assert(DeclType->isAggregateType() && "non-aggregate records should be handed in CheckSubElementType"); RecordDecl *RD = DeclType->getAs()->getDecl(); - CheckStructUnionTypes(Entity, IList, DeclType, RD->field_begin(), - SubobjectIsDesignatorContext, Index, - StructuredList, StructuredIndex, - TopLevelObject); + auto Bases = + CXXRecordDecl::base_class_range(CXXRecordDecl::base_class_iterator(), + CXXRecordDecl::base_class_iterator()); + if (auto *CXXRD = dyn_cast(RD)) + Bases = CXXRD->bases(); + CheckStructUnionTypes(Entity, IList, DeclType, Bases, RD->field_begin(), + SubobjectIsDesignatorContext, Index, StructuredList, + StructuredIndex, TopLevelObject); } else if (DeclType->isArrayType()) { llvm::APSInt Zero( SemaRef.Context.getTypeSize(SemaRef.Context.getSizeType()), @@ -1670,16 +1723,13 @@ bool InitListChecker::CheckFlexibleArrayInit(const InitializedEntity &Entity, return FlexArrayDiag != diag::ext_flexible_array_init; } -void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, - InitListExpr *IList, - QualType DeclType, - RecordDecl::field_iterator Field, - bool SubobjectIsDesignatorContext, - unsigned &Index, - InitListExpr *StructuredList, - unsigned &StructuredIndex, - bool TopLevelObject) { - RecordDecl* structDecl = DeclType->getAs()->getDecl(); +void InitListChecker::CheckStructUnionTypes( + const InitializedEntity &Entity, InitListExpr *IList, QualType DeclType, + CXXRecordDecl::base_class_range Bases, RecordDecl::field_iterator Field, + bool SubobjectIsDesignatorContext, unsigned &Index, + InitListExpr *StructuredList, unsigned &StructuredIndex, + bool TopLevelObject) { + RecordDecl *structDecl = DeclType->getAs()->getDecl(); // If the record is invalid, some of it's members are invalid. To avoid // confusion, we forgo checking the intializer for the entire record. @@ -1724,13 +1774,35 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, return; } + bool InitializedSomething = false; + + // If we have any base classes, they are initialized prior to the fields. + for (auto &Base : Bases) { + Expr *Init = Index < IList->getNumInits() ? IList->getInit(Index) : nullptr; + SourceLocation InitLoc = Init ? Init->getLocStart() : IList->getLocEnd(); + + // Designated inits always initialize fields, so if we see one, all + // remaining base classes have no explicit initializer. + if (Init && isa(Init)) + Init = nullptr; + + InitializedEntity BaseEntity = InitializedEntity::InitializeBase( + SemaRef.Context, &Base, false, &Entity); + if (Init) { + CheckSubElementType(BaseEntity, IList, Base.getType(), Index, + StructuredList, StructuredIndex); + InitializedSomething = true; + } else if (VerifyOnly) { + CheckEmptyInitializable(BaseEntity, InitLoc); + } + } + // If structDecl is a forward declaration, this loop won't do // anything except look at designated initializers; That's okay, // because an error should get printed out elsewhere. It might be // worthwhile to skip over the rest of the initializer, though. RecordDecl *RD = DeclType->getAs()->getDecl(); RecordDecl::field_iterator FieldEnd = RD->field_end(); - bool InitializedSomething = false; bool CheckForMissingFields = true; while (Index < IList->getNumInits()) { Expr *Init = IList->getInit(Index); @@ -2302,8 +2374,11 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // Check the remaining fields within this class/struct/union subobject. bool prevHadError = hadError; - CheckStructUnionTypes(Entity, IList, CurrentObjectType, Field, false, Index, - StructuredList, FieldIndex); + auto NoBases = + CXXRecordDecl::base_class_range(CXXRecordDecl::base_class_iterator(), + CXXRecordDecl::base_class_iterator()); + CheckStructUnionTypes(Entity, IList, CurrentObjectType, NoBases, Field, + false, Index, StructuredList, FieldIndex); return hadError && !prevHadError; } @@ -2785,10 +2860,11 @@ InitializedEntity::InitializedEntity(ASTContext &Context, unsigned Index, InitializedEntity InitializedEntity::InitializeBase(ASTContext &Context, const CXXBaseSpecifier *Base, - bool IsInheritedVirtualBase) { + bool IsInheritedVirtualBase, + const InitializedEntity *Parent) { InitializedEntity Result; Result.Kind = EK_Base; - Result.Parent = nullptr; + Result.Parent = Parent; Result.Base = reinterpret_cast(Base); if (IsInheritedVirtualBase) Result.Base |= 0x01; @@ -5778,6 +5854,11 @@ static const InitializedEntity *getEntityForTemporaryLifetimeExtension( FallbackDecl); case InitializedEntity::EK_Base: + // For subobjects, we look at the complete object. + if (Entity->getParent()) + return getEntityForTemporaryLifetimeExtension(Entity->getParent(), + Entity); + // Fall through. case InitializedEntity::EK_Delegating: // We can reach this case for aggregate initialization in a constructor: // struct A { int &&r; }; diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp deleted file mode 100644 index 8767678362..0000000000 --- a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1y %s -DCXX1Y - -// An aggregate is an array or a class... -struct Aggr { -private: - static const int n; - void f(); -protected: - struct Inner { int m; }; -public: - bool &br; // expected-note {{default constructor of 'Aggr' is implicitly deleted because field 'br' of reference type 'bool &' would not be initialized}} -}; -bool b; -Aggr ag = { b }; - -// with no user-provided constructors, ... -struct NonAggr1a { // expected-note 2 {{candidate constructor}} - NonAggr1a(int, int); // expected-note {{candidate constructor}} - int k; -}; -NonAggr1a na1a = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr1a'}} - -struct NonAggr1b { - NonAggr1b(const NonAggr1b &); // expected-note {{candidate constructor}} - int k; -}; -NonAggr1b na1b = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr1b'}} - -// no brace-or-equal-initializers for non-static data members, ... -// Note, this bullet was removed in C++1y. -struct NonAggr2 { - int m = { 123 }; -}; -NonAggr2 na2 = { 42 }; -#ifndef CXX1Y -// expected-error@-2 {{no matching constructor for initialization of 'NonAggr2'}} -// expected-note@-6 3 {{candidate constructor}} -#endif - -// no private... -struct NonAggr3 { // expected-note 3 {{candidate constructor}} -private: - int n; -}; -NonAggr3 na3 = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr3'}} - -// or protected non-static data members, ... -struct NonAggr4 { // expected-note 3 {{candidate constructor}} -protected: - int n; -}; -NonAggr4 na4 = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr4'}} - -// no base classes, ... -struct NonAggr5 : Aggr { // expected-note 3 {{candidate constructor}} -}; -NonAggr5 na5 = { b }; // expected-error {{no matching constructor for initialization of 'NonAggr5'}} -template -struct MaybeAggr5a : BaseList... {}; // expected-note {{default constructor of 'MaybeAggr5a' is implicitly deleted because base class 'Aggr' has a deleted default constructor}} -MaybeAggr5a<> ma5a0 = {}; // ok -MaybeAggr5a ma5a1 = {}; // expected-error {{call to implicitly-deleted default constructor of 'MaybeAggr5a'}} - -// and no virtual functions. -struct NonAggr6 { // expected-note 3 {{candidate constructor}} - virtual void f(); - int n; -}; -NonAggr6 na6 = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr6'}} - -struct DefaultedAggr { - int n; - - DefaultedAggr() = default; - DefaultedAggr(const DefaultedAggr &) = default; - DefaultedAggr(DefaultedAggr &&) = default; - DefaultedAggr &operator=(const DefaultedAggr &) = default; - DefaultedAggr &operator=(DefaultedAggr &&) = default; - ~DefaultedAggr() = default; -}; -DefaultedAggr da = { 42 } ; diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1.cpp new file mode 100644 index 0000000000..40f6431e3d --- /dev/null +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1.cpp @@ -0,0 +1,124 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s + +// An aggregate is an array or a class... +struct Aggr { +private: + static const int n; + void f(); +protected: + struct Inner { int m; }; +public: + bool &br; +}; +bool b; +Aggr ag = { b }; + +// with no user-provided constructors, ... +struct NonAggr1a { // expected-note 2 {{candidate constructor}} + NonAggr1a(int, int); // expected-note {{candidate constructor}} + int k; +}; +NonAggr1a na1a = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr1a'}} + +struct NonAggr1b { + NonAggr1b(const NonAggr1b &); // expected-note {{candidate constructor}} + int k; +}; +NonAggr1b na1b = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr1b'}} + +// no brace-or-equal-initializers for non-static data members, ... +// Note, this bullet was removed in C++1y. +struct NonAggr2 { + int m = { 123 }; +}; +NonAggr2 na2 = { 42 }; +#if __cplusplus < 201402L +// expected-error@-2 {{no matching constructor for initialization of 'NonAggr2'}} +// expected-note@-6 3 {{candidate constructor}} +#endif + +// no private... +struct NonAggr3 { // expected-note 3 {{candidate constructor}} +private: + int n; +}; +NonAggr3 na3 = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr3'}} + +// or protected non-static data members, ... +struct NonAggr4 { // expected-note 3 {{candidate constructor}} +protected: + int n; +}; +NonAggr4 na4 = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr4'}} + +// [pre-C++1z] no base classes, ... +struct NonAggr5 : Aggr { +}; +NonAggr5 na5 = { b }; +#if __cplusplus <= 201402L +// expected-error@-2 {{no matching constructor for initialization of 'NonAggr5'}} +// expected-note@-5 3 {{candidate constructor}} +#endif +template +struct MaybeAggr5a : BaseList... {}; +MaybeAggr5a<> ma5a0 = {}; // ok +MaybeAggr5a ma5a1 = {}; // ok in C++17 +MaybeAggr5a m5a2 = {}; // ok, aggregate init in C++17, default ctor in C++11 and C++14 +MaybeAggr5a m5a3 = {0}; // ok in C++17, overrides default member initializer in base class +#if __cplusplus <= 201402L +// expected-error@-4 {{call to implicitly-deleted default constructor of 'MaybeAggr5a'}} +// expected-note@-7 {{default constructor of 'MaybeAggr5a' is implicitly deleted because base class 'Aggr' has a deleted default constructor}} +// expected-note@13 {{default constructor of 'Aggr' is implicitly deleted because field 'br' of reference type 'bool &' would not be initialized}} +// expected-error@-5 {{no matching constructor}} expected-note@-9 3{{candidate}} +#else +// expected-error@-9 {{reference member of type 'bool &' uninitialized}} +// expected-note@13 {{uninitialized reference member is here}} +#endif + +// [C++1z] no virtual, protected, or private base classes, ... +struct NonAggr5b : virtual Aggr {}; // expected-note 3{{candidate}} +NonAggr5b na5b = { b }; // expected-error {{no matching constructor}} +struct NonAggr5c : NonAggr5b {}; // expected-note 3{{candidate}} +NonAggr5c na5c = { b }; // expected-error {{no matching constructor}} +struct NonAggr5d : protected Aggr {}; // expected-note 3{{candidate}} +NonAggr5d na5d = { b }; // expected-error {{no matching constructor}} +struct NonAggr5e : private Aggr {}; // expected-note 3{{candidate}} +NonAggr5e na5e = { b }; // expected-error {{no matching constructor}} +class NonAggr5f : Aggr {}; // expected-note 3{{candidate}} +NonAggr5f na5f = { b }; // expected-error {{no matching constructor}} + +// [C++1z] (the base class need not itself be an aggregate) +struct MaybeAggr5g : NonAggr1a {}; +MaybeAggr5g ma5g1 = { 1 }; +MaybeAggr5g ma5g2 = { {1, 2} }; +MaybeAggr5g ma5g3 = {}; +#if __cplusplus <= 201402L +// expected-error@-4 {{no matching constructor}} // expected-note@-5 3{{candidate}} +// expected-error@-4 {{no matching constructor}} // expected-note@-6 3{{candidate}} +// expected-error@-4 {{implicitly-deleted default constructor}} expected-note@-7 {{no default constructor}} +#else +// expected-error@-8 {{no viable conversion from 'int' to 'NonAggr1a'}} expected-note@19 2{{candidate}} +// (ok) +// expected-error@-8 {{no matching constructor}} expected-note@19 2{{candidate}} expected-note@20 {{candidate}} +#endif + +// and no virtual functions. +struct NonAggr6 { // expected-note 3 {{candidate constructor}} + virtual void f(); + int n; +}; +NonAggr6 na6 = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr6'}} + +struct DefaultedAggr { + int n; + + DefaultedAggr() = default; + DefaultedAggr(const DefaultedAggr &) = default; + DefaultedAggr(DefaultedAggr &&) = default; + DefaultedAggr &operator=(const DefaultedAggr &) = default; + DefaultedAggr &operator=(DefaultedAggr &&) = default; + ~DefaultedAggr() = default; +}; +DefaultedAggr da = { 42 } ; diff --git a/test/CodeGenCXX/cxx1z-initializer-aggregate.cpp b/test/CodeGenCXX/cxx1z-initializer-aggregate.cpp new file mode 100644 index 0000000000..9110e49f93 --- /dev/null +++ b/test/CodeGenCXX/cxx1z-initializer-aggregate.cpp @@ -0,0 +1,114 @@ +// RUN: %clang_cc1 -std=c++1z %s -triple x86_64-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm -o - | FileCheck %s + +namespace Constant { + struct A { + int n; + char k; + ~A(); + }; + + struct B { + char k2; + }; + + struct C : B {}; + + struct D : A, C {}; + + C c1 = {}; + C c2 = {1}; + // CHECK: @_ZN8Constant2c1E = global { i8 } zeroinitializer, align 1 + // CHECK: @_ZN8Constant2c2E = global { i8 } { i8 1 }, align 1 + + // Test packing bases into tail padding. + D d1 = {}; + D d2 = {1, 2, 3}; + D d3 = {1}; + // CHECK: @_ZN8Constant2d1E = global { i32, i8, i8 } zeroinitializer, align 4 + // CHECK: @_ZN8Constant2d2E = global { i32, i8, i8 } { i32 1, i8 2, i8 3 }, align 4 + // CHECK: @_ZN8Constant2d3E = global { i32, i8, i8 } { i32 1, i8 0, i8 0 }, align 4 + + // CHECK-LABEL: define {{.*}}global_var_init + // CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN8Constant1DD1Ev {{.*}} @_ZN8Constant2d1E + + // CHECK-LABEL: define {{.*}}global_var_init + // CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN8Constant1DD1Ev {{.*}} @_ZN8Constant2d2E + + // CHECK-LABEL: define {{.*}}global_var_init + // CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN8Constant1DD1Ev {{.*}} @_ZN8Constant2d3E +} + +namespace Dynamic { + struct A { + A(); + A(int); + A(const char*, unsigned); + ~A(); + void *p; + }; + + struct B { + ~B(); + int n = 5; + }; + + struct C { + C(bool = true); + }; + + int f(), g(), h(), i(); + struct D : A, B, C { + int n = f(); + }; + + D d1 = {}; + // CHECK-LABEL: define {{.*}}global_var_init + // CHECK: call void @_ZN7Dynamic1AC2Ev({{.*}} @_ZN7Dynamic2d1E + // CHECK: store i32 5, {{.*}}i8* getelementptr inbounds {{.*}} @_ZN7Dynamic2d1E{{.*}}, i64 8 + // CHECK: invoke void @_ZN7Dynamic1CC2Eb({{.*}} @_ZN7Dynamic2d1E{{.*}}, i1 zeroext true) + // CHECK: unwind label %[[UNWIND:.*]] + // CHECK: invoke i32 @_ZN7Dynamic1fEv() + // CHECK: unwind label %[[UNWIND:.*]] + // CHECK: store i32 {{.*}}, i32* getelementptr {{.*}} @_ZN7Dynamic2d1E, i32 0, i32 2 + // CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN7Dynamic1DD1Ev {{.*}} @_ZN7Dynamic2d1E + // CHECK: ret + // + // UNWIND: + // CHECK: call void @_ZN7Dynamic1BD1Ev({{.*}}i8* getelementptr inbounds {{.*}} @_ZN7Dynamic2d1E{{.*}}, i64 8 + // CHECK: call void @_ZN7Dynamic1AD1Ev({{.*}} @_ZN7Dynamic2d1E + + D d2 = {1, 2, false}; + // CHECK-LABEL: define {{.*}}global_var_init + // CHECK: call void @_ZN7Dynamic1AC1Ei({{.*}} @_ZN7Dynamic2d2E{{.*}}, i32 1) + // CHECK: store i32 2, {{.*}}i8* getelementptr inbounds {{.*}}@_ZN7Dynamic2d2E{{.*}}, i64 8 + // CHECK: invoke void @_ZN7Dynamic1CC1Eb({{.*}} @_ZN7Dynamic2d2E{{.*}}, i1 zeroext false) + // CHECK: invoke i32 @_ZN7Dynamic1fEv() + // CHECK: store i32 {{.*}}, i32* getelementptr {{.*}} @_ZN7Dynamic2d2E, i32 0, i32 2 + // CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN7Dynamic1DD1Ev {{.*}} @_ZN7Dynamic2d2E + // CHECK: ret void + + D d3 = {g(), h(), {}, i()}; + // CHECK-LABEL: define {{.*}}global_var_init + // CHECK: %[[G_CALL:.*]] = call i32 @_ZN7Dynamic1gEv() + // CHECK: call void @_ZN7Dynamic1AC1Ei({{.*}} @_ZN7Dynamic2d3E{{.*}}, i32 %[[G_CALL]]) + // CHECK: %[[H_CALL:.*]] = invoke i32 @_ZN7Dynamic1hEv() + // CHECK: unwind label %[[DESTROY_A_LPAD:.*]] + // CHECK: store i32 %[[H_CALL]], {{.*}}i8* getelementptr inbounds {{.*}} @_ZN7Dynamic2d3E{{.*}}, i64 8 + // CHECK: invoke void @_ZN7Dynamic1CC2Eb({{.*}} @_ZN7Dynamic2d3E{{.*}}, i1 zeroext true) + // CHECK: unwind label %[[DESTROY_AB_LPAD:.*]] + // CHECK: %[[I_CALL:.*]] = invoke i32 @_ZN7Dynamic1iEv() + // CHECK: unwind label %[[DESTROY_AB_LPAD:.*]] + // CHECK: store i32 %[[I_CALL]], i32* getelementptr {{.*}} @_ZN7Dynamic2d3E, i32 0, i32 2 + // CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN7Dynamic1DD1Ev {{.*}} @_ZN7Dynamic2d3E to i8* + // CHECK: ret + // + // DESTROY_A_LPAD: + // CHECK: br label %[[A_CLEANUP:.*]] + // + // DESTROY_B_LPAD: + // CHECK: call void @_ZN7Dynamic1BD1Ev({{.*}}i8* getelementptr inbounds {{.*}} @_ZN7Dynamic2d3E{{.*}}, i64 8 + // CHECK: br label %[[A_CLEANUP:.*]] + // + // A_CLEANUP: + // CHECK: call void @_ZN7Dynamic1AD1Ev({{.*}} @_ZN7Dynamic2d3E +} diff --git a/test/SemaCXX/constant-expression-cxx1z.cpp b/test/SemaCXX/constant-expression-cxx1z.cpp new file mode 100644 index 0000000000..e84de44d37 --- /dev/null +++ b/test/SemaCXX/constant-expression-cxx1z.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu + +namespace BaseClassAggregateInit { + struct A { + int a, b, c; + constexpr A(int n) : a(n), b(3 * n), c(b - 1) {} // expected-note {{outside the range of representable}} + constexpr A() : A(10) {}; + }; + struct B : A {}; + struct C { int q; }; + struct D : B, C { int k; }; + + constexpr D d1 = { 1, 2, 3 }; + static_assert(d1.a == 1 && d1.b == 3 && d1.c == 2 && d1.q == 2 && d1.k == 3); + + constexpr D d2 = { 14 }; + static_assert(d2.a == 14 && d2.b == 42 && d2.c == 41 && d2.q == 0 && d2.k == 0); + + constexpr D d3 = { A(5), C{2}, 1 }; + static_assert(d3.a == 5 && d3.b == 15 && d3.c == 14 && d3.q == 2 && d3.k == 1); + + constexpr D d4 = {}; + static_assert(d4.a == 10 && d4.b == 30 && d4.c == 29 && d4.q == 0 && d4.k == 0); + + constexpr D d5 = { __INT_MAX__ }; // expected-error {{must be initialized by a constant expression}} + // expected-note-re@-1 {{in call to 'A({{.*}})'}} +} diff --git a/www/cxx_status.html b/www/cxx_status.html index 85692f9d2b..5163402e35 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -644,7 +644,7 @@ as the draft C++1z standard evolves.

Aggregate initialization of classes with base classes P0017R1 - No + SVN constexpr lambda expressions -- 2.40.0