From: Alan Zhao Date: Thu, 13 Apr 2023 18:04:59 +0000 (-0700) Subject: [clang] Fix overly aggressive lifetime checks for parenthesized aggregate initialization X-Git-Tag: upstream/17.0.6~9893 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9b4faa11c68be4b45ddf29acd575bb52a3c2fad7;p=platform%2Fupstream%2Fllvm.git [clang] Fix overly aggressive lifetime checks for parenthesized aggregate initialization Before this patch, initialized class members would have the LifetimeKind LK_MemInitializer, which does not allow for binding a temporary to a reference. Binding to a temporary however is allowed in parenthesized aggregate initialization, even if it leads to a dangling reference. To fix this, we create a new EntityKind, EK_ParenAggInitMember, which has LifetimeKind LK_FullExpression. This patch does *not* attempt to diagnose dangling references as a result of using this feature. This patch also refactors TryOrBuildParenListInitialization(...) to accomodate creating different InitializedEntity objects. Fixes #61567 [0]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0960r3.html Reviewed By: shafik Differential Revision: https://reviews.llvm.org/D148274 --- diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index eaabe08..ec0dc0a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -373,9 +373,15 @@ Bug Fixes to C++ Support - Fix bug in the computation of the ``__has_unique_object_representations`` builtin for types with unnamed bitfields. (`#61336 `_) +<<<<<<< HEAD - Fix default member initializers sometimes being ignored when performing parenthesized aggregate initialization of templated types. (`#62266 `_) +======= +- Fix overly aggressive lifetime checks for parenthesized aggregate + initialization. + (`#61567 `_) +>>>>>>> c7422b289522 ([clang] Fix overly aggressive lifetime checks for parenthesized aggregate initialization) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7d0ee50..1758575 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2126,7 +2126,8 @@ def err_init_conversion_failed : Error< "exception object|a member subobject|an array element|a new value|a value|a " "base class|a constructor delegation|a vector element|a block element|a " "block element|a complex element|a lambda capture|a compound literal " - "initializer|a related result|a parameter of CF audited function}0 " + "initializer|a related result|a parameter of CF audited function|a " + "structured binding|a member subobject}0 " "%diff{of type $ with an %select{rvalue|lvalue}2 of type $|" "with an %select{rvalue|lvalue}2 of incompatible type}1,3" "%select{|: different classes%diff{ ($ vs $)|}5,6" diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index fcfb56f..2072cd8 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -123,6 +123,10 @@ public: /// decomposition declaration. EK_Binding, + /// The entity being initialized is a non-static data member subobject of an + /// object initialized via parenthesized aggregate initialization. + EK_ParenAggInitMember, + // Note: err_init_conversion_failed in DiagnosticSemaKinds.td uses this // enum as an index for its first %select. When modifying this list, // that diagnostic text needs to be updated as well. @@ -227,8 +231,10 @@ private: /// Create the initialization entity for a member subobject. InitializedEntity(FieldDecl *Member, const InitializedEntity *Parent, - bool Implicit, bool DefaultMemberInit) - : Kind(EK_Member), Parent(Parent), Type(Member->getType()), + bool Implicit, bool DefaultMemberInit, + bool IsParenAggInit = false) + : Kind(IsParenAggInit ? EK_ParenAggInitMember : EK_Member), + Parent(Parent), Type(Member->getType()), Variable{Member, Implicit, DefaultMemberInit} {} /// Create the initialization entity for an array element. @@ -388,6 +394,14 @@ public: return InitializedEntity(Member->getAnonField(), Parent, Implicit, false); } + /// Create the initialization entity for a member subobject initialized via + /// parenthesized aggregate init. + static InitializedEntity InitializeMemberFromParenAggInit(FieldDecl *Member) { + return InitializedEntity(Member, /*Parent=*/nullptr, /*Implicit=*/false, + /*DefaultMemberInit=*/false, + /*IsParenAggInit=*/true); + } + /// Create the initialization entity for a default member initializer. static InitializedEntity InitializeMemberFromDefaultMemberInitializer(FieldDecl *Member) { diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index cbda624..4a39c2d 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -1651,7 +1651,8 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, << Entity.getBaseSpecifier()->getType() << getSpecialMember(Constructor); break; - case InitializedEntity::EK_Member: { + case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: { const FieldDecl *Field = cast(Entity.getDecl()); PD = PDiag(diag::err_access_field_ctor); PD << Field->getType() << getSpecialMember(Constructor); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 55ef72d..2d5d31f 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -1186,6 +1186,7 @@ static void warnBracedScalarInit(Sema &S, const InitializedEntity &Entity, case InitializedEntity::EK_LambdaToBlockConversionBlockElement: case InitializedEntity::EK_Binding: case InitializedEntity::EK_StmtExprResult: + case InitializedEntity::EK_ParenAggInitMember: llvm_unreachable("unexpected braced scalar init"); } @@ -3359,6 +3360,7 @@ DeclarationName InitializedEntity::getName() const { case EK_Variable: case EK_Member: + case EK_ParenAggInitMember: case EK_Binding: case EK_TemplateParameter: return Variable.VariableOrMember->getDeclName(); @@ -3390,6 +3392,7 @@ ValueDecl *InitializedEntity::getDecl() const { switch (getKind()) { case EK_Variable: case EK_Member: + case EK_ParenAggInitMember: case EK_Binding: case EK_TemplateParameter: return Variable.VariableOrMember; @@ -3431,6 +3434,7 @@ bool InitializedEntity::allowsNRVO() const { case EK_Parameter_CF_Audited: case EK_TemplateParameter: case EK_Member: + case EK_ParenAggInitMember: case EK_Binding: case EK_New: case EK_Temporary: @@ -3465,7 +3469,10 @@ unsigned InitializedEntity::dumpImpl(raw_ostream &OS) const { case EK_Result: OS << "Result"; break; case EK_StmtExprResult: OS << "StmtExprResult"; break; case EK_Exception: OS << "Exception"; break; - case EK_Member: OS << "Member"; break; + case EK_Member: + case EK_ParenAggInitMember: + OS << "Member"; + break; case EK_Binding: OS << "Binding"; break; case EK_New: OS << "New"; break; case EK_Temporary: OS << "Temporary"; break; @@ -5303,179 +5310,224 @@ static void TryOrBuildParenListInitialization( Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, ArrayRef Args, InitializationSequence &Sequence, bool VerifyOnly, ExprResult *Result = nullptr) { - unsigned ArgIndexToProcess = 0; + unsigned EntityIndexToProcess = 0; SmallVector InitExprs; QualType ResultType; Expr *ArrayFiller = nullptr; FieldDecl *InitializedFieldInUnion = nullptr; - // Process entities (i.e. array members, base classes, or class fields) by - // adding an initialization expression to InitExprs for each entity to - // initialize. - auto ProcessEntities = [&](auto Range) -> bool { - bool IsUnionType = Entity.getType()->isUnionType(); - for (InitializedEntity SubEntity : Range) { - // Unions should only have one initializer expression. - // If there are more initializers than it will be caught when we check - // whether Index equals Args.size(). - if (ArgIndexToProcess == 1 && IsUnionType) - return true; - - bool IsMember = SubEntity.getKind() == InitializedEntity::EK_Member; - - // Unnamed bitfields should not be initialized at all, either with an arg - // or by default. - if (IsMember && cast(SubEntity.getDecl())->isUnnamedBitfield()) - continue; - - if (ArgIndexToProcess < Args.size()) { - // There are still expressions in Args that haven't been processed. - // Let's match them to the current entity to initialize. - Expr *E = Args[ArgIndexToProcess++]; - - // Incomplete array types indicate flexible array members. Do not allow - // paren list initializations of structs with these members, as GCC - // doesn't either. - if (IsMember) { - auto *FD = cast(SubEntity.getDecl()); - if (FD->getType()->isIncompleteArrayType()) { - if (!VerifyOnly) { - S.Diag(E->getBeginLoc(), diag::err_flexible_array_init) - << SourceRange(E->getBeginLoc(), E->getEndLoc()); - S.Diag(FD->getLocation(), diag::note_flexible_array_member) << FD; - } - Sequence.SetFailed( - InitializationSequence::FK_ParenthesizedListInitFailed); - return false; - } - } - - InitializationKind SubKind = InitializationKind::CreateForInit( - E->getExprLoc(), /*isDirectInit=*/false, E); - InitializationSequence SubSeq(S, SubEntity, SubKind, E); - - if (SubSeq.Failed()) { - if (!VerifyOnly) - SubSeq.Diagnose(S, SubEntity, SubKind, E); - else - Sequence.SetFailed( - InitializationSequence::FK_ParenthesizedListInitFailed); + auto HandleInitializedEntity = [&](const InitializedEntity &SubEntity, + const InitializationKind &SubKind, + Expr *Arg, Expr **InitExpr = nullptr) { + InitializationSequence IS = [&]() { + if (Arg) + return InitializationSequence(S, SubEntity, SubKind, Arg); + return InitializationSequence(S, SubEntity, SubKind, std::nullopt); + }(); - return false; - } - if (!VerifyOnly) { - ExprResult ER = SubSeq.Perform(S, SubEntity, SubKind, E); - InitExprs.push_back(ER.get()); - if (IsMember && IsUnionType) - InitializedFieldInUnion = cast(SubEntity.getDecl()); - } + if (IS.Failed()) { + if (!VerifyOnly) { + if (Arg) + IS.Diagnose(S, SubEntity, SubKind, Arg); + else + IS.Diagnose(S, SubEntity, SubKind, std::nullopt); } else { - // We've processed all of the args, but there are still entities that - // have to be initialized. - if (IsMember) { - // C++ [dcl.init]p17.6.2.2 - // The remaining elements are initialized with their default member - // initializers, if any - auto *FD = cast(SubEntity.getDecl()); - if (FD->hasInClassInitializer()) { - if (!VerifyOnly) { - ExprResult DIE = S.BuildCXXDefaultInitExpr(FD->getLocation(), FD); - if (DIE.isInvalid()) - return false; - S.checkInitializerLifetime(SubEntity, DIE.get()); - InitExprs.push_back(DIE.get()); - } - continue; - } - } - // Remaining class elements without default member initializers and - // array elements are value initialized: - // - // C++ [dcl.init]p17.6.2.2 - // The remaining elements...otherwise are value initialzed - // - // C++ [dcl.init]p17.5 - // if the destination type is an array, the object is initialized as - // . follows. Let x1, . . . , xk be the elements of the expression-list - // ...Let n denote the array size...the ith array element is...value- - // initialized for each k < i <= n. - InitializationKind SubKind = InitializationKind::CreateValue( - Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); - InitializationSequence SubSeq(S, SubEntity, SubKind, std::nullopt); - if (SubSeq.Failed()) { - if (!VerifyOnly) - SubSeq.Diagnose(S, SubEntity, SubKind, std::nullopt); - return false; - } - if (!VerifyOnly) { - ExprResult ER = SubSeq.Perform(S, SubEntity, SubKind, std::nullopt); - if (SubEntity.getKind() == InitializedEntity::EK_ArrayElement) { - ArrayFiller = ER.get(); - return true; - } - InitExprs.push_back(ER.get()); - } + Sequence.SetFailed( + InitializationSequence::FK_ParenthesizedListInitFailed); } + + return false; + } + if (!VerifyOnly) { + ExprResult ER; + if (Arg) + ER = IS.Perform(S, SubEntity, SubKind, Arg); + else + ER = IS.Perform(S, SubEntity, SubKind, std::nullopt); + if (InitExpr) + *InitExpr = ER.get(); + else + InitExprs.push_back(ER.get()); } return true; }; if (const ArrayType *AT = S.getASTContext().getAsArrayType(Entity.getType())) { - SmallVector ElementEntities; uint64_t ArrayLength; - // C++ [dcl.init]p17.5 + // C++ [dcl.init]p16.5 // if the destination type is an array, the object is initialized as // follows. Let x1, . . . , xk be the elements of the expression-list. If - // the destination type is an array of unknown bound, it is define as + // the destination type is an array of unknown bound, it is defined as // having k elements. if (const ConstantArrayType *CAT = - S.getASTContext().getAsConstantArrayType(Entity.getType())) + S.getASTContext().getAsConstantArrayType(Entity.getType())) { ArrayLength = CAT->getSize().getZExtValue(); - else + ResultType = Entity.getType(); + } else if (const VariableArrayType *VAT = + S.getASTContext().getAsVariableArrayType(Entity.getType())) { + // Braced-initialization of variable array types is not allowed, even if + // the size is greater than or equal to the number of args, so we don't + // allow them to be initialized via parenthesized aggregate initialization + // either. + const Expr *SE = VAT->getSizeExpr(); + S.Diag(SE->getBeginLoc(), diag::err_variable_object_no_init) + << SE->getSourceRange(); + return; + } else { + assert(isa(Entity.getType())); ArrayLength = Args.size(); + } + EntityIndexToProcess = ArrayLength; - if (ArrayLength >= Args.size()) { - for (uint64_t I = 0; I < ArrayLength; ++I) - ElementEntities.push_back( - InitializedEntity::InitializeElement(S.getASTContext(), I, Entity)); - - if (!ProcessEntities(ElementEntities)) + // ...the ith array element is copy-initialized with xi for each + // 1 <= i <= k + for (Expr *E : Args) { + InitializedEntity SubEntity = InitializedEntity::InitializeElement( + S.getASTContext(), EntityIndexToProcess, Entity); + InitializationKind SubKind = InitializationKind::CreateForInit( + E->getExprLoc(), /*isDirectInit=*/false, E); + if (!HandleInitializedEntity(SubEntity, SubKind, E)) + return; + } + // ...and value-initialized for each k < i <= n; + if (ArrayLength > Args.size()) { + InitializedEntity SubEntity = InitializedEntity::InitializeElement( + S.getASTContext(), Args.size(), Entity); + InitializationKind SubKind = InitializationKind::CreateValue( + Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); + if (!HandleInitializedEntity(SubEntity, SubKind, nullptr, &ArrayFiller)) return; + } + if (ResultType.isNull()) { ResultType = S.Context.getConstantArrayType( AT->getElementType(), llvm::APInt(/*numBits=*/32, ArrayLength), - nullptr, ArrayType::Normal, 0); + /*SizeExpr=*/nullptr, ArrayType::Normal, 0); } } else if (auto *RT = Entity.getType()->getAs()) { + bool IsUnion = RT->isUnionType(); const CXXRecordDecl *RD = cast(RT->getDecl()); - auto BaseRange = map_range(RD->bases(), [&](auto &base) { - return InitializedEntity::InitializeBase(S.getASTContext(), &base, false, - &Entity); - }); - auto FieldRange = map_range(RD->fields(), [](auto *field) { - return InitializedEntity::InitializeMember(field); - }); + if (!IsUnion) { + for (const CXXBaseSpecifier &Base : RD->bases()) { + InitializedEntity SubEntity = InitializedEntity::InitializeBase( + S.getASTContext(), &Base, false, &Entity); + if (EntityIndexToProcess < Args.size()) { + // C++ [dcl.init]p16.6.2.2. + // ...the object is initialized is follows. Let e1, ..., en be the + // elements of the aggregate([dcl.init.aggr]). Let x1, ..., xk be + // the elements of the expression-list...The element ei is + // copy-initialized with xi for 1 <= i <= k. + Expr *E = Args[EntityIndexToProcess]; + InitializationKind SubKind = InitializationKind::CreateForInit( + E->getExprLoc(), /*isDirectInit=*/false, E); + if (!HandleInitializedEntity(SubEntity, SubKind, E)) + return; + } else { + // We've processed all of the args, but there are still base classes + // that have to be initialized. + // C++ [dcl.init]p17.6.2.2 + // The remaining elements...otherwise are value initialzed + InitializationKind SubKind = InitializationKind::CreateValue( + Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), + /*IsImplicit=*/true); + if (!HandleInitializedEntity(SubEntity, SubKind, nullptr)) + return; + } + EntityIndexToProcess++; + } + } - if (!ProcessEntities(BaseRange)) - return; + for (FieldDecl *FD : RD->fields()) { + // Unnamed bitfields should not be initialized at all, either with an arg + // or by default. + if (FD->isUnnamedBitfield()) + continue; - if (!ProcessEntities(FieldRange)) - return; + InitializedEntity SubEntity = + InitializedEntity::InitializeMemberFromParenAggInit(FD); + if (EntityIndexToProcess < Args.size()) { + // ...The element ei is copy-initialized with xi for 1 <= i <= k. + Expr *E = Args[EntityIndexToProcess]; + + // Incomplete array types indicate flexible array members. Do not allow + // paren list initializations of structs with these members, as GCC + // doesn't either. + if (FD->getType()->isIncompleteArrayType()) { + if (!VerifyOnly) { + S.Diag(E->getBeginLoc(), diag::err_flexible_array_init) + << SourceRange(E->getBeginLoc(), E->getEndLoc()); + S.Diag(FD->getLocation(), diag::note_flexible_array_member) << FD; + } + Sequence.SetFailed( + InitializationSequence::FK_ParenthesizedListInitFailed); + return; + } + + InitializationKind SubKind = InitializationKind::CreateForInit( + E->getExprLoc(), /*isDirectInit=*/false, E); + if (!HandleInitializedEntity(SubEntity, SubKind, E)) + return; + + // Unions should have only one initializer expression, so we bail out + // after processing the first field. If there are more initializers then + // it will be caught when we later check whether EntityIndexToProcess is + // less than Args.size(); + if (IsUnion) { + InitializedFieldInUnion = FD; + EntityIndexToProcess = 1; + break; + } + } else { + // We've processed all of the args, but there are still members that + // have to be initialized. + if (FD->hasInClassInitializer()) { + if (!VerifyOnly) { + // C++ [dcl.init]p16.6.2.2 + // The remaining elements are initialized with their default + // member initializers, if any + ExprResult DIE = S.BuildCXXDefaultInitExpr(FD->getLocation(), FD); + if (DIE.isInvalid()) + return; + S.checkInitializerLifetime(SubEntity, DIE.get()); + InitExprs.push_back(DIE.get()); + } + } else { + // C++ [dcl.init]p17.6.2.2 + // The remaining elements...otherwise are value initialzed + if (FD->getType()->isReferenceType()) { + Sequence.SetFailed( + InitializationSequence::FK_ParenthesizedListInitFailed); + if (!VerifyOnly) { + SourceRange SR = Kind.getParenOrBraceRange(); + S.Diag(SR.getEnd(), diag::err_init_reference_member_uninitialized) + << FD->getType() << SR; + S.Diag(FD->getLocation(), diag::note_uninit_reference_member); + } + return; + } + InitializationKind SubKind = InitializationKind::CreateValue( + Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); + if (!HandleInitializedEntity(SubEntity, SubKind, nullptr)) + return; + } + } + EntityIndexToProcess++; + } ResultType = Entity.getType(); } // Not all of the args have been processed, so there must've been more args // than were required to initialize the element. - if (ArgIndexToProcess < Args.size()) { + if (EntityIndexToProcess < Args.size()) { Sequence.SetFailed(InitializationSequence::FK_ParenthesizedListInitFailed); if (!VerifyOnly) { QualType T = Entity.getType(); int InitKind = T->isArrayType() ? 0 : T->isUnionType() ? 3 : 4; - SourceRange ExcessInitSR(Args[ArgIndexToProcess]->getBeginLoc(), + SourceRange ExcessInitSR(Args[EntityIndexToProcess]->getBeginLoc(), Args.back()->getEndLoc()); S.Diag(Kind.getLocation(), diag::err_excess_initializers) << InitKind << ExcessInitSR; @@ -6441,6 +6493,7 @@ getAssignmentAction(const InitializedEntity &Entity, bool Diagnose = false) { return Sema::AA_Converting; case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: case InitializedEntity::EK_Binding: case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_VectorElement: @@ -6461,6 +6514,7 @@ static bool shouldBindAsTemporary(const InitializedEntity &Entity) { switch (Entity.getKind()) { case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: case InitializedEntity::EK_Result: case InitializedEntity::EK_StmtExprResult: case InitializedEntity::EK_New: @@ -6505,6 +6559,7 @@ static bool shouldDestroyEntity(const InitializedEntity &Entity) { return false; case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: case InitializedEntity::EK_Binding: case InitializedEntity::EK_Variable: case InitializedEntity::EK_Parameter: @@ -6541,6 +6596,7 @@ static SourceLocation getInitializationLoc(const InitializedEntity &Entity, case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: case InitializedEntity::EK_Parameter: case InitializedEntity::EK_Parameter_CF_Audited: case InitializedEntity::EK_TemplateParameter: @@ -7107,7 +7163,15 @@ static LifetimeResult getEntityLifetime( case InitializedEntity::EK_Exception: // FIXME: Can we diagnose lifetime problems with exceptions? return {nullptr, LK_FullExpression}; + + case InitializedEntity::EK_ParenAggInitMember: + // -- A temporary object bound to a reference element of an aggregate of + // class type initialized from a parenthesized expression-list + // [dcl.init, 9.3] persists until the completion of the full-expression + // containing the expression-list. + return {nullptr, LK_FullExpression}; } + llvm_unreachable("unknown entity kind"); } @@ -9223,7 +9287,9 @@ ExprResult InitializationSequence::Perform(Sema &S, S.checkInitializerLifetime(Entity, Init); // Diagnose non-fatal problems with the completed initialization. - if (Entity.getKind() == InitializedEntity::EK_Member && + if (InitializedEntity::EntityKind EK = Entity.getKind(); + (EK == InitializedEntity::EK_Member || + EK == InitializedEntity::EK_ParenAggInitMember) && cast(Entity.getDecl())->isBitField()) S.CheckBitFieldInitialization(Kind.getLocation(), cast(Entity.getDecl()), @@ -9677,7 +9743,8 @@ bool InitializationSequence::Diagnose(Sema &S, case OR_No_Viable_Function: if (Kind.getKind() == InitializationKind::IK_Default && (Entity.getKind() == InitializedEntity::EK_Base || - Entity.getKind() == InitializedEntity::EK_Member) && + Entity.getKind() == InitializedEntity::EK_Member || + Entity.getKind() == InitializedEntity::EK_ParenAggInitMember) && isa(S.CurContext)) { // This is implicit default initialization of a member or // base within a constructor. If no viable function was diff --git a/clang/test/CodeGen/paren-list-agg-init.cpp b/clang/test/CodeGen/paren-list-agg-init.cpp index 7e06a46..0e68beb 100644 --- a/clang/test/CodeGen/paren-list-agg-init.cpp +++ b/clang/test/CodeGen/paren-list-agg-init.cpp @@ -99,6 +99,14 @@ namespace gh62266 { }; } +namespace gh61567 { + // CHECK-DAG: [[STRUCT_I:%.*I.*]] = type { i32, ptr } + struct I { + int a; + int&& r = 2; + }; +} + // CHECK-DAG: [[A1:@.*a1.*]] = internal constant [[STRUCT_A]] { i8 3, double 2.000000e+00 }, align 8 constexpr A a1(3.1, 2.0); // CHECK-DAG: [[A2:@.*a2.*]] = internal constant [[STRUCT_A]] { i8 99, double 0.000000e+00 }, align 8 @@ -444,3 +452,64 @@ namespace gh62266 { H<2> h(1); } } + +namespace gh61567 { + int foo20(); + + // CHECK: define {{.*}} void @{{.*foo21.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_I]], align 8 + // CHECK-NEXT: [[REF_TMP:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 + // CHECK-NEXT: store i32 0, ptr [[A]], align 8 + // CHECK-NEXT: [[R:%.*r.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 + // CHECK-NEXT: store i32 1, ptr [[REF_TMP]], align 4 + // CHECK-NEXT: store ptr [[REF_TMP]], ptr [[R]], align 8 + // CHECK-NEXT: ret void + void foo21() { + I(0, 1); + } + + // CHECK: define {{.*}} void @{{.*foo22.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_I]], align 8 + // CHECK-NEXT: [[REF_TMP:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 + // CHECK-NEXT: store i32 0, ptr [[A]], align 8 + // CHECK-NEXT: [[R:%.*r.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 + // CHECK-NEXT: [[CALL:%.*call*]] = call noundef i32 @{{.*foo20.*}} + // CHECK-NEXT: store i32 [[CALL]], ptr [[REF_TMP]], align 4 + // CHECK-NEXT: store ptr [[REF_TMP]], ptr [[R]], align 8 + // CHECK-NEXT: ret void + void foo22() { + I(0, foo20()); + } + + // CHECK: define {{.*}} void @{{.*foo23.*}}(i32 noundef [[I:%.*i.*]]) + // CHECK-NEXT: entry + // CHECK-NEXT: [[I_ADDR:%.*i.*]] = alloca i32, align 4 + // CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_I]], align 8 + // CHECK-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4 + // CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 + // CHECK-NEXT: store i32 0, ptr [[A]], align 8 + // CHECK-NEXT: [[R:%.*r.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 + // CHECK-NEXT: store ptr [[I_ADDR]], ptr [[R]], align 8 + // CHECK-NEXT: ret void + void foo23(int i) { + I(0, static_cast(i)); + } + + // CHECK: define {{.*}} void @{{.*foo24.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_I]], align 8 + // CHECK-NEXT: [[REF_TMP:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 + // CHECK-NEXT: store i32 0, ptr [[A]], align 8 + // CHECK-NEXT: [[R:%.*r.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 + // CHECK-NEXT: store i32 2, ptr [[REF_TMP]], align 4 + // CHECK-NEXT: store ptr [[REF_TMP]], ptr [[R]], align 8 + // CHECK-NEXT: ret void + void foo24() { + I(0); + } +} diff --git a/clang/test/SemaCXX/paren-list-agg-init.cpp b/clang/test/SemaCXX/paren-list-agg-init.cpp index c9d7332..7bdf49b 100644 --- a/clang/test/SemaCXX/paren-list-agg-init.cpp +++ b/clang/test/SemaCXX/paren-list-agg-init.cpp @@ -9,7 +9,7 @@ struct A { // expected-note 4{{candidate constructor}} struct B { A a; int b[20]; - int &&c; // expected-note {{reference member declared here}} + int &&c; }; struct C { // expected-note 5{{candidate constructor}} @@ -21,9 +21,9 @@ struct D : public C, public A { int a; }; -struct E { // expected-note 3{{candidate constructor}} - struct F { - F(int, int); +struct E { + struct F { // expected-note 2{{candidate constructor}} + F(int, int); // expected-note {{candidate constructor}} }; int a; F f; @@ -56,6 +56,22 @@ struct J { int b[]; // expected-note {{initialized flexible array member 'b' is here}} }; +enum K { K0, K1, K2 }; + +struct L { + K k : 1; +}; + +struct M { + struct N { + private: + N(int); + // expected-note@-1 {{declared private here}} + }; + int i; + N n; +}; + union U { int a; char* b; @@ -74,7 +90,7 @@ T Construct(Args... args) { // beforecxx20-warning@-1 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} } -void foo() { +void foo(int n) { A a1(1954, 9, 21); // expected-error@-1 {{excess elements in struct initializer}} A a2(2.1); @@ -96,9 +112,8 @@ void foo() { B b1(2022, {7, 8}); // expected-error@-1 {{no viable conversion from 'int' to 'A'}} B b2(A(1), {}, 1); - // expected-error@-1 {{reference member 'c' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}} - // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} - // beforecxx20-warning@-3 {{aggregate initialization of type 'B' from a parenthesized list of values is a C++20 extension}} + // beforecxx20-warning@-1 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'B' from a parenthesized list of values is a C++20 extension}} C c(A(1), 1, 2, 3, 4); // expected-error@-1 {{array initializer must be an initializer list}} @@ -130,7 +145,7 @@ void foo() { // expected-error@-1 {{excess elements in union initializer}} E e1(1); - // expected-error@-1 {{no matching constructor for initialization of 'E'}} + // expected-error@-1 {{no matching constructor for initialization of 'F'}} constexpr F f1(1); // expected-error@-1 {{constexpr variable 'f1' must be initialized by a constant expression}} @@ -148,18 +163,29 @@ void foo() { A a7 = Construct('i', 2.2); // beforecxx20-note@-1 {{in instantiation of function template specialization 'Construct' requested here}} + L l(K::K2); + // expected-warning@-1 {{implicit truncation}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'L' from a parenthesized list of values is a C++20 extension}} + int arr4[](1, 2); // beforecxx20-warning@-1 {{aggregate initialization of type 'int[2]' from a parenthesized list of values is a C++20 extension}} int arr5[2](1, 2); // beforecxx20-warning@-1 {{aggregate initialization of type 'int[2]' from a parenthesized list of values is a C++20 extension}} + int arr6[n](1, 2, 3); + // expected-error@-1 {{variable-sized object may not be initialized}} + I i(1, 2); // expected-error@-1 {{no matching constructor for initialization of 'I'}} J j(1, {2, 3}); // expected-error@-1 {{initialization of flexible array member is not allowed}} + M m(1, 1); + // expected-error@-1 {{field of type 'N' has private constructor}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'M' from a parenthesized list of values is a C++20 extension}} + static_assert(__is_trivially_constructible(A, char, double)); static_assert(__is_trivially_constructible(A, char, int)); static_assert(__is_trivially_constructible(A, char)); @@ -221,5 +247,22 @@ M m(42); N n(43); // expected-error@-1 {{field of type 'L' has protected constructor}} // beforecxx20-warning@-2 {{aggregate initialization of type 'N' from a parenthesized list of values is a C++20 extension}} +} + +namespace gh61567 { +struct O { + int i; + int &&j; + // expected-note@-1 {{uninitialized reference member is here}} + int &&k = 1; +}; + +O o1(0, 0, 0); // no-error +// beforecxx20-warning@-1 {{aggregate initialization of type 'O' from a parenthesized list of values is a C++20 extension}} + +O o2(0, 0); // no-error +// beforecxx20-warning@-1 {{aggregate initialization of type 'O' from a parenthesized list of values is a C++20 extension}} +O o3(0); +// expected-error@-1 {{reference member of type 'int &&' uninitialized}} }