[clang] 377257f - [Clang] Support initializing structured bindings from an array with direct-list-initialization (#102581)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 27 14:18:24 PDT 2024
Author: Yanzuo Liu
Date: 2024-08-27T23:18:20+02:00
New Revision: 377257f063c2b828462499b9e570aed726825064
URL: https://github.com/llvm/llvm-project/commit/377257f063c2b828462499b9e570aed726825064
DIFF: https://github.com/llvm/llvm-project/commit/377257f063c2b828462499b9e570aed726825064.diff
LOG: [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.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Sema/Initialization.h
clang/lib/Sema/SemaInit.cpp
clang/test/SemaCXX/cxx1z-decomposition.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2c29d49ba20f03..d9fa068c2910f4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -318,6 +318,7 @@ Bug Fixes to C++ Support
of the current instantiation in all cases.
- Fix evaluation of the index of dependent pack indexing expressions/types specifiers (#GH105900)
- Correctly handle subexpressions of an immediate invocation in the presence of implicit casts. (#GH105558)
+- Clang now correctly handles direct-list-initialization of a structured bindings from an array. (#GH31813)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index 4b876db436b48c..0455e1fa5016bb 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -1384,6 +1384,11 @@ class InitializationSequence {
void AddParenthesizedListInitStep(QualType T);
+ /// Only used when initializing structured bindings from an array with
+ /// direct-list-initialization. Unwrap the initializer list to get the array
+ /// for array copy.
+ void AddUnwrapInitListInitStep(InitListExpr *Syntactic);
+
/// Add steps to unwrap a initializer list for a reference around a
/// single element and rewrap it at the end.
void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic);
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index dcfe3bc80c87ac..5a19a3505454ca 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -4091,6 +4091,16 @@ void InitializationSequence::AddParenthesizedListInitStep(QualType T) {
Steps.push_back(S);
}
+void InitializationSequence::AddUnwrapInitListInitStep(
+ InitListExpr *Syntactic) {
+ assert(Syntactic->getNumInits() == 1 &&
+ "Can only unwrap trivial init lists.");
+ Step S;
+ S.Kind = SK_UnwrapInitList;
+ S.Type = Syntactic->getInit(0)->getType();
+ Steps.insert(Steps.begin(), S);
+}
+
void InitializationSequence::RewrapReferenceInitList(QualType T,
InitListExpr *Syntactic) {
assert(Syntactic->getNumInits() == 1 &&
@@ -4167,6 +4177,33 @@ static void MaybeProduceObjCObject(Sema &S,
}
}
+/// Initialize an array from another array
+static void TryArrayCopy(Sema &S, const InitializationKind &Kind,
+ const InitializedEntity &Entity, Expr *Initializer,
+ QualType DestType, InitializationSequence &Sequence,
+ bool TreatUnavailableAsInvalid) {
+ // If source is a prvalue, use it directly.
+ if (Initializer->isPRValue()) {
+ Sequence.AddArrayInitStep(DestType, /*IsGNUExtension*/ false);
+ return;
+ }
+
+ // Emit element-at-a-time copy loop.
+ InitializedEntity Element =
+ InitializedEntity::InitializeElement(S.Context, 0, Entity);
+ QualType InitEltT =
+ S.Context.getAsArrayType(Initializer->getType())->getElementType();
+ OpaqueValueExpr OVE(Initializer->getExprLoc(), InitEltT,
+ Initializer->getValueKind(),
+ Initializer->getObjectKind());
+ Expr *OVEAsExpr = &OVE;
+ Sequence.InitializeFrom(S, Element, Kind, OVEAsExpr,
+ /*TopLevelOfInitList*/ false,
+ TreatUnavailableAsInvalid);
+ if (Sequence)
+ Sequence.AddArrayInitLoopStep(Entity.getType(), InitEltT);
+}
+
static void TryListInitialization(Sema &S,
const InitializedEntity &Entity,
const InitializationKind &Kind,
@@ -4775,6 +4812,31 @@ static void TryListInitialization(Sema &S,
}
if (const ArrayType *DestAT = S.Context.getAsArrayType(DestType)) {
Expr *SubInit[1] = {InitList->getInit(0)};
+
+ // C++17 [dcl.struct.bind]p1:
+ // ... If the assignment-expression in the initializer has array type A
+ // and no ref-qualifier is present, e has type cv A and each element is
+ // copy-initialized or direct-initialized from the corresponding element
+ // of the assignment-expression as specified by the form of the
+ // initializer. ...
+ //
+ // This is a special case not following list-initialization.
+ if (isa<ConstantArrayType>(DestAT) &&
+ Entity.getKind() == InitializedEntity::EK_Variable &&
+ isa<DecompositionDecl>(Entity.getDecl())) {
+ assert(
+ S.Context.hasSameUnqualifiedType(SubInit[0]->getType(), DestType) &&
+ "Deduced to other type?");
+ TryArrayCopy(S,
+ InitializationKind::CreateCopy(Kind.getLocation(),
+ InitList->getLBraceLoc()),
+ Entity, SubInit[0], DestType, Sequence,
+ TreatUnavailableAsInvalid);
+ if (Sequence)
+ Sequence.AddUnwrapInitListInitStep(InitList);
+ return;
+ }
+
if (!isa<VariableArrayType>(DestAT) &&
IsStringInit(SubInit[0], DestAT, S.Context) == SIF_None) {
InitializationKind SubKind =
@@ -6461,25 +6523,8 @@ void InitializationSequence::InitializeFrom(Sema &S,
S.Context.hasSameUnqualifiedType(Initializer->getType(),
Entity.getType()) &&
canPerformArrayCopy(Entity)) {
- // If source is a prvalue, use it directly.
- if (Initializer->isPRValue()) {
- AddArrayInitStep(DestType, /*IsGNUExtension*/false);
- return;
- }
-
- // Emit element-at-a-time copy loop.
- InitializedEntity Element =
- InitializedEntity::InitializeElement(S.Context, 0, Entity);
- QualType InitEltT =
- Context.getAsArrayType(Initializer->getType())->getElementType();
- OpaqueValueExpr OVE(Initializer->getExprLoc(), InitEltT,
- Initializer->getValueKind(),
- Initializer->getObjectKind());
- Expr *OVEAsExpr = &OVE;
- InitializeFrom(S, Element, Kind, OVEAsExpr, TopLevelOfInitList,
- TreatUnavailableAsInvalid);
- if (!Failed())
- AddArrayInitLoopStep(Entity.getType(), InitEltT);
+ TryArrayCopy(S, Kind, Entity, Initializer, DestType, *this,
+ TreatUnavailableAsInvalid);
return;
}
diff --git a/clang/test/SemaCXX/cxx1z-decomposition.cpp b/clang/test/SemaCXX/cxx1z-decomposition.cpp
index 19c730303625ee..a8914fe4e9cd82 100644
--- a/clang/test/SemaCXX/cxx1z-decomposition.cpp
+++ b/clang/test/SemaCXX/cxx1z-decomposition.cpp
@@ -198,4 +198,40 @@ namespace lambdas {
}
}
-// FIXME: by-value array copies
+namespace by_value_array_copy {
+ struct explicit_copy {
+ explicit_copy() = default; // expected-note 2{{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
+ explicit explicit_copy(const explicit_copy&) = default; // expected-note 2{{explicit constructor is not a candidate}}
+ };
+
+ constexpr int direct_initialization_for_elements() {
+ explicit_copy ec_arr[2];
+ auto [a1, b1](ec_arr);
+
+ int arr[3]{1, 2, 3};
+ auto [a2, b2, c2](arr);
+ arr[0]--;
+ return a2 + b2 + c2 + arr[0];
+ }
+ static_assert(direct_initialization_for_elements() == 6);
+
+ constexpr int copy_initialization_for_elements() {
+ int arr[2]{4, 5};
+ auto [a1, b1] = arr;
+ auto [a2, b2]{arr}; // GH31813
+ arr[0] = 0;
+ return a1 + b1 + a2 + b2 + arr[0];
+ }
+ static_assert(copy_initialization_for_elements() == 18);
+
+ void copy_initialization_for_elements_with_explicit_copy_ctor() {
+ explicit_copy ec_arr[2];
+ auto [a1, b1] = ec_arr; // expected-error {{no matching constructor for initialization of 'explicit_copy[2]'}}
+ auto [a2, b2]{ec_arr}; // expected-error {{no matching constructor for initialization of 'explicit_copy[2]'}}
+
+ // Test prvalue
+ using T = explicit_copy[2];
+ auto [a3, b3] = T{};
+ auto [a4, b4]{T{}};
+ }
+} // namespace by_value_array_copy
More information about the cfe-commits
mailing list