Skip to content

Commit 10cd7c2

Browse files
committed
[clang] Refine handling of C++20 aggregate initialization
* Move parts of `InitializationSequence::InitializeFrom` corresponding to C++ [dcl.init.general] p16.6.1 and p16.6.2 into a separate function, `TryConstructorOrParenListInitialization` * Use it in `TryListInitialization` to implement [dcl.init.list] p3.2 * Fix parenthesized aggregate initialization being attempted in copy-initialization contexts or when the constructor call is ambiguous
1 parent 8392573 commit 10cd7c2

File tree

3 files changed

+183
-56
lines changed

3 files changed

+183
-56
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ Bug Fixes to C++ Support
330330
- Fixed an assertion failure affecting code that uses C++23 "deducing this". (#GH130272)
331331
- Clang now properly instantiates destructors for initialized members within non-delegating constructors. (#GH93251)
332332
- Correctly diagnoses if unresolved using declarations shadows template paramters (#GH129411)
333+
- Fixed C++20 aggregate initialization rules being incorrectly applied in certain contexts. (#GH131320)
333334

334335
Bug Fixes to AST Handling
335336
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaInit.cpp

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4633,6 +4633,59 @@ static void TryConstructorInitialization(Sema &S,
46334633
IsListInit | IsInitListCopy, AsInitializerList);
46344634
}
46354635

4636+
static void TryOrBuildParenListInitialization(
4637+
Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
4638+
ArrayRef<Expr *> Args, InitializationSequence &Sequence, bool VerifyOnly,
4639+
ExprResult *Result = nullptr);
4640+
4641+
/// Attempt to initialize an object of a class type either by
4642+
/// direct-initialization, or by copy-initialization from an
4643+
/// expression of the same or derived class type. This corresponds
4644+
/// to the first two sub-bullets of C++2c [dcl.init.general] p16.6.
4645+
///
4646+
/// \param IsAggrListInit Is this non-list-initialization being done as
4647+
/// part of a list-initialization of an aggregate
4648+
/// from a single expression of the same or
4649+
/// derived class type (C++2c [dcl.init.list] p3.2)?
4650+
static void TryConstructorOrParenListInitialization(
4651+
Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
4652+
MultiExprArg Args, QualType DestType, InitializationSequence &Sequence,
4653+
bool IsAggrListInit) {
4654+
// C++2c [dcl.init.general] p16.6:
4655+
// * Otherwise, if the destination type is a class type:
4656+
// * If the initializer expression is a prvalue and
4657+
// the cv-unqualified version of the source type is the same
4658+
// as the destination type, the initializer expression is used
4659+
// to initialize the destination object.
4660+
// * Otherwise, if the initialization is direct-initialization,
4661+
// or if it is copy-initialization where the cv-unqualified
4662+
// version of the source type is the same as or is derived from
4663+
// the class of the destination type, constructors are considered.
4664+
// The applicable constructors are enumerated, and the best one
4665+
// is chosen through overload resolution. Then:
4666+
// * If overload resolution is successful, the selected
4667+
// constructor is called to initialize the object, with
4668+
// the initializer expression or expression-list as its
4669+
// argument(s).
4670+
TryConstructorInitialization(S, Entity, Kind, Args, DestType, DestType,
4671+
Sequence, /*IsListInit=*/false, IsAggrListInit);
4672+
4673+
// * Otherwise, if no constructor is viable, the destination type
4674+
// is an aggregate class, and the initializer is a parenthesized
4675+
// expression-list, the object is initialized as follows. [...]
4676+
// Parenthesized initialization of aggregates is a C++20 feature.
4677+
if (S.getLangOpts().CPlusPlus20 &&
4678+
Kind.getKind() == InitializationKind::IK_Direct && Sequence.Failed() &&
4679+
Sequence.getFailureKind() ==
4680+
InitializationSequence::FK_ConstructorOverloadFailed &&
4681+
Sequence.getFailedOverloadResult() == OR_No_Viable_Function &&
4682+
(IsAggrListInit || DestType->isAggregateType()))
4683+
TryOrBuildParenListInitialization(S, Entity, Kind, Args, Sequence,
4684+
/*VerifyOnly=*/true);
4685+
4686+
// * Otherwise, the initialization is ill-formed.
4687+
}
4688+
46364689
static bool
46374690
ResolveOverloadedFunctionForReferenceBinding(Sema &S,
46384691
Expr *Initializer,
@@ -4846,11 +4899,16 @@ static void TryListInitialization(Sema &S,
48464899
QualType InitType = InitList->getInit(0)->getType();
48474900
if (S.Context.hasSameUnqualifiedType(InitType, DestType) ||
48484901
S.IsDerivedFrom(InitList->getBeginLoc(), InitType, DestType)) {
4902+
InitializationKind SubKind =
4903+
Kind.getKind() == InitializationKind::IK_DirectList
4904+
? InitializationKind::CreateDirect(Kind.getLocation(),
4905+
InitList->getLBraceLoc(),
4906+
InitList->getRBraceLoc())
4907+
: Kind;
48494908
Expr *InitListAsExpr = InitList;
4850-
TryConstructorInitialization(S, Entity, Kind, InitListAsExpr, DestType,
4851-
DestType, Sequence,
4852-
/*InitListSyntax*/false,
4853-
/*IsInitListCopy*/true);
4909+
TryConstructorOrParenListInitialization(
4910+
S, Entity, SubKind, InitListAsExpr, DestType, Sequence,
4911+
/*IsAggrListInit=*/true);
48544912
return;
48554913
}
48564914
}
@@ -5709,7 +5767,7 @@ static void TryDefaultInitialization(Sema &S,
57095767
static void TryOrBuildParenListInitialization(
57105768
Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
57115769
ArrayRef<Expr *> Args, InitializationSequence &Sequence, bool VerifyOnly,
5712-
ExprResult *Result = nullptr) {
5770+
ExprResult *Result) {
57135771
unsigned EntityIndexToProcess = 0;
57145772
SmallVector<Expr *, 4> InitExprs;
57155773
QualType ResultType;
@@ -6688,42 +6746,8 @@ void InitializationSequence::InitializeFrom(Sema &S,
66886746
(Context.hasSameUnqualifiedType(SourceType, DestType) ||
66896747
(Initializer && S.IsDerivedFrom(Initializer->getBeginLoc(),
66906748
SourceType, DestType))))) {
6691-
TryConstructorInitialization(S, Entity, Kind, Args, DestType, DestType,
6692-
*this);
6693-
6694-
// We fall back to the "no matching constructor" path if the
6695-
// failed candidate set has functions other than the three default
6696-
// constructors. For example, conversion function.
6697-
if (const auto *RD =
6698-
dyn_cast<CXXRecordDecl>(DestType->getAs<RecordType>()->getDecl());
6699-
// In general, we should call isCompleteType for RD to check its
6700-
// completeness, we don't call it here as it was already called in the
6701-
// above TryConstructorInitialization.
6702-
S.getLangOpts().CPlusPlus20 && RD && RD->hasDefinition() &&
6703-
RD->isAggregate() && Failed() &&
6704-
getFailureKind() == FK_ConstructorOverloadFailed) {
6705-
// Do not attempt paren list initialization if overload resolution
6706-
// resolves to a deleted function .
6707-
//
6708-
// We may reach this condition if we have a union wrapping a class with
6709-
// a non-trivial copy or move constructor and we call one of those two
6710-
// constructors. The union is an aggregate, but the matched constructor
6711-
// is implicitly deleted, so we need to prevent aggregate initialization
6712-
// (otherwise, it'll attempt aggregate initialization by initializing
6713-
// the first element with a reference to the union).
6714-
OverloadCandidateSet::iterator Best;
6715-
OverloadingResult OR = getFailedCandidateSet().BestViableFunction(
6716-
S, Kind.getLocation(), Best);
6717-
if (OR != OverloadingResult::OR_Deleted) {
6718-
// C++20 [dcl.init] 17.6.2.2:
6719-
// - Otherwise, if no constructor is viable, the destination type is
6720-
// an
6721-
// aggregate class, and the initializer is a parenthesized
6722-
// expression-list.
6723-
TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this,
6724-
/*VerifyOnly=*/true);
6725-
}
6726-
}
6749+
TryConstructorOrParenListInitialization(S, Entity, Kind, Args, DestType,
6750+
*this, /*IsAggrListInit=*/false);
67276751
} else {
67286752
// - Otherwise (i.e., for the remaining copy-initialization cases),
67296753
// user-defined conversion sequences that can convert from the
Lines changed: 117 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,120 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
22

3-
// If the initializer is (), the object is value-initialized.
4-
5-
// expected-no-diagnostics
63
namespace GH69890 {
7-
struct A {
8-
constexpr A() {}
9-
int x;
10-
};
11-
12-
struct B : A {
13-
int y;
14-
};
15-
16-
static_assert(B().x == 0);
17-
static_assert(B().y == 0);
18-
}
4+
// If the initializer is (), the object is value-initialized.
5+
struct A {
6+
constexpr A() {}
7+
int x;
8+
};
9+
10+
struct B : A {
11+
int y;
12+
};
13+
14+
static_assert(B().x == 0);
15+
static_assert(B().y == 0);
16+
} // namespace GH69890
17+
18+
namespace P0960R3 {
19+
struct A { // expected-note 22 {{candidate constructor}}
20+
int i;
21+
operator int() volatile;
22+
};
23+
volatile A va;
24+
25+
A a1(va);
26+
A a2 = va; // expected-error {{no matching constructor for initialization of 'A'}}
27+
A a3 {va};
28+
A a4 = {va}; // expected-error {{no matching constructor for initialization of 'A'}}
29+
30+
A f() {
31+
return va; // expected-error {{no matching constructor for initialization of 'A'}}
32+
return {va}; // expected-error {{no matching constructor for initialization of 'A'}}
33+
}
34+
35+
int g(A); // expected-note 2 {{passing argument to parameter here}}
36+
int i = g(va); // expected-error {{no matching constructor for initialization of 'A'}}
37+
int j = g({va}); // expected-error {{no matching constructor for initialization of 'A'}}
38+
39+
struct Ambig {
40+
operator const A&(); // expected-note {{candidate function}}
41+
operator A&&(); // expected-note {{candidate function}}
42+
operator int();
43+
};
44+
45+
A a5(Ambig {}); // expected-error {{call to constructor of 'A' is ambiguous}}
46+
A a6 = Ambig {}; // expected-error {{conversion from 'Ambig' to 'A' is ambiguous}}
47+
A a7 {Ambig {}};
48+
A a8 = {Ambig {}};
49+
50+
A a9(1);
51+
A a10 = 1; // expected-error {{no viable conversion from 'int' to 'A'}}
52+
A a11 {1};
53+
A a12 = {1};
54+
55+
56+
struct B { // expected-note 12 {{candidate constructor}}
57+
int i;
58+
virtual operator int() volatile;
59+
};
60+
volatile B vb;
61+
62+
B b1(vb); // expected-error {{no matching constructor for initialization of 'B'}}
63+
B b2 = vb; // expected-error {{no matching constructor for initialization of 'B'}}
64+
B b3 {vb}; // expected-error {{no matching constructor for initialization of 'B'}}
65+
B b4 = {vb}; // expected-error {{no matching constructor for initialization of 'B'}}
66+
67+
68+
struct Immovable {
69+
Immovable();
70+
Immovable(const Immovable&) = delete; // #Imm_copy
71+
};
72+
73+
struct C { // #C
74+
int i;
75+
Immovable j; // #C_j
76+
77+
operator int() volatile;
78+
};
79+
C c;
80+
volatile C vc;
81+
82+
C c1(c); // expected-error {{call to implicitly-deleted copy constructor of 'C'}}
83+
C c2 = c; // expected-error {{call to implicitly-deleted copy constructor of 'C'}}
84+
C c3 {c}; // expected-error {{call to implicitly-deleted copy constructor of 'C'}}
85+
C c4 = {c}; // expected-error {{call to implicitly-deleted copy constructor of 'C'}}
86+
// expected-note@#C_j 4 {{copy constructor of 'C' is implicitly deleted}}
87+
// expected-note@#Imm_copy 4 {{'Immovable' has been explicitly marked deleted here}}
88+
89+
C c5(vc);
90+
C c6 = vc; // expected-error {{no matching constructor for initialization of 'C'}}
91+
C c7 {vc};
92+
C c8 = {vc}; // expected-error {{no matching constructor for initialization of 'C'}}
93+
// expected-note@#C 4 {{candidate constructor}}
94+
95+
C c9(C {});
96+
C c10 = C(123);
97+
C c11 {C {0, Immovable()}};
98+
C c12 = {C()};
99+
100+
101+
struct D { // expected-note 6 {{candidate constructor}}
102+
int i;
103+
};
104+
105+
struct DD : private D { // expected-note 4 {{declared private here}}
106+
virtual operator int() volatile;
107+
};
108+
DD dd;
109+
volatile DD vdd;
110+
111+
D d1(dd); // expected-error {{cannot cast 'const DD' to its private base class 'const D'}}
112+
D d2 = dd; // expected-error {{cannot cast 'const DD' to its private base class 'const D'}}
113+
D d3 {dd}; // expected-error {{cannot cast 'const DD' to its private base class 'const D'}}
114+
D d4 = {dd}; // expected-error {{cannot cast 'const DD' to its private base class 'const D'}}
115+
116+
D d5(vdd);
117+
D d6 = vdd; // expected-error {{no matching constructor for initialization of 'D'}}
118+
D d7 {vdd};
119+
D d8 = {vdd}; // expected-error {{no matching constructor for initialization of 'D'}}
120+
} // namespace P0960R3

0 commit comments

Comments
 (0)