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 <zyn7...@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 <zyn7...@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 <zyn7...@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 <zyn7...@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 <zyn7...@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 <zyn7...@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 <zyn7...@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))) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits