[clang] [Clang] Fix handling of brace ellison when building deduction guides (PR #94889)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Wed Jun 12 20:41:13 PDT 2024


https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/94889

>From 217c00f47aaa65b113d1c1cfd93a9c4e1d235c1a Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 9 Jun 2024 11:49:18 +0800
Subject: [PATCH 1/7] [Clang] Fix two issues of CTAD for aggregates

---
 clang/lib/Sema/SemaInit.cpp                 | 56 +++++++++++++++------
 clang/test/SemaTemplate/deduction-guide.cpp | 19 +++++++
 2 files changed, 60 insertions(+), 15 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 79bdc8e9f8783..de2ea639bbba8 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -313,6 +313,8 @@ class InitListChecker {
   InitListExpr *FullyStructuredList = nullptr;
   NoInitExpr *DummyExpr = nullptr;
   SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr;
+  SmallVectorImpl<QualType>
+      *AggrDeductionCandidateParamTypesWithoutBraceElision = nullptr;
 
   NoInitExpr *getDummyInit() {
     if (!DummyExpr)
@@ -506,14 +508,19 @@ class InitListChecker {
       Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
       bool VerifyOnly, bool TreatUnavailableAsInvalid,
       bool InOverloadResolution = false,
-      SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr);
+      SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr,
+      SmallVectorImpl<QualType>
+          *AggrDeductionCandidateParamTypesWithoutBraceElision = nullptr);
   InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL,
                   QualType &T,
-                  SmallVectorImpl<QualType> &AggrDeductionCandidateParamTypes)
+                  SmallVectorImpl<QualType> &AggrDeductionCandidateParamTypes,
+                  SmallVectorImpl<QualType>
+                      &AggrDeductionCandidateParamTypesWithoutBraceElision)
       : InitListChecker(S, Entity, IL, T, /*VerifyOnly=*/true,
                         /*TreatUnavailableAsInvalid=*/false,
                         /*InOverloadResolution=*/false,
-                        &AggrDeductionCandidateParamTypes){};
+                        &AggrDeductionCandidateParamTypes,
+                        &AggrDeductionCandidateParamTypesWithoutBraceElision) {}
 
   bool HadError() { return hadError; }
 
@@ -982,11 +989,15 @@ static bool hasAnyDesignatedInits(const InitListExpr *IL) {
 InitListChecker::InitListChecker(
     Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
     bool VerifyOnly, bool TreatUnavailableAsInvalid, bool InOverloadResolution,
-    SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes)
+    SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes,
+    SmallVectorImpl<QualType>
+        *AggrDeductionCandidateParamTypesWithoutBraceElision)
     : SemaRef(S), VerifyOnly(VerifyOnly),
       TreatUnavailableAsInvalid(TreatUnavailableAsInvalid),
       InOverloadResolution(InOverloadResolution),
-      AggrDeductionCandidateParamTypes(AggrDeductionCandidateParamTypes) {
+      AggrDeductionCandidateParamTypes(AggrDeductionCandidateParamTypes),
+      AggrDeductionCandidateParamTypesWithoutBraceElision(
+          AggrDeductionCandidateParamTypesWithoutBraceElision) {
   if (!VerifyOnly || hasAnyDesignatedInits(IL)) {
     FullyStructuredList =
         createInitListExpr(T, IL->getSourceRange(), IL->getNumInits());
@@ -1448,13 +1459,17 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
       //   brace elision is not considered for any aggregate element that has a
       //   dependent non-array type or an array type with a value-dependent
       //   bound
-      assert(AggrDeductionCandidateParamTypes);
-      if (!isa_and_nonnull<ConstantArrayType>(
+      assert(AggrDeductionCandidateParamTypes &&
+             AggrDeductionCandidateParamTypesWithoutBraceElision);
+      if (!isa_and_present<ConstantArrayType>(
               SemaRef.Context.getAsArrayType(ElemType))) {
         ++Index;
         AggrDeductionCandidateParamTypes->push_back(ElemType);
         return;
       }
+      // For array types with known bounds, we still want the brace version even
+      // though the braces can be elided.
+      AggrDeductionCandidateParamTypesWithoutBraceElision->push_back(ElemType);
     } else {
       InitializationSequence Seq(SemaRef, TmpEntity, Kind, expr,
                                  /*TopLevelOfInitList*/ true);
@@ -10918,22 +10933,24 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
       if (!(RD->getDefinition() && RD->isAggregate()))
         return;
       QualType Ty = Context.getRecordType(RD);
-      SmallVector<QualType, 8> ElementTypes;
-
-      InitListChecker CheckInitList(*this, Entity, ListInit, Ty, ElementTypes);
-      if (!CheckInitList.HadError()) {
+      auto BuildAggregateDeductionGuide = [&](MutableArrayRef<QualType>
+                                                  ElementTypes,
+                                              bool BracedVersion = false) {
+        if (ElementTypes.empty())
+          return;
         // C++ [over.match.class.deduct]p1.8:
         //   if e_i is of array type and x_i is a braced-init-list, T_i is an
         //   rvalue reference to the declared type of e_i and
         // C++ [over.match.class.deduct]p1.9:
-        //   if e_i is of array type and x_i is a bstring-literal, T_i is an
+        //   if e_i is of array type and x_i is a string-literal, T_i is an
         //   lvalue reference to the const-qualified declared type of e_i and
         // C++ [over.match.class.deduct]p1.10:
         //   otherwise, T_i is the declared type of e_i
-        for (int I = 0, E = ListInit->getNumInits();
+        for (int I = 0, E = BracedVersion ? ElementTypes.size()
+                                          : ListInit->getNumInits();
              I < E && !isa<PackExpansionType>(ElementTypes[I]); ++I)
           if (ElementTypes[I]->isArrayType()) {
-            if (isa<InitListExpr>(ListInit->getInit(I)))
+            if (isa<InitListExpr, DesignatedInitExpr>(ListInit->getInit(I)))
               ElementTypes[I] = Context.getRValueReferenceType(ElementTypes[I]);
             else if (isa<StringLiteral>(
                          ListInit->getInit(I)->IgnoreParenImpCasts()))
@@ -10951,7 +10968,16 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
                                 /*AllowAggregateDeductionCandidate=*/true);
           HasAnyDeductionGuide = true;
         }
-      }
+      };
+      SmallVector<QualType, 8> ElementTypes, ElementTypesWithoutBraceElision;
+
+      InitListChecker CheckInitList(*this, Entity, ListInit, Ty, ElementTypes,
+                                    ElementTypesWithoutBraceElision);
+      if (CheckInitList.HadError())
+        return;
+      BuildAggregateDeductionGuide(ElementTypes);
+      BuildAggregateDeductionGuide(ElementTypesWithoutBraceElision,
+                                   /*BracedVersion=*/true);
     };
 
     for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) {
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index 758ca14e4b1c3..a72b2f0de1a58 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -335,3 +335,22 @@ namespace TTP {
 // CHECK-NEXT:      `-TemplateArgument type 'T':'type-parameter-0-0'{{$}}
 // CHECK-NEXT:        `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0{{$}}
 // CHECK-NEXT:          `-TemplateTypeParm {{.+}} 'T'{{$}}
+
+namespace GH83368 {
+
+template <int N> struct A {
+  int f1[N];
+};
+
+A a{.f1 = {1}};
+
+} // namespace GH83368
+
+namespace GH64625 {
+template <class T> struct X {
+  T t[2];
+};
+
+X x = {{1, 2}};
+
+} // namespace GH64625

>From 6c723e85d59317cf415b8aa96fc7327772078391 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Jun 2024 11:49:56 +0800
Subject: [PATCH 2/7] Release notes & AST verification.

---
 clang/docs/ReleaseNotes.rst                 |  1 +
 clang/test/SemaTemplate/deduction-guide.cpp | 69 ++++++++++++++++++---
 2 files changed, 61 insertions(+), 9 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0c700d23257bf..e5efcae2289c6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -823,6 +823,7 @@ Bug Fixes to C++ Support
   differering by their constraints when only one of these function was variadic.
 - Fix a crash when a variable is captured by a block nested inside a lambda. (Fixes #GH93625).
 - Fixed a type constraint substitution issue involving a generic lambda expression. (#GH93821)
+- Fixed two issues when building CTAD deduction guides for aggregates. (#GH64625), (#GH83368).
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index a72b2f0de1a58..1cce455d966df 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -336,6 +336,50 @@ namespace TTP {
 // CHECK-NEXT:        `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0{{$}}
 // CHECK-NEXT:          `-TemplateTypeParm {{.+}} 'T'{{$}}
 
+namespace GH64625 {
+
+template <class T> struct X {
+  T t[2];
+};
+
+X x = {{1, 2}}, y = {1, 2};
+
+// CHECK-LABEL: Dumping GH64625::<deduction guide for X>:
+// CHECK-NEXT: FunctionTemplateDecl {{.+}} <{{.+}}:[[#@LINE - 7]]:1, col:27> col:27 implicit <deduction guide for X>
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} <col:11, col:17> col:17 referenced class depth 0 index 0 T
+// CHECK:      |-CXXDeductionGuideDecl {{.+}} <col:27> col:27 implicit <deduction guide for X> 'auto (T (&&)[2]) -> X<T>' aggregate 
+// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:27> col:27 'T (&&)[2]'
+// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:27> col:27 implicit used <deduction guide for X> 'auto (int (&&)[2]) -> GH64625::X<int>' implicit_instantiation aggregate 
+// CHECK-NEXT:  |-TemplateArgument type 'int'
+// CHECK-NEXT:  | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT:  `-ParmVarDecl {{.+}} <col:27> col:27 'int (&&)[2]'
+// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T (&&)[2]) -> X<T>' dependent trailing_return
+// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'X<T>' dependent
+// CHECK-NEXT: | `-CXXRecord {{.+}} 'X'
+// CHECK-NEXT: `-RValueReferenceType {{.+}} 'T (&&)[2]' dependent
+// CHECK-NEXT:  `-ConstantArrayType {{.+}} 'T[2]' dependent 2 
+// CHECK-NEXT:    `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT:      `-TemplateTypeParm {{.+}} 'T'
+
+// Brace-elision version:
+// CHECK:      |-CXXDeductionGuideDecl {{.+}} <col:27> col:27 implicit <deduction guide for X> 'auto (T, T) -> X<T>' aggregate 
+// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:27> col:27 'T'
+// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:27> col:27 'T'
+// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:27> col:27 implicit used <deduction guide for X> 'auto (int, int) -> GH64625::X<int>' implicit_instantiation aggregate 
+// CHECK-NEXT:   |-TemplateArgument type 'int'
+// CHECK-NEXT:   | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT:   |-ParmVarDecl {{.+}} <col:27> col:27 'int'
+// CHECK-NEXT:   `-ParmVarDecl {{.+}} <col:27> col:27 'int'
+// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T, T) -> X<T>' dependent trailing_return
+// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'X<T>' dependent
+// CHECK-NEXT: | `-CXXRecord {{.+}} 'X'
+// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'T'
+// CHECK-NEXT: `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT:  `-TemplateTypeParm {{.+}} 'T'
+
+} // namespace GH64625
+
 namespace GH83368 {
 
 template <int N> struct A {
@@ -344,13 +388,20 @@ template <int N> struct A {
 
 A a{.f1 = {1}};
 
-} // namespace GH83368
+// CHECK-LABEL: Dumping GH83368::<deduction guide for A>:
+// CHECK-NEXT: FunctionTemplateDecl 0x{{.+}} <{{.+}}:[[#@LINE - 7]]:1, col:25> col:25 implicit <deduction guide for A>
+// CHECK-NEXT: |-NonTypeTemplateParmDecl {{.+}} <col:11, col:15> col:15 referenced 'int' depth 0 index 0 N
+// CHECK:      |-CXXDeductionGuideDecl {{.+}} <col:25> col:25 implicit <deduction guide for A> 'auto (int (&&)[N]) -> A<N>' aggregate
+// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:25> col:25 'int (&&)[N]'
+// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:25> col:25 implicit used <deduction guide for A> 'auto (int (&&)[1]) -> GH83368::A<1>' implicit_instantiation aggregate 
+// CHECK-NEXT:   |-TemplateArgument integral '1'
+// CHECK-NEXT:   `-ParmVarDecl {{.+}} <col:25> col:25 'int (&&)[1]'
+// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (int (&&)[N]) -> A<N>' dependent trailing_return
+// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'A<N>' dependent
+// CHECK-NEXT: | `-CXXRecord {{.+}} 'A'
+// CHECK-NEXT: `-RValueReferenceType {{.+}} 'int (&&)[N]' dependent
+// CHECK-NEXT:   `-DependentSizedArrayType {{.+}} 'int[N]' dependent
+// CHECK-NEXT:     |-BuiltinType {{.+}} 'int'
+// CHECK-NEXT:     `-DeclRefExpr {{.+}} <col:10> 'int' NonTypeTemplateParm {{.+}} 'N' 'int'
 
-namespace GH64625 {
-template <class T> struct X {
-  T t[2];
-};
-
-X x = {{1, 2}};
-
-} // namespace GH64625
+} // namespace GH83368

>From da3df4040f5810916f1b12054f569959158cb6a1 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Jun 2024 23:07:18 +0800
Subject: [PATCH 3/7] Apply Corentin's feedback

---
 clang/docs/ReleaseNotes.rst |   2 +-
 clang/lib/Sema/SemaInit.cpp | 103 ++++++++++++++++++++----------------
 2 files changed, 59 insertions(+), 46 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e5efcae2289c6..b8712a60a37c1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -823,7 +823,7 @@ Bug Fixes to C++ Support
   differering by their constraints when only one of these function was variadic.
 - Fix a crash when a variable is captured by a block nested inside a lambda. (Fixes #GH93625).
 - Fixed a type constraint substitution issue involving a generic lambda expression. (#GH93821)
-- Fixed two issues when building CTAD deduction guides for aggregates. (#GH64625), (#GH83368).
+- Fixed handling of brace ellison when building deduction guides. (#GH64625), (#GH83368).
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index de2ea639bbba8..c47c2b3533920 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -305,6 +305,24 @@ namespace {
 /// structured list even in 'verify only' mode, so that we can track which
 /// elements need 'empty' initializtion.
 class InitListChecker {
+public:
+  struct CandidateParamTypesForAggregateDeduction {
+    /// Pointer to a container that would hold the parameter types of a
+    /// deduction guide for an aggregate.
+    SmallVectorImpl<QualType> *ParamTypes;
+    /// Pointer to a container that would hold the parameter types of a
+    /// deduction guide for an aggregate as if the brace elision were not
+    /// applied.
+    SmallVectorImpl<QualType> *ParamTypesWithoutBraceElision;
+
+    CandidateParamTypesForAggregateDeduction(
+        SmallVectorImpl<QualType> *ParamTypes = nullptr,
+        SmallVectorImpl<QualType> *ParamTypesWithoutBraceElision = nullptr)
+        : ParamTypes(ParamTypes),
+          ParamTypesWithoutBraceElision(ParamTypesWithoutBraceElision) {}
+  };
+
+private:
   Sema &SemaRef;
   bool hadError = false;
   bool VerifyOnly; // No diagnostics.
@@ -312,9 +330,7 @@ class InitListChecker {
   bool InOverloadResolution;
   InitListExpr *FullyStructuredList = nullptr;
   NoInitExpr *DummyExpr = nullptr;
-  SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr;
-  SmallVectorImpl<QualType>
-      *AggrDeductionCandidateParamTypesWithoutBraceElision = nullptr;
+  CandidateParamTypesForAggregateDeduction ParamTypesForAggregateDeduction;
 
   NoInitExpr *getDummyInit() {
     if (!DummyExpr)
@@ -508,19 +524,16 @@ class InitListChecker {
       Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
       bool VerifyOnly, bool TreatUnavailableAsInvalid,
       bool InOverloadResolution = false,
-      SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr,
-      SmallVectorImpl<QualType>
-          *AggrDeductionCandidateParamTypesWithoutBraceElision = nullptr);
-  InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL,
-                  QualType &T,
-                  SmallVectorImpl<QualType> &AggrDeductionCandidateParamTypes,
-                  SmallVectorImpl<QualType>
-                      &AggrDeductionCandidateParamTypesWithoutBraceElision)
+      CandidateParamTypesForAggregateDeduction ParamTypesForAggregateDeduction =
+          CandidateParamTypesForAggregateDeduction());
+  InitListChecker(
+      Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
+      CandidateParamTypesForAggregateDeduction ParamTypesForAggregateDeduction)
       : InitListChecker(S, Entity, IL, T, /*VerifyOnly=*/true,
                         /*TreatUnavailableAsInvalid=*/false,
                         /*InOverloadResolution=*/false,
-                        &AggrDeductionCandidateParamTypes,
-                        &AggrDeductionCandidateParamTypesWithoutBraceElision) {}
+                        /*ParamTypesForAggregateDeduction=*/
+                        std::move(ParamTypesForAggregateDeduction)) {}
 
   bool HadError() { return hadError; }
 
@@ -989,15 +1002,11 @@ static bool hasAnyDesignatedInits(const InitListExpr *IL) {
 InitListChecker::InitListChecker(
     Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
     bool VerifyOnly, bool TreatUnavailableAsInvalid, bool InOverloadResolution,
-    SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes,
-    SmallVectorImpl<QualType>
-        *AggrDeductionCandidateParamTypesWithoutBraceElision)
+    CandidateParamTypesForAggregateDeduction ParamsForDeduction)
     : SemaRef(S), VerifyOnly(VerifyOnly),
       TreatUnavailableAsInvalid(TreatUnavailableAsInvalid),
       InOverloadResolution(InOverloadResolution),
-      AggrDeductionCandidateParamTypes(AggrDeductionCandidateParamTypes),
-      AggrDeductionCandidateParamTypesWithoutBraceElision(
-          AggrDeductionCandidateParamTypesWithoutBraceElision) {
+      ParamTypesForAggregateDeduction(std::move(ParamsForDeduction)) {
   if (!VerifyOnly || hasAnyDesignatedInits(IL)) {
     FullyStructuredList =
         createInitListExpr(T, IL->getSourceRange(), IL->getNumInits());
@@ -1011,7 +1020,7 @@ InitListChecker::InitListChecker(
   CheckExplicitInitList(Entity, IL, T, FullyStructuredList,
                         /*TopLevelObject=*/true);
 
-  if (!hadError && !AggrDeductionCandidateParamTypes && FullyStructuredList) {
+  if (!hadError && !ParamsForDeduction.ParamTypes && FullyStructuredList) {
     bool RequiresSecondPass = false;
     FillInEmptyInitializations(Entity, FullyStructuredList, RequiresSecondPass,
                                /*OuterILE=*/nullptr, /*OuterIndex=*/0);
@@ -1395,8 +1404,8 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity,
     //   brace elision is not considered for any aggregate element that has a
     //   dependent non-array type or an array type with a value-dependent bound
     ++Index;
-    assert(AggrDeductionCandidateParamTypes);
-    AggrDeductionCandidateParamTypes->push_back(DeclType);
+    assert(ParamTypesForAggregateDeduction.ParamTypes);
+    ParamTypesForAggregateDeduction.ParamTypes->push_back(DeclType);
   } else {
     if (!VerifyOnly)
       SemaRef.Diag(IList->getBeginLoc(), diag::err_illegal_initializer_type)
@@ -1459,17 +1468,18 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
       //   brace elision is not considered for any aggregate element that has a
       //   dependent non-array type or an array type with a value-dependent
       //   bound
-      assert(AggrDeductionCandidateParamTypes &&
-             AggrDeductionCandidateParamTypesWithoutBraceElision);
+      assert(ParamTypesForAggregateDeduction.ParamTypes &&
+             ParamTypesForAggregateDeduction.ParamTypesWithoutBraceElision);
       if (!isa_and_present<ConstantArrayType>(
               SemaRef.Context.getAsArrayType(ElemType))) {
         ++Index;
-        AggrDeductionCandidateParamTypes->push_back(ElemType);
+        ParamTypesForAggregateDeduction.ParamTypes->push_back(ElemType);
         return;
       }
-      // For array types with known bounds, we still want the brace version even
-      // though the braces can be elided.
-      AggrDeductionCandidateParamTypesWithoutBraceElision->push_back(ElemType);
+      // For array types with known bounds, we still want a deduction guide for
+      // the brace initializer even though the brace can be elided.
+      ParamTypesForAggregateDeduction.ParamTypesWithoutBraceElision->push_back(
+          ElemType);
     } else {
       InitializationSequence Seq(SemaRef, TmpEntity, Kind, expr,
                                  /*TopLevelOfInitList*/ true);
@@ -1494,8 +1504,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
                                       getDummyInit());
         }
         ++Index;
-        if (AggrDeductionCandidateParamTypes)
-          AggrDeductionCandidateParamTypes->push_back(ElemType);
+        if (ParamTypesForAggregateDeduction.ParamTypes)
+          ParamTypesForAggregateDeduction.ParamTypes->push_back(ElemType);
         return;
       }
     }
@@ -1715,8 +1725,8 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity,
   }
   UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr);
   ++Index;
-  if (AggrDeductionCandidateParamTypes)
-    AggrDeductionCandidateParamTypes->push_back(DeclType);
+  if (ParamTypesForAggregateDeduction.ParamTypes)
+    ParamTypesForAggregateDeduction.ParamTypes->push_back(DeclType);
 }
 
 void InitListChecker::CheckReferenceType(const InitializedEntity &Entity,
@@ -1772,8 +1782,8 @@ void InitListChecker::CheckReferenceType(const InitializedEntity &Entity,
 
   UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
   ++Index;
-  if (AggrDeductionCandidateParamTypes)
-    AggrDeductionCandidateParamTypes->push_back(DeclType);
+  if (ParamTypesForAggregateDeduction.ParamTypes)
+    ParamTypesForAggregateDeduction.ParamTypes->push_back(DeclType);
 }
 
 void InitListChecker::CheckVectorType(const InitializedEntity &Entity,
@@ -1825,8 +1835,8 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity,
       }
       UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr);
       ++Index;
-      if (AggrDeductionCandidateParamTypes)
-        AggrDeductionCandidateParamTypes->push_back(elementType);
+      if (ParamTypesForAggregateDeduction.ParamTypes)
+        ParamTypesForAggregateDeduction.ParamTypes->push_back(elementType);
       return;
     }
 
@@ -1990,8 +2000,8 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
         StructuredList->resizeInits(SemaRef.Context, StructuredIndex);
       }
       ++Index;
-      if (AggrDeductionCandidateParamTypes)
-        AggrDeductionCandidateParamTypes->push_back(DeclType);
+      if (ParamTypesForAggregateDeduction.ParamTypes)
+        ParamTypesForAggregateDeduction.ParamTypes->push_back(DeclType);
       return;
     }
   }
@@ -2229,8 +2239,8 @@ void InitListChecker::CheckStructUnionTypes(
     //   trailing sequence of parameters corresponding to a trailing
     //   aggregate element that is a pack expansion (if any) is replaced
     //   by a single parameter of the form T_n....
-    if (AggrDeductionCandidateParamTypes && Base.isPackExpansion()) {
-      AggrDeductionCandidateParamTypes->push_back(
+    if (ParamTypesForAggregateDeduction.ParamTypes && Base.isPackExpansion()) {
+      ParamTypesForAggregateDeduction.ParamTypes->push_back(
           SemaRef.Context.getPackExpansionType(Base.getType(), std::nullopt));
 
       // Trailing pack expansion
@@ -2467,7 +2477,7 @@ void InitListChecker::CheckStructUnionTypes(
     InitializedEntity::InitializeMember(*Field, &Entity);
 
   if (isa<InitListExpr>(IList->getInit(Index)) ||
-      AggrDeductionCandidateParamTypes)
+      ParamTypesForAggregateDeduction.ParamTypes)
     CheckSubElementType(MemberEntity, IList, Field->getType(), Index,
                         StructuredList, StructuredIndex);
   else
@@ -2619,8 +2629,9 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
                                     Result.get());
       }
       ++Index;
-      if (AggrDeductionCandidateParamTypes)
-        AggrDeductionCandidateParamTypes->push_back(CurrentObjectType);
+      if (ParamTypesForAggregateDeduction.ParamTypes)
+        ParamTypesForAggregateDeduction.ParamTypes->push_back(
+            CurrentObjectType);
       return !Seq;
     }
 
@@ -10971,8 +10982,10 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
       };
       SmallVector<QualType, 8> ElementTypes, ElementTypesWithoutBraceElision;
 
-      InitListChecker CheckInitList(*this, Entity, ListInit, Ty, ElementTypes,
-                                    ElementTypesWithoutBraceElision);
+      InitListChecker CheckInitList(
+          *this, Entity, ListInit, Ty,
+          InitListChecker::CandidateParamTypesForAggregateDeduction(
+              &ElementTypes, &ElementTypesWithoutBraceElision));
       if (CheckInitList.HadError())
         return;
       BuildAggregateDeductionGuide(ElementTypes);

>From c644f1089b083119c303eaae4f7962171bb767af Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 11 Jun 2024 23:28:22 +0800
Subject: [PATCH 4/7] Fix handling of two or more aggregates

---
 clang/lib/Sema/SemaInit.cpp                 | 116 ++++++++++----------
 clang/test/SemaTemplate/deduction-guide.cpp |  49 ++++++---
 2 files changed, 87 insertions(+), 78 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index c47c2b3533920..8cfc541164494 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -305,32 +305,16 @@ namespace {
 /// structured list even in 'verify only' mode, so that we can track which
 /// elements need 'empty' initializtion.
 class InitListChecker {
-public:
-  struct CandidateParamTypesForAggregateDeduction {
-    /// Pointer to a container that would hold the parameter types of a
-    /// deduction guide for an aggregate.
-    SmallVectorImpl<QualType> *ParamTypes;
-    /// Pointer to a container that would hold the parameter types of a
-    /// deduction guide for an aggregate as if the brace elision were not
-    /// applied.
-    SmallVectorImpl<QualType> *ParamTypesWithoutBraceElision;
-
-    CandidateParamTypesForAggregateDeduction(
-        SmallVectorImpl<QualType> *ParamTypes = nullptr,
-        SmallVectorImpl<QualType> *ParamTypesWithoutBraceElision = nullptr)
-        : ParamTypes(ParamTypes),
-          ParamTypesWithoutBraceElision(ParamTypesWithoutBraceElision) {}
-  };
-
-private:
   Sema &SemaRef;
   bool hadError = false;
   bool VerifyOnly; // No diagnostics.
   bool TreatUnavailableAsInvalid; // Used only in VerifyOnly mode.
   bool InOverloadResolution;
+  bool BraceElisionOccurredForDeductionGuides = false;
+  bool NoBraceElisionForDeductionGuides;
   InitListExpr *FullyStructuredList = nullptr;
   NoInitExpr *DummyExpr = nullptr;
-  CandidateParamTypesForAggregateDeduction ParamTypesForAggregateDeduction;
+  SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr;
 
   NoInitExpr *getDummyInit() {
     if (!DummyExpr)
@@ -524,22 +508,26 @@ class InitListChecker {
       Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
       bool VerifyOnly, bool TreatUnavailableAsInvalid,
       bool InOverloadResolution = false,
-      CandidateParamTypesForAggregateDeduction ParamTypesForAggregateDeduction =
-          CandidateParamTypesForAggregateDeduction());
-  InitListChecker(
-      Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
-      CandidateParamTypesForAggregateDeduction ParamTypesForAggregateDeduction)
+      bool NoBraceElisionForDeductionGuides = false,
+      SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr);
+  InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL,
+                  QualType &T, bool NoBraceElisionForDeductionGuides,
+                  SmallVectorImpl<QualType> &AggrDeductionCandidateParamTypes)
       : InitListChecker(S, Entity, IL, T, /*VerifyOnly=*/true,
                         /*TreatUnavailableAsInvalid=*/false,
                         /*InOverloadResolution=*/false,
-                        /*ParamTypesForAggregateDeduction=*/
-                        std::move(ParamTypesForAggregateDeduction)) {}
+                        NoBraceElisionForDeductionGuides,
+                        &AggrDeductionCandidateParamTypes) {}
 
   bool HadError() { return hadError; }
 
   // Retrieves the fully-structured initializer list used for
   // semantic analysis and code generation.
   InitListExpr *getFullyStructuredList() const { return FullyStructuredList; }
+
+  bool HadBraceElisionOccurredForDeductionGuides() const {
+    return BraceElisionOccurredForDeductionGuides;
+  }
 };
 
 } // end anonymous namespace
@@ -1002,11 +990,13 @@ static bool hasAnyDesignatedInits(const InitListExpr *IL) {
 InitListChecker::InitListChecker(
     Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
     bool VerifyOnly, bool TreatUnavailableAsInvalid, bool InOverloadResolution,
-    CandidateParamTypesForAggregateDeduction ParamsForDeduction)
+    bool NoBraceElisionForDeductionGuides,
+    SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes)
     : SemaRef(S), VerifyOnly(VerifyOnly),
       TreatUnavailableAsInvalid(TreatUnavailableAsInvalid),
       InOverloadResolution(InOverloadResolution),
-      ParamTypesForAggregateDeduction(std::move(ParamsForDeduction)) {
+      NoBraceElisionForDeductionGuides(NoBraceElisionForDeductionGuides),
+      AggrDeductionCandidateParamTypes(AggrDeductionCandidateParamTypes) {
   if (!VerifyOnly || hasAnyDesignatedInits(IL)) {
     FullyStructuredList =
         createInitListExpr(T, IL->getSourceRange(), IL->getNumInits());
@@ -1020,7 +1010,7 @@ InitListChecker::InitListChecker(
   CheckExplicitInitList(Entity, IL, T, FullyStructuredList,
                         /*TopLevelObject=*/true);
 
-  if (!hadError && !ParamsForDeduction.ParamTypes && FullyStructuredList) {
+  if (!hadError && !AggrDeductionCandidateParamTypes && FullyStructuredList) {
     bool RequiresSecondPass = false;
     FillInEmptyInitializations(Entity, FullyStructuredList, RequiresSecondPass,
                                /*OuterILE=*/nullptr, /*OuterIndex=*/0);
@@ -1404,8 +1394,8 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity,
     //   brace elision is not considered for any aggregate element that has a
     //   dependent non-array type or an array type with a value-dependent bound
     ++Index;
-    assert(ParamTypesForAggregateDeduction.ParamTypes);
-    ParamTypesForAggregateDeduction.ParamTypes->push_back(DeclType);
+    assert(AggrDeductionCandidateParamTypes);
+    AggrDeductionCandidateParamTypes->push_back(DeclType);
   } else {
     if (!VerifyOnly)
       SemaRef.Diag(IList->getBeginLoc(), diag::err_illegal_initializer_type)
@@ -1468,18 +1458,15 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
       //   brace elision is not considered for any aggregate element that has a
       //   dependent non-array type or an array type with a value-dependent
       //   bound
-      assert(ParamTypesForAggregateDeduction.ParamTypes &&
-             ParamTypesForAggregateDeduction.ParamTypesWithoutBraceElision);
+      assert(AggrDeductionCandidateParamTypes);
       if (!isa_and_present<ConstantArrayType>(
-              SemaRef.Context.getAsArrayType(ElemType))) {
+              SemaRef.Context.getAsArrayType(ElemType)) ||
+          NoBraceElisionForDeductionGuides) {
         ++Index;
-        ParamTypesForAggregateDeduction.ParamTypes->push_back(ElemType);
+        AggrDeductionCandidateParamTypes->push_back(ElemType);
         return;
       }
-      // For array types with known bounds, we still want a deduction guide for
-      // the brace initializer even though the brace can be elided.
-      ParamTypesForAggregateDeduction.ParamTypesWithoutBraceElision->push_back(
-          ElemType);
+      BraceElisionOccurredForDeductionGuides = true;
     } else {
       InitializationSequence Seq(SemaRef, TmpEntity, Kind, expr,
                                  /*TopLevelOfInitList*/ true);
@@ -1504,8 +1491,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
                                       getDummyInit());
         }
         ++Index;
-        if (ParamTypesForAggregateDeduction.ParamTypes)
-          ParamTypesForAggregateDeduction.ParamTypes->push_back(ElemType);
+        if (AggrDeductionCandidateParamTypes)
+          AggrDeductionCandidateParamTypes->push_back(ElemType);
         return;
       }
     }
@@ -1725,8 +1712,8 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity,
   }
   UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr);
   ++Index;
-  if (ParamTypesForAggregateDeduction.ParamTypes)
-    ParamTypesForAggregateDeduction.ParamTypes->push_back(DeclType);
+  if (AggrDeductionCandidateParamTypes)
+    AggrDeductionCandidateParamTypes->push_back(DeclType);
 }
 
 void InitListChecker::CheckReferenceType(const InitializedEntity &Entity,
@@ -1782,8 +1769,8 @@ void InitListChecker::CheckReferenceType(const InitializedEntity &Entity,
 
   UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
   ++Index;
-  if (ParamTypesForAggregateDeduction.ParamTypes)
-    ParamTypesForAggregateDeduction.ParamTypes->push_back(DeclType);
+  if (AggrDeductionCandidateParamTypes)
+    AggrDeductionCandidateParamTypes->push_back(DeclType);
 }
 
 void InitListChecker::CheckVectorType(const InitializedEntity &Entity,
@@ -1835,8 +1822,8 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity,
       }
       UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr);
       ++Index;
-      if (ParamTypesForAggregateDeduction.ParamTypes)
-        ParamTypesForAggregateDeduction.ParamTypes->push_back(elementType);
+      if (AggrDeductionCandidateParamTypes)
+        AggrDeductionCandidateParamTypes->push_back(elementType);
       return;
     }
 
@@ -2000,8 +1987,8 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
         StructuredList->resizeInits(SemaRef.Context, StructuredIndex);
       }
       ++Index;
-      if (ParamTypesForAggregateDeduction.ParamTypes)
-        ParamTypesForAggregateDeduction.ParamTypes->push_back(DeclType);
+      if (AggrDeductionCandidateParamTypes)
+        AggrDeductionCandidateParamTypes->push_back(DeclType);
       return;
     }
   }
@@ -2239,8 +2226,8 @@ void InitListChecker::CheckStructUnionTypes(
     //   trailing sequence of parameters corresponding to a trailing
     //   aggregate element that is a pack expansion (if any) is replaced
     //   by a single parameter of the form T_n....
-    if (ParamTypesForAggregateDeduction.ParamTypes && Base.isPackExpansion()) {
-      ParamTypesForAggregateDeduction.ParamTypes->push_back(
+    if (AggrDeductionCandidateParamTypes && Base.isPackExpansion()) {
+      AggrDeductionCandidateParamTypes->push_back(
           SemaRef.Context.getPackExpansionType(Base.getType(), std::nullopt));
 
       // Trailing pack expansion
@@ -2477,7 +2464,7 @@ void InitListChecker::CheckStructUnionTypes(
     InitializedEntity::InitializeMember(*Field, &Entity);
 
   if (isa<InitListExpr>(IList->getInit(Index)) ||
-      ParamTypesForAggregateDeduction.ParamTypes)
+      AggrDeductionCandidateParamTypes)
     CheckSubElementType(MemberEntity, IList, Field->getType(), Index,
                         StructuredList, StructuredIndex);
   else
@@ -2629,9 +2616,8 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
                                     Result.get());
       }
       ++Index;
-      if (ParamTypesForAggregateDeduction.ParamTypes)
-        ParamTypesForAggregateDeduction.ParamTypes->push_back(
-            CurrentObjectType);
+      if (AggrDeductionCandidateParamTypes)
+        AggrDeductionCandidateParamTypes->push_back(CurrentObjectType);
       return !Seq;
     }
 
@@ -10982,15 +10968,23 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
       };
       SmallVector<QualType, 8> ElementTypes, ElementTypesWithoutBraceElision;
 
-      InitListChecker CheckInitList(
-          *this, Entity, ListInit, Ty,
-          InitListChecker::CandidateParamTypesForAggregateDeduction(
-              &ElementTypes, &ElementTypesWithoutBraceElision));
+      InitListChecker CheckInitList(*this, Entity, ListInit, Ty,
+                                    /*NoBraceElisionForDeductionGuides=*/false,
+                                    ElementTypes);
       if (CheckInitList.HadError())
         return;
       BuildAggregateDeductionGuide(ElementTypes);
-      BuildAggregateDeductionGuide(ElementTypesWithoutBraceElision,
-                                   /*BracedVersion=*/true);
+      if (CheckInitList.HadBraceElisionOccurredForDeductionGuides()) {
+        // We still want a deduction guide for the brace initializer even though
+        // the brace can be elided.
+        InitListChecker CheckInitList(*this, Entity, ListInit, Ty,
+                                      /*NoBraceElisionForDeductionGuides=*/true,
+                                      ElementTypesWithoutBraceElision);
+        if (CheckInitList.HadError())
+          return;
+        BuildAggregateDeductionGuide(ElementTypesWithoutBraceElision,
+                                     /*BracedVersion=*/true);
+      }
     };
 
     for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) {
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index 1cce455d966df..735ea258ae163 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -342,7 +342,7 @@ template <class T> struct X {
   T t[2];
 };
 
-X x = {{1, 2}}, y = {1, 2};
+X x = {{1, 2}};
 
 // CHECK-LABEL: Dumping GH64625::<deduction guide for X>:
 // CHECK-NEXT: FunctionTemplateDecl {{.+}} <{{.+}}:[[#@LINE - 7]]:1, col:27> col:27 implicit <deduction guide for X>
@@ -361,22 +361,37 @@ X x = {{1, 2}}, y = {1, 2};
 // CHECK-NEXT:    `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
 // CHECK-NEXT:      `-TemplateTypeParm {{.+}} 'T'
 
-// Brace-elision version:
-// CHECK:      |-CXXDeductionGuideDecl {{.+}} <col:27> col:27 implicit <deduction guide for X> 'auto (T, T) -> X<T>' aggregate 
-// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:27> col:27 'T'
-// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:27> col:27 'T'
-// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:27> col:27 implicit used <deduction guide for X> 'auto (int, int) -> GH64625::X<int>' implicit_instantiation aggregate 
-// CHECK-NEXT:   |-TemplateArgument type 'int'
-// CHECK-NEXT:   | `-BuiltinType {{.+}} 'int'
-// CHECK-NEXT:   |-ParmVarDecl {{.+}} <col:27> col:27 'int'
-// CHECK-NEXT:   `-ParmVarDecl {{.+}} <col:27> col:27 'int'
-// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T, T) -> X<T>' dependent trailing_return
-// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'X<T>' dependent
-// CHECK-NEXT: | `-CXXRecord {{.+}} 'X'
-// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
-// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'T'
-// CHECK-NEXT: `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
-// CHECK-NEXT:  `-TemplateTypeParm {{.+}} 'T'
+template <class T, class U> struct TwoArrays {
+  T t[2];
+  U u[3];
+};
+
+TwoArrays ta = {{1, 2}, {3, 4, 5}};
+// CHECK-LABEL: Dumping GH64625::<deduction guide for TwoArrays>:
+// CHECK-NEXT: FunctionTemplateDecl {{.+}} <{{.+}}:[[#@LINE - 7]]:1, col:36> col:36 implicit <deduction guide for TwoArrays> 
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} <col:11, col:17> col:17 referenced class depth 0 index 0 T 
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} <col:20, col:26> col:26 referenced class depth 0 index 1 U 
+// CHECK:      |-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit <deduction guide for TwoArrays> 'auto (T (&&)[2], U (&&)[3]) -> TwoArrays<T, U>' aggregate  
+// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'T (&&)[2]' 
+// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:36> col:36 'U (&&)[3]' 
+// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit used <deduction guide for TwoArrays> 'auto (int (&&)[2], int (&&)[3]) -> GH64625::TwoArrays<int, int>' implicit_instantiation aggregate  
+// CHECK-NEXT:   |-TemplateArgument type 'int' 
+// CHECK-NEXT:   | `-BuiltinType {{.+}} 'int' 
+// CHECK-NEXT:   |-TemplateArgument type 'int' 
+// CHECK-NEXT:   | `-BuiltinType {{.+}} 'int' 
+// CHECK-NEXT:   |-ParmVarDecl {{.+}} <col:36> col:36 'int (&&)[2]' 
+// CHECK-NEXT:   `-ParmVarDecl {{.+}} <col:36> col:36 'int (&&)[3]' 
+// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T (&&)[2], U (&&)[3]) -> TwoArrays<T, U>' dependent trailing_return 
+// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'TwoArrays<T, U>' dependent 
+// CHECK-NEXT: | `-CXXRecord {{.+}} 'TwoArrays' 
+// CHECK-NEXT: |-RValueReferenceType {{.+}} 'T (&&)[2]' dependent 
+// CHECK-NEXT: | `-ConstantArrayType {{.+}} 'T[2]' dependent 2  
+// CHECK-NEXT: |   `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0 
+// CHECK-NEXT: |     `-TemplateTypeParm {{.+}} 'T' 
+// CHECK-NEXT: `-RValueReferenceType {{.+}} 'U (&&)[3]' dependent 
+// CHECK-NEXT:   `-ConstantArrayType {{.+}} 'U[3]' dependent 3  
+// CHECK-NEXT:     `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1 
+// CHECK-NEXT:       `-TemplateTypeParm {{.+}} 'U'
 
 } // namespace GH64625
 

>From 16cf879782d8dd7b6326fb60409d4b00ae9e8ba8 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 12 Jun 2024 14:43:19 +0800
Subject: [PATCH 5/7] Simplify the whole things

---
 clang/lib/Sema/SemaInit.cpp                 | 52 +++++---------------
 clang/test/SemaTemplate/deduction-guide.cpp | 54 +++++++++++++++++++++
 2 files changed, 65 insertions(+), 41 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 8cfc541164494..ee8befbdea1eb 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -310,8 +310,6 @@ class InitListChecker {
   bool VerifyOnly; // No diagnostics.
   bool TreatUnavailableAsInvalid; // Used only in VerifyOnly mode.
   bool InOverloadResolution;
-  bool BraceElisionOccurredForDeductionGuides = false;
-  bool NoBraceElisionForDeductionGuides;
   InitListExpr *FullyStructuredList = nullptr;
   NoInitExpr *DummyExpr = nullptr;
   SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr;
@@ -508,15 +506,13 @@ class InitListChecker {
       Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
       bool VerifyOnly, bool TreatUnavailableAsInvalid,
       bool InOverloadResolution = false,
-      bool NoBraceElisionForDeductionGuides = false,
       SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr);
   InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL,
-                  QualType &T, bool NoBraceElisionForDeductionGuides,
+                  QualType &T,
                   SmallVectorImpl<QualType> &AggrDeductionCandidateParamTypes)
       : InitListChecker(S, Entity, IL, T, /*VerifyOnly=*/true,
                         /*TreatUnavailableAsInvalid=*/false,
                         /*InOverloadResolution=*/false,
-                        NoBraceElisionForDeductionGuides,
                         &AggrDeductionCandidateParamTypes) {}
 
   bool HadError() { return hadError; }
@@ -524,10 +520,6 @@ class InitListChecker {
   // Retrieves the fully-structured initializer list used for
   // semantic analysis and code generation.
   InitListExpr *getFullyStructuredList() const { return FullyStructuredList; }
-
-  bool HadBraceElisionOccurredForDeductionGuides() const {
-    return BraceElisionOccurredForDeductionGuides;
-  }
 };
 
 } // end anonymous namespace
@@ -990,12 +982,10 @@ static bool hasAnyDesignatedInits(const InitListExpr *IL) {
 InitListChecker::InitListChecker(
     Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
     bool VerifyOnly, bool TreatUnavailableAsInvalid, bool InOverloadResolution,
-    bool NoBraceElisionForDeductionGuides,
     SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes)
     : SemaRef(S), VerifyOnly(VerifyOnly),
       TreatUnavailableAsInvalid(TreatUnavailableAsInvalid),
       InOverloadResolution(InOverloadResolution),
-      NoBraceElisionForDeductionGuides(NoBraceElisionForDeductionGuides),
       AggrDeductionCandidateParamTypes(AggrDeductionCandidateParamTypes) {
   if (!VerifyOnly || hasAnyDesignatedInits(IL)) {
     FullyStructuredList =
@@ -1459,14 +1449,15 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
       //   dependent non-array type or an array type with a value-dependent
       //   bound
       assert(AggrDeductionCandidateParamTypes);
-      if (!isa_and_present<ConstantArrayType>(
-              SemaRef.Context.getAsArrayType(ElemType)) ||
-          NoBraceElisionForDeductionGuides) {
+      // Don't consider the brace elision version if the initializer is in brace
+      // form.
+      if (isa<InitListExpr, DesignatedInitExpr>(expr) ||
+          !isa_and_present<ConstantArrayType>(
+              SemaRef.Context.getAsArrayType(ElemType))) {
         ++Index;
         AggrDeductionCandidateParamTypes->push_back(ElemType);
         return;
       }
-      BraceElisionOccurredForDeductionGuides = true;
     } else {
       InitializationSequence Seq(SemaRef, TmpEntity, Kind, expr,
                                  /*TopLevelOfInitList*/ true);
@@ -10930,11 +10921,10 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
       if (!(RD->getDefinition() && RD->isAggregate()))
         return;
       QualType Ty = Context.getRecordType(RD);
-      auto BuildAggregateDeductionGuide = [&](MutableArrayRef<QualType>
-                                                  ElementTypes,
-                                              bool BracedVersion = false) {
-        if (ElementTypes.empty())
-          return;
+      SmallVector<QualType, 8> ElementTypes;
+
+      InitListChecker CheckInitList(*this, Entity, ListInit, Ty, ElementTypes);
+      if (!CheckInitList.HadError()) {
         // C++ [over.match.class.deduct]p1.8:
         //   if e_i is of array type and x_i is a braced-init-list, T_i is an
         //   rvalue reference to the declared type of e_i and
@@ -10943,8 +10933,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
         //   lvalue reference to the const-qualified declared type of e_i and
         // C++ [over.match.class.deduct]p1.10:
         //   otherwise, T_i is the declared type of e_i
-        for (int I = 0, E = BracedVersion ? ElementTypes.size()
-                                          : ListInit->getNumInits();
+        for (int I = 0, E = ListInit->getNumInits();
              I < E && !isa<PackExpansionType>(ElementTypes[I]); ++I)
           if (ElementTypes[I]->isArrayType()) {
             if (isa<InitListExpr, DesignatedInitExpr>(ListInit->getInit(I)))
@@ -10965,25 +10954,6 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
                                 /*AllowAggregateDeductionCandidate=*/true);
           HasAnyDeductionGuide = true;
         }
-      };
-      SmallVector<QualType, 8> ElementTypes, ElementTypesWithoutBraceElision;
-
-      InitListChecker CheckInitList(*this, Entity, ListInit, Ty,
-                                    /*NoBraceElisionForDeductionGuides=*/false,
-                                    ElementTypes);
-      if (CheckInitList.HadError())
-        return;
-      BuildAggregateDeductionGuide(ElementTypes);
-      if (CheckInitList.HadBraceElisionOccurredForDeductionGuides()) {
-        // We still want a deduction guide for the brace initializer even though
-        // the brace can be elided.
-        InitListChecker CheckInitList(*this, Entity, ListInit, Ty,
-                                      /*NoBraceElisionForDeductionGuides=*/true,
-                                      ElementTypesWithoutBraceElision);
-        if (CheckInitList.HadError())
-          return;
-        BuildAggregateDeductionGuide(ElementTypesWithoutBraceElision,
-                                     /*BracedVersion=*/true);
       }
     };
 
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index 735ea258ae163..41708d8c5c9c1 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -393,6 +393,60 @@ TwoArrays ta = {{1, 2}, {3, 4, 5}};
 // CHECK-NEXT:     `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1 
 // CHECK-NEXT:       `-TemplateTypeParm {{.+}} 'U'
 
+TwoArrays tb = {1, 2, {3, 4, 5}};
+// CHECK:   |-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit <deduction guide for TwoArrays> 'auto (T, T, U (&&)[3]) -> TwoArrays<T, U>' aggregate 
+// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'T'
+// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'T'
+// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:36> col:36 'U (&&)[3]'
+// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit used <deduction guide for TwoArrays> 'auto (int, int, int (&&)[3]) -> GH64625::TwoArrays<int, int>' implicit_instantiation aggregate 
+// CHECK-NEXT:   |-TemplateArgument type 'int'
+// CHECK-NEXT:   | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT:   |-TemplateArgument type 'int'
+// CHECK-NEXT:   | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT:   |-ParmVarDecl {{.+}} <col:36> col:36 'int'
+// CHECK-NEXT:   |-ParmVarDecl {{.+}} <col:36> col:36 'int'
+// CHECK-NEXT:   `-ParmVarDecl {{.+}} <col:36> col:36 'int (&&)[3]'
+// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T, T, U (&&)[3]) -> TwoArrays<T, U>' dependent trailing_return
+// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'TwoArrays<T, U>' dependent
+// CHECK-NEXT: | `-CXXRecord {{.+}} 'TwoArrays'
+// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'T'
+// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'T'
+// CHECK-NEXT: `-RValueReferenceType {{.+}} 'U (&&)[3]' dependent
+// CHECK-NEXT:   `-ConstantArrayType {{.+}} 'U[3]' dependent 3 
+// CHECK-NEXT:     `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
+// CHECK-NEXT:       `-TemplateTypeParm {{.+}} 'U'
+
+TwoArrays tc = {{1, 2}, 3, 4, 5};
+// CHECK: |-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit <deduction guide for TwoArrays> 'auto (T (&&)[2], U, U, U) -> TwoArrays<T, U>' aggregate 
+// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'T (&&)[2]'
+// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'U'
+// CHECK-NEXT: | |-ParmVarDecl {{.+}} <col:36> col:36 'U'
+// CHECK-NEXT: | `-ParmVarDecl {{.+}} <col:36> col:36 'U'
+// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} <col:36> col:36 implicit used <deduction guide for TwoArrays> 'auto (int (&&)[2], int, int, int) -> GH64625::TwoArrays<int, int>' implicit_instantiation aggregate 
+// CHECK-NEXT:   |-TemplateArgument type 'int'
+// CHECK-NEXT:   | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT:   |-TemplateArgument type 'int'
+// CHECK-NEXT:   | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT:   |-ParmVarDecl {{.+}} <col:36> col:36 'int (&&)[2]'
+// CHECK-NEXT:   |-ParmVarDecl {{.+}} <col:36> col:36 'int'
+// CHECK-NEXT:   |-ParmVarDecl {{.+}} <col:36> col:36 'int'
+// CHECK-NEXT:   `-ParmVarDecl {{.+}} <col:36> col:36 'int'
+// CHECK-NEXT: FunctionProtoType {{.+}} 'auto (T (&&)[2], U, U, U) -> TwoArrays<T, U>' dependent trailing_return
+// CHECK-NEXT: |-InjectedClassNameType {{.+}} 'TwoArrays<T, U>' dependent
+// CHECK-NEXT: | `-CXXRecord {{.+}} 'TwoArrays'
+// CHECK-NEXT: |-RValueReferenceType {{.+}} 'T (&&)[2]' dependent
+// CHECK-NEXT: | `-ConstantArrayType {{.+}} 'T[2]' dependent 2 
+// CHECK-NEXT: |   `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT: |     `-TemplateTypeParm {{.+}} 'T'
+// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
+// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'U'
+// CHECK-NEXT: |-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
+// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'U'
+// CHECK-NEXT: `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
+// CHECK-NEXT:   `-TemplateTypeParm {{.+}} 'U'
+
 } // namespace GH64625
 
 namespace GH83368 {

>From b3dd755613bd57b768a05480287f39ab6dd42c4f Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 12 Jun 2024 14:55:46 +0800
Subject: [PATCH 6/7] Tidy up the comment

---
 clang/lib/Sema/SemaInit.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index ee8befbdea1eb..1c6f82a3f6129 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -1449,8 +1449,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
       //   dependent non-array type or an array type with a value-dependent
       //   bound
       assert(AggrDeductionCandidateParamTypes);
-      // Don't consider the brace elision version if the initializer is in brace
-      // form.
+      // Don't consider the brace elision if the initializer is a
+      // braced-init-list.
       if (isa<InitListExpr, DesignatedInitExpr>(expr) ||
           !isa_and_present<ConstantArrayType>(
               SemaRef.Context.getAsArrayType(ElemType))) {

>From 7a70867c8370a1b6247b6dad8a0378c4770fc329 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 13 Jun 2024 11:40:36 +0800
Subject: [PATCH 7/7] Clarify the comments

---
 clang/lib/Sema/SemaInit.cpp | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 1c6f82a3f6129..da894fedaf2f1 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -1449,8 +1449,19 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
       //   dependent non-array type or an array type with a value-dependent
       //   bound
       assert(AggrDeductionCandidateParamTypes);
-      // Don't consider the brace elision if the initializer is a
-      // braced-init-list.
+
+      // In the presence of a braced-init-list within the initializer, we should
+      // not fall through to the brace-elision logic, even if the brace elision
+      // is applicable. Given the example,
+      //
+      // template <class T> struct Foo {
+      //   T t[2];
+      // };
+      //
+      // Foo t = {{1, 2}};
+      //
+      // we don't want the (T, T) but rather (T [2]) in terms of the initializer
+      // {{1, 2}}.
       if (isa<InitListExpr, DesignatedInitExpr>(expr) ||
           !isa_and_present<ConstantArrayType>(
               SemaRef.Context.getAsArrayType(ElemType))) {



More information about the cfe-commits mailing list