aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Zhao <ayzhao@google.com>2023-04-13 11:04:59 -0700
committerTom Stellard <tstellar@redhat.com>2023-05-30 22:10:17 -0700
commit84078877549b6b1b62a1dbe232108d67266de4bf (patch)
tree1a2a5636da7aea67998a408df10ff361326ba74e
parent3a69d8111157b18b77daed6008e762afdf913eee (diff)
downloadllvm-84078877549b6b1b62a1dbe232108d67266de4bf.zip
llvm-84078877549b6b1b62a1dbe232108d67266de4bf.tar.gz
llvm-84078877549b6b1b62a1dbe232108d67266de4bf.tar.bz2
[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
-rw-r--r--clang/docs/ReleaseNotes.rst3
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td3
-rw-r--r--clang/include/clang/Sema/Initialization.h18
-rw-r--r--clang/lib/Sema/SemaAccess.cpp3
-rw-r--r--clang/lib/Sema/SemaInit.cpp335
-rw-r--r--clang/test/CodeGen/paren-list-agg-init.cpp69
-rw-r--r--clang/test/SemaCXX/paren-list-agg-init.cpp61
7 files changed, 345 insertions, 147 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 105cfd9..bc770c8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -824,6 +824,9 @@ Bug Fixes to C++ Support
- Fix default member initializers sometimes being ignored when performing
parenthesized aggregate initialization of templated types.
(`#62266 <https://github.com/llvm/llvm-project/issues/62266>`_)
+- Fix overly aggressive lifetime checks for parenthesized aggregate
+ initialization.
+ (`#61567 <https://github.com/llvm/llvm-project/issues/61567>`_)
Concepts Specific Fixes:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index bfe582d..6d72040 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2124,7 +2124,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 e5a98ba..e1bbea0 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<FieldDecl>(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 44adb16..09fd481 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");
}
@@ -3348,6 +3349,7 @@ DeclarationName InitializedEntity::getName() const {
case EK_Variable:
case EK_Member:
+ case EK_ParenAggInitMember:
case EK_Binding:
case EK_TemplateParameter:
return Variable.VariableOrMember->getDeclName();
@@ -3379,6 +3381,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;
@@ -3420,6 +3423,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:
@@ -3454,7 +3458,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;
@@ -5274,179 +5281,224 @@ static void TryOrBuildParenListInitialization(
Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
ArrayRef<Expr *> Args, InitializationSequence &Sequence, bool VerifyOnly,
ExprResult *Result = nullptr) {
- unsigned ArgIndexToProcess = 0;
+ unsigned EntityIndexToProcess = 0;
SmallVector<Expr *, 4> 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<FieldDecl>(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<FieldDecl>(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<FieldDecl>(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<FieldDecl>(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<InitializedEntity, 4> 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<IncompleteArrayType>(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<RecordType>()) {
+ bool IsUnion = RT->isUnionType();
const CXXRecordDecl *RD = cast<CXXRecordDecl>(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
- // then were required to initialize the element.
- if (ArgIndexToProcess < Args.size()) {
+ // than were required to initialize the element.
+ 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;
@@ -6412,6 +6464,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:
@@ -6432,6 +6485,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:
@@ -6476,6 +6530,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:
@@ -6512,6 +6567,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:
@@ -7080,7 +7136,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");
}
@@ -9196,7 +9260,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<FieldDecl>(Entity.getDecl())->isBitField())
S.CheckBitFieldInitialization(Kind.getLocation(),
cast<FieldDecl>(Entity.getDecl()),
@@ -9650,7 +9716,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<CXXConstructorDecl>(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<int&&>(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<A>('i', 2.2);
// beforecxx20-note@-1 {{in instantiation of function template specialization 'Construct<A, char, double>' 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}}
}