Skip to content

Commit 377257f

Browse files
authored
[Clang] Support initializing structured bindings from an array with direct-list-initialization (#102581)
When initializing structured bindings from an array with direct-list-initialization, array copy will be performed, which is a special case not following list-initialization. This PR adds support for this case. Fixes #31813.
1 parent b6b6482 commit 377257f

File tree

4 files changed

+107
-20
lines changed

4 files changed

+107
-20
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ Bug Fixes to C++ Support
318318
of the current instantiation in all cases.
319319
- Fix evaluation of the index of dependent pack indexing expressions/types specifiers (#GH105900)
320320
- Correctly handle subexpressions of an immediate invocation in the presence of implicit casts. (#GH105558)
321+
- Clang now correctly handles direct-list-initialization of a structured bindings from an array. (#GH31813)
321322

322323
Bug Fixes to AST Handling
323324
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Initialization.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,11 @@ class InitializationSequence {
13841384

13851385
void AddParenthesizedListInitStep(QualType T);
13861386

1387+
/// Only used when initializing structured bindings from an array with
1388+
/// direct-list-initialization. Unwrap the initializer list to get the array
1389+
/// for array copy.
1390+
void AddUnwrapInitListInitStep(InitListExpr *Syntactic);
1391+
13871392
/// Add steps to unwrap a initializer list for a reference around a
13881393
/// single element and rewrap it at the end.
13891394
void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic);

clang/lib/Sema/SemaInit.cpp

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4091,6 +4091,16 @@ void InitializationSequence::AddParenthesizedListInitStep(QualType T) {
40914091
Steps.push_back(S);
40924092
}
40934093

4094+
void InitializationSequence::AddUnwrapInitListInitStep(
4095+
InitListExpr *Syntactic) {
4096+
assert(Syntactic->getNumInits() == 1 &&
4097+
"Can only unwrap trivial init lists.");
4098+
Step S;
4099+
S.Kind = SK_UnwrapInitList;
4100+
S.Type = Syntactic->getInit(0)->getType();
4101+
Steps.insert(Steps.begin(), S);
4102+
}
4103+
40944104
void InitializationSequence::RewrapReferenceInitList(QualType T,
40954105
InitListExpr *Syntactic) {
40964106
assert(Syntactic->getNumInits() == 1 &&
@@ -4167,6 +4177,33 @@ static void MaybeProduceObjCObject(Sema &S,
41674177
}
41684178
}
41694179

4180+
/// Initialize an array from another array
4181+
static void TryArrayCopy(Sema &S, const InitializationKind &Kind,
4182+
const InitializedEntity &Entity, Expr *Initializer,
4183+
QualType DestType, InitializationSequence &Sequence,
4184+
bool TreatUnavailableAsInvalid) {
4185+
// If source is a prvalue, use it directly.
4186+
if (Initializer->isPRValue()) {
4187+
Sequence.AddArrayInitStep(DestType, /*IsGNUExtension*/ false);
4188+
return;
4189+
}
4190+
4191+
// Emit element-at-a-time copy loop.
4192+
InitializedEntity Element =
4193+
InitializedEntity::InitializeElement(S.Context, 0, Entity);
4194+
QualType InitEltT =
4195+
S.Context.getAsArrayType(Initializer->getType())->getElementType();
4196+
OpaqueValueExpr OVE(Initializer->getExprLoc(), InitEltT,
4197+
Initializer->getValueKind(),
4198+
Initializer->getObjectKind());
4199+
Expr *OVEAsExpr = &OVE;
4200+
Sequence.InitializeFrom(S, Element, Kind, OVEAsExpr,
4201+
/*TopLevelOfInitList*/ false,
4202+
TreatUnavailableAsInvalid);
4203+
if (Sequence)
4204+
Sequence.AddArrayInitLoopStep(Entity.getType(), InitEltT);
4205+
}
4206+
41704207
static void TryListInitialization(Sema &S,
41714208
const InitializedEntity &Entity,
41724209
const InitializationKind &Kind,
@@ -4775,6 +4812,31 @@ static void TryListInitialization(Sema &S,
47754812
}
47764813
if (const ArrayType *DestAT = S.Context.getAsArrayType(DestType)) {
47774814
Expr *SubInit[1] = {InitList->getInit(0)};
4815+
4816+
// C++17 [dcl.struct.bind]p1:
4817+
// ... If the assignment-expression in the initializer has array type A
4818+
// and no ref-qualifier is present, e has type cv A and each element is
4819+
// copy-initialized or direct-initialized from the corresponding element
4820+
// of the assignment-expression as specified by the form of the
4821+
// initializer. ...
4822+
//
4823+
// This is a special case not following list-initialization.
4824+
if (isa<ConstantArrayType>(DestAT) &&
4825+
Entity.getKind() == InitializedEntity::EK_Variable &&
4826+
isa<DecompositionDecl>(Entity.getDecl())) {
4827+
assert(
4828+
S.Context.hasSameUnqualifiedType(SubInit[0]->getType(), DestType) &&
4829+
"Deduced to other type?");
4830+
TryArrayCopy(S,
4831+
InitializationKind::CreateCopy(Kind.getLocation(),
4832+
InitList->getLBraceLoc()),
4833+
Entity, SubInit[0], DestType, Sequence,
4834+
TreatUnavailableAsInvalid);
4835+
if (Sequence)
4836+
Sequence.AddUnwrapInitListInitStep(InitList);
4837+
return;
4838+
}
4839+
47784840
if (!isa<VariableArrayType>(DestAT) &&
47794841
IsStringInit(SubInit[0], DestAT, S.Context) == SIF_None) {
47804842
InitializationKind SubKind =
@@ -6461,25 +6523,8 @@ void InitializationSequence::InitializeFrom(Sema &S,
64616523
S.Context.hasSameUnqualifiedType(Initializer->getType(),
64626524
Entity.getType()) &&
64636525
canPerformArrayCopy(Entity)) {
6464-
// If source is a prvalue, use it directly.
6465-
if (Initializer->isPRValue()) {
6466-
AddArrayInitStep(DestType, /*IsGNUExtension*/false);
6467-
return;
6468-
}
6469-
6470-
// Emit element-at-a-time copy loop.
6471-
InitializedEntity Element =
6472-
InitializedEntity::InitializeElement(S.Context, 0, Entity);
6473-
QualType InitEltT =
6474-
Context.getAsArrayType(Initializer->getType())->getElementType();
6475-
OpaqueValueExpr OVE(Initializer->getExprLoc(), InitEltT,
6476-
Initializer->getValueKind(),
6477-
Initializer->getObjectKind());
6478-
Expr *OVEAsExpr = &OVE;
6479-
InitializeFrom(S, Element, Kind, OVEAsExpr, TopLevelOfInitList,
6480-
TreatUnavailableAsInvalid);
6481-
if (!Failed())
6482-
AddArrayInitLoopStep(Entity.getType(), InitEltT);
6526+
TryArrayCopy(S, Kind, Entity, Initializer, DestType, *this,
6527+
TreatUnavailableAsInvalid);
64836528
return;
64846529
}
64856530

clang/test/SemaCXX/cxx1z-decomposition.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,40 @@ namespace lambdas {
198198
}
199199
}
200200

201-
// FIXME: by-value array copies
201+
namespace by_value_array_copy {
202+
struct explicit_copy {
203+
explicit_copy() = default; // expected-note 2{{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
204+
explicit explicit_copy(const explicit_copy&) = default; // expected-note 2{{explicit constructor is not a candidate}}
205+
};
206+
207+
constexpr int direct_initialization_for_elements() {
208+
explicit_copy ec_arr[2];
209+
auto [a1, b1](ec_arr);
210+
211+
int arr[3]{1, 2, 3};
212+
auto [a2, b2, c2](arr);
213+
arr[0]--;
214+
return a2 + b2 + c2 + arr[0];
215+
}
216+
static_assert(direct_initialization_for_elements() == 6);
217+
218+
constexpr int copy_initialization_for_elements() {
219+
int arr[2]{4, 5};
220+
auto [a1, b1] = arr;
221+
auto [a2, b2]{arr}; // GH31813
222+
arr[0] = 0;
223+
return a1 + b1 + a2 + b2 + arr[0];
224+
}
225+
static_assert(copy_initialization_for_elements() == 18);
226+
227+
void copy_initialization_for_elements_with_explicit_copy_ctor() {
228+
explicit_copy ec_arr[2];
229+
auto [a1, b1] = ec_arr; // expected-error {{no matching constructor for initialization of 'explicit_copy[2]'}}
230+
auto [a2, b2]{ec_arr}; // expected-error {{no matching constructor for initialization of 'explicit_copy[2]'}}
231+
232+
// Test prvalue
233+
using T = explicit_copy[2];
234+
auto [a3, b3] = T{};
235+
auto [a4, b4]{T{}};
236+
}
237+
} // namespace by_value_array_copy

0 commit comments

Comments
 (0)