diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1369,6 +1369,13 @@ } }; +/// Only used by CXXDeductionGuideDecl. +enum class DeductionCandidateKind : unsigned char { + Normal, + Copy, + Aggregate, +}; + /// DeclContext - This is used only as base class of specific decl types that /// can act as declaration contexts. These decls are (only the top classes /// that directly derive from DeclContext are mentioned, not their subclasses): @@ -1657,10 +1664,10 @@ /// function using attribute 'target'. uint64_t IsMultiVersion : 1; - /// [C++17] Only used by CXXDeductionGuideDecl. Indicates that - /// the Deduction Guide is the implicitly generated 'copy - /// deduction candidate' (is used during overload resolution). - uint64_t IsCopyDeductionCandidate : 1; + /// Only used by CXXDeductionGuideDecl. Indicates the kind + /// of the Deduction Guide that is the implicitly generated + /// (used during overload resolution). + DeductionCandidateKind DeductionCandidateKind : 2; /// Store the ODRHash after first calculation. uint64_t HasODRHash : 1; @@ -1674,7 +1681,7 @@ }; /// Number of non-inherited bits in FunctionDeclBitfields. - enum { NumFunctionDeclBits = 29 }; + enum { NumFunctionDeclBits = 30 }; /// Stores the bits used by CXXConstructorDecl. If modified /// NumCXXConstructorDeclBits and the accessor @@ -1691,7 +1698,7 @@ /// exactly 64 bits and thus the width of NumCtorInitializers /// will need to be shrunk if some bit is added to NumDeclContextBitfields, /// NumFunctionDeclBitfields or CXXConstructorDeclBitfields. - uint64_t NumCtorInitializers : 19; + uint64_t NumCtorInitializers : 18; uint64_t IsInheritingConstructor : 1; /// Whether this constructor has a trail-allocated explicit specifier. diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1908,7 +1908,7 @@ Ctor(Ctor), ExplicitSpec(ES) { if (EndLocation.isValid()) setRangeEnd(EndLocation); - setIsCopyDeductionCandidate(false); + setDeductionCandidateKind(DeductionCandidateKind::Normal); } CXXConstructorDecl *Ctor; @@ -1940,16 +1940,22 @@ /// Get the constructor from which this deduction guide was generated, if /// this is an implicit deduction guide. - CXXConstructorDecl *getCorrespondingConstructor() const { - return Ctor; + CXXConstructorDecl *getCorrespondingConstructor() const { return Ctor; } + + void setDeductionCandidateKind(DeductionCandidateKind K) { + FunctionDeclBits.DeductionCandidateKind = K; } - void setIsCopyDeductionCandidate(bool isCDC = true) { - FunctionDeclBits.IsCopyDeductionCandidate = isCDC; + DeductionCandidateKind getDeductionCandidateKind() const { + return FunctionDeclBits.DeductionCandidateKind; } bool isCopyDeductionCandidate() const { - return FunctionDeclBits.IsCopyDeductionCandidate; + return getDeductionCandidateKind() == DeductionCandidateKind::Copy; + } + + bool isAggregateDeductionCandidate() const { + return getDeductionCandidateKind() == DeductionCandidateKind::Aggregate; } // Implement isa/cast/dyncast/etc. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3935,7 +3935,8 @@ bool AllowExplicitConversion = false, ADLCallKind IsADLCandidate = ADLCallKind::NotADL, ConversionSequenceList EarlyConversions = std::nullopt, - OverloadCandidateParamOrder PO = {}); + OverloadCandidateParamOrder PO = {}, + bool AggregateCandidateDeduction = false); void AddFunctionCandidates(const UnresolvedSetImpl &Functions, ArrayRef Args, OverloadCandidateSet &CandidateSet, @@ -3976,7 +3977,8 @@ OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false, bool PartialOverloading = false, bool AllowExplicit = true, ADLCallKind IsADLCandidate = ADLCallKind::NotADL, - OverloadCandidateParamOrder PO = {}); + OverloadCandidateParamOrder PO = {}, + bool AggregateCandidateDeduction = false); bool CheckNonDependentConversions( FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, ArrayRef Args, OverloadCandidateSet &CandidateSet, @@ -9000,7 +9002,7 @@ FunctionTemplateDecl *FunctionTemplate, TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, - bool PartialOverloading, + bool PartialOverloading, bool AggregateDeductionCandidate, llvm::function_ref)> CheckNonDependent); TemplateDeductionResult @@ -9056,6 +9058,11 @@ /// not already done so. void DeclareImplicitDeductionGuides(TemplateDecl *Template, SourceLocation Loc); + FunctionTemplateDecl *DeclareImplicitDeductionGuideFromInitList( + TemplateDecl *Template, MutableArrayRef ParamTypes, + SourceLocation Loc); + llvm::DenseMap + AggregateDeductionCandidates; QualType DeduceTemplateSpecializationFromInitializer( TypeSourceInfo *TInfo, const InitializedEntity &Entity, diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h --- a/clang/include/clang/Sema/TemplateDeduction.h +++ b/clang/include/clang/Sema/TemplateDeduction.h @@ -234,6 +234,13 @@ /// different argument type from its substituted parameter type. unsigned CallArgIndex = 0; + // C++ [over.match.class.deduct]p5.2: + // During template argument deduction for the aggregate deduction + // candidate, the number of elements in a trailing parameter pack is only + // deduced from the number of remaining function arguments if it is not + // otherwise deduced. + bool AggregateDeductionCandidateHasMismatchedArity = false; + /// Information on packs that we're currently expanding. /// /// FIXME: This should be kept internal to SemaTemplateDeduction. diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3669,7 +3669,7 @@ NameInfo, T, TInfo, ToEndLoc, Ctor)) return ToFunction; cast(ToFunction) - ->setIsCopyDeductionCandidate(Guide->isCopyDeductionCandidate()); + ->setDeductionCandidateKind(Guide->getDeductionCandidateKind()); } else { if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart, diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2985,7 +2985,7 @@ FunctionDeclBits.HasSkippedBody = false; FunctionDeclBits.WillHaveBody = false; FunctionDeclBits.IsMultiVersion = false; - FunctionDeclBits.IsCopyDeductionCandidate = false; + FunctionDeclBits.DeductionCandidateKind = DeductionCandidateKind::Normal; FunctionDeclBits.HasODRHash = false; FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false; if (TrailingRequiresClause) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10,24 +10,33 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" +#include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/Designator.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/SemaInternal.h" #include "llvm/ADT/APInt.h" +#include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include using namespace clang; @@ -300,6 +309,7 @@ bool InOverloadResolution; InitListExpr *FullyStructuredList = nullptr; NoInitExpr *DummyExpr = nullptr; + SmallVector *AggrDeductionCandidateParamTypes = nullptr; NoInitExpr *getDummyInit() { if (!DummyExpr) @@ -386,6 +396,7 @@ unsigned ExpectedNumInits); int numArrayElements(QualType DeclType); int numStructUnionElements(QualType DeclType); + static RecordDecl *getRecordDecl(QualType DeclType); ExprResult PerformEmptyInit(SourceLocation Loc, const InitializedEntity &Entity); @@ -487,7 +498,15 @@ public: InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T, bool VerifyOnly, bool TreatUnavailableAsInvalid, - bool InOverloadResolution = false); + bool InOverloadResolution = false, + SmallVector *AggrDeductionCandidateParamTypes = nullptr); + InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL, + QualType &T, + SmallVector &AggrDeductionCandidateParamTypes) + : InitListChecker(S, Entity, IL, T, true, false, false, + &AggrDeductionCandidateParamTypes) { + }; + bool HadError() { return hadError; } // Retrieves the fully-structured initializer list used for @@ -952,13 +971,14 @@ return false; } -InitListChecker::InitListChecker(Sema &S, const InitializedEntity &Entity, - InitListExpr *IL, QualType &T, bool VerifyOnly, - bool TreatUnavailableAsInvalid, - bool InOverloadResolution) +InitListChecker::InitListChecker( + Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T, + bool VerifyOnly, bool TreatUnavailableAsInvalid, bool InOverloadResolution, + SmallVector *AggrDeductionCandidateParamTypes) : SemaRef(S), VerifyOnly(VerifyOnly), TreatUnavailableAsInvalid(TreatUnavailableAsInvalid), - InOverloadResolution(InOverloadResolution) { + InOverloadResolution(InOverloadResolution), + AggrDeductionCandidateParamTypes(AggrDeductionCandidateParamTypes) { if (!VerifyOnly || hasAnyDesignatedInits(IL)) { FullyStructuredList = createInitListExpr(T, IL->getSourceRange(), IL->getNumInits()); @@ -972,7 +992,7 @@ CheckExplicitInitList(Entity, IL, T, FullyStructuredList, /*TopLevelObject=*/true); - if (!hadError && FullyStructuredList) { + if (!hadError && !AggrDeductionCandidateParamTypes && FullyStructuredList) { bool RequiresSecondPass = false; FillInEmptyInitializations(Entity, FullyStructuredList, RequiresSecondPass, /*OuterILE=*/nullptr, /*OuterIndex=*/0); @@ -1008,6 +1028,14 @@ return InitializableMembers - structDecl->hasFlexibleArrayMember(); } +RecordDecl *InitListChecker::getRecordDecl(QualType DeclType) { + if (DeclType->isRecordType()) + return DeclType->castAs()->getDecl(); + if (auto *Inject = dyn_cast(DeclType)) + return Inject->getDecl(); + return nullptr; +} + /// Determine whether Entity is an entity for which it is idiomatic to elide /// the braces in aggregate initialization. static bool isIdiomaticBraceElisionEntity(const InitializedEntity &Entity) { @@ -1067,6 +1095,8 @@ maxElements = numStructUnionElements(T); else if (T->isVectorType()) maxElements = T->castAs()->getNumElements(); + else if (T->isDependentType()) + maxElements = 1; else llvm_unreachable("CheckImplicitInitList(): Illegal type"); @@ -1301,15 +1331,18 @@ } else if (DeclType->isVectorType()) { CheckVectorType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); - } else if (DeclType->isRecordType()) { - assert(DeclType->isAggregateType() && - "non-aggregate records should be handed in CheckSubElementType"); - RecordDecl *RD = DeclType->castAs()->getDecl(); + } else if (RecordDecl *RD = getRecordDecl(DeclType)) { auto Bases = CXXRecordDecl::base_class_range(CXXRecordDecl::base_class_iterator(), CXXRecordDecl::base_class_iterator()); - if (auto *CXXRD = dyn_cast(RD)) - Bases = CXXRD->bases(); + if (DeclType->isRecordType()) { + assert(DeclType->isAggregateType() && + "non-aggregate records should be handed in CheckSubElementType"); + if (auto *CXXRD = dyn_cast(RD)) + Bases = CXXRD->bases(); + } else { + Bases = cast(RD)->bases(); + } CheckStructUnionTypes(Entity, IList, DeclType, Bases, RD->field_begin(), SubobjectIsDesignatorContext, Index, StructuredList, StructuredIndex, TopLevelObject); @@ -1339,6 +1372,13 @@ // Checks for scalar type are sufficient for these types too. CheckScalarType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); + } else if (DeclType->isDependentType()) { + // C++ [over.match.class.deduct]p1.5: + // 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); } else { if (!VerifyOnly) SemaRef.Diag(IList->getBeginLoc(), diag::err_illegal_initializer_type) @@ -1396,31 +1436,46 @@ ? InitializedEntity::InitializeTemporary(ElemType) : Entity; - InitializationSequence Seq(SemaRef, TmpEntity, Kind, expr, - /*TopLevelOfInitList*/ true); + if (TmpEntity.getType()->isDependentType()) { + // C++ [over.match.class.deduct]p1.5: + // 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( + SemaRef.Context.getAsArrayType(ElemType))) { + ++Index; + AggrDeductionCandidateParamTypes->push_back(ElemType); + return; + } + } else { + InitializationSequence Seq(SemaRef, TmpEntity, Kind, expr, + /*TopLevelOfInitList*/ true); + // C++14 [dcl.init.aggr]p13: + // If the assignment-expression can initialize a member, the member is + // initialized. Otherwise [...] brace elision is assumed + // + // Brace elision is never performed if the element is not an + // assignment-expression. + if (Seq || isa(expr)) { + if (!VerifyOnly) { + ExprResult Result = Seq.Perform(SemaRef, TmpEntity, Kind, expr); + if (Result.isInvalid()) + hadError = true; - // C++14 [dcl.init.aggr]p13: - // If the assignment-expression can initialize a member, the member is - // initialized. Otherwise [...] brace elision is assumed - // - // Brace elision is never performed if the element is not an - // assignment-expression. - if (Seq || isa(expr)) { - if (!VerifyOnly) { - ExprResult Result = Seq.Perform(SemaRef, TmpEntity, Kind, expr); - if (Result.isInvalid()) + UpdateStructuredListElement(StructuredList, StructuredIndex, + Result.getAs()); + } else if (!Seq) { hadError = true; - - UpdateStructuredListElement(StructuredList, StructuredIndex, - Result.getAs()); - } else if (!Seq) { - hadError = true; - } else if (StructuredList) { - UpdateStructuredListElement(StructuredList, StructuredIndex, - getDummyInit()); + } else if (StructuredList) { + UpdateStructuredListElement(StructuredList, StructuredIndex, + getDummyInit()); + } + ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(ElemType); + return; } - ++Index; - return; } // Fall through for subaggregate initialization @@ -1633,6 +1688,8 @@ } UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr); ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(DeclType); } void InitListChecker::CheckReferenceType(const InitializedEntity &Entity, @@ -1688,6 +1745,8 @@ UpdateStructuredListElement(StructuredList, StructuredIndex, expr); ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(DeclType); } void InitListChecker::CheckVectorType(const InitializedEntity &Entity, @@ -1739,6 +1798,8 @@ } UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr); ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(elementType); return; } @@ -1900,6 +1961,8 @@ StructuredList->resizeInits(SemaRef.Context, StructuredIndex); } ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(DeclType); return; } } @@ -2046,20 +2109,18 @@ bool SubobjectIsDesignatorContext, unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex, bool TopLevelObject) { - RecordDecl *structDecl = DeclType->castAs()->getDecl(); + RecordDecl *RD = getRecordDecl(DeclType); // If the record is invalid, some of it's members are invalid. To avoid // confusion, we forgo checking the initializer for the entire record. - if (structDecl->isInvalidDecl()) { + if (RD->isInvalidDecl()) { // Assume it was supposed to consume a single initializer. ++Index; hadError = true; return; } - if (DeclType->isUnionType() && IList->getNumInits() == 0) { - RecordDecl *RD = DeclType->castAs()->getDecl(); - + if (RD->isUnion() && IList->getNumInits() == 0) { if (!VerifyOnly) for (FieldDecl *FD : RD->fields()) { QualType ET = SemaRef.Context.getBaseElementType(FD->getType()); @@ -2103,7 +2164,8 @@ bool InitializedSomething = false; // If we have any base classes, they are initialized prior to the fields. - for (auto &Base : Bases) { + for (auto I = Bases.begin(), E = Bases.end(); I != E; ++I) { + auto &Base = *I; Expr *Init = Index < IList->getNumInits() ? IList->getInit(Index) : nullptr; // Designated inits always initialize fields, so if we see one, all @@ -2111,6 +2173,34 @@ if (Init && isa(Init)) Init = nullptr; + // C++ [over.match.class.deduct]p1.6: + // each non-trailing aggregate element that is a pack expansion is assumed + // to correspond to no elements of the initializer list, and (1.7) a + // trailing aggregate element that is a pack expansion is assumed to + // correspond to all remaining elements of the initializer list (if any). + + // C++ [over.match.class.deduct]p1.9: + // ... except that additional parameter packs of the form P_j... are + // inserted into the parameter list in their original aggregate element + // position corresponding to each non-trailing aggregate element of + // type Pj that was skipped because it was a parameter pack, and the + // 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( + SemaRef.Context.getPackExpansionType(Base.getType(), std::nullopt)); + + // Trailing pack expansion + if (I + 1 == E && RD->field_empty()) { + if (Index < IList->getNumInits()) + Index = IList->getNumInits(); + return; + } + + continue; + } + SourceLocation InitLoc = Init ? Init->getBeginLoc() : IList->getEndLoc(); InitializedEntity BaseEntity = InitializedEntity::InitializeBase( SemaRef.Context, &Base, false, &Entity); @@ -2133,7 +2223,6 @@ // anything except look at designated initializers; That's okay, // because an error should get printed out elsewhere. It might be // worthwhile to skip over the rest of the initializer, though. - RecordDecl *RD = DeclType->castAs()->getDecl(); RecordDecl::field_iterator FieldEnd = RD->field_end(); size_t NumRecordDecls = llvm::count_if(RD->decls(), [&](const Decl *D) { return isa(D) || isa(D); @@ -2217,7 +2306,7 @@ } // We've already initialized a member of a union. We're done. - if (InitializedSomething && DeclType->isUnionType()) + if (InitializedSomething && RD->isUnion()) break; // If we've hit the flexible array member at the end, we're done. @@ -2258,7 +2347,7 @@ StructuredList, StructuredIndex); InitializedSomething = true; - if (DeclType->isUnionType() && StructuredList) { + if (RD->isUnion() && StructuredList) { // Initialize the first field within the union. StructuredList->setInitializedFieldInUnion(*Field); } @@ -2269,7 +2358,7 @@ // Emit warnings for missing struct field initializers. if (!VerifyOnly && InitializedSomething && CheckForMissingFields && Field != FieldEnd && !Field->getType()->isIncompleteArrayType() && - !DeclType->isUnionType()) { + !RD->isUnion()) { // It is possible we have one or more unnamed bitfields remaining. // Find first (if any) named field and emit warning. for (RecordDecl::field_iterator it = Field, end = RD->field_end(); @@ -2284,7 +2373,7 @@ // Check that any remaining fields can be value-initialized if we're not // building a structured list. (If we are, we'll check this later.) - if (!StructuredList && Field != FieldEnd && !DeclType->isUnionType() && + if (!StructuredList && Field != FieldEnd && !RD->isUnion() && !Field->getType()->isIncompleteArrayType()) { for (; Field != FieldEnd && !hadError; ++Field) { if (!Field->isUnnamedBitfield() && !Field->hasInClassInitializer()) @@ -2323,7 +2412,8 @@ InitializedEntity MemberEntity = InitializedEntity::InitializeMember(*Field, &Entity); - if (isa(IList->getInit(Index))) + if (isa(IList->getInit(Index)) || + AggrDeductionCandidateParamTypes) CheckSubElementType(MemberEntity, IList, Field->getType(), Index, StructuredList, StructuredIndex); else @@ -2381,7 +2471,7 @@ // the given struct or union. class FieldInitializerValidatorCCC final : public CorrectionCandidateCallback { public: - explicit FieldInitializerValidatorCCC(RecordDecl *RD) + explicit FieldInitializerValidatorCCC(const RecordDecl *RD) : Record(RD) {} bool ValidateCandidate(const TypoCorrection &candidate) override { @@ -2394,7 +2484,7 @@ } private: - RecordDecl *Record; + const RecordDecl *Record; }; } // end anonymous namespace @@ -2470,6 +2560,8 @@ Result.get()); } ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(CurrentObjectType); return !Seq; } @@ -2562,8 +2654,8 @@ // then the current object (defined below) shall have // structure or union type and the identifier shall be the // name of a member of that type. - const RecordType *RT = CurrentObjectType->getAs(); - if (!RT) { + RecordDecl *RD = getRecordDecl(CurrentObjectType); + if (!RD) { SourceLocation Loc = D->getDotLoc(); if (Loc.isInvalid()) Loc = D->getFieldLoc(); @@ -2577,7 +2669,7 @@ FieldDecl *KnownField = D->getField(); if (!KnownField) { IdentifierInfo *FieldName = D->getFieldName(); - DeclContext::lookup_result Lookup = RT->getDecl()->lookup(FieldName); + DeclContext::lookup_result Lookup = RD->lookup(FieldName); for (NamedDecl *ND : Lookup) { if (auto *FD = dyn_cast(ND)) { KnownField = FD; @@ -2611,11 +2703,11 @@ // Name lookup didn't find anything. // Determine whether this was a typo for another field name. - FieldInitializerValidatorCCC CCC(RT->getDecl()); + FieldInitializerValidatorCCC CCC(RD); if (TypoCorrection Corrected = SemaRef.CorrectTypo( DeclarationNameInfo(FieldName, D->getFieldLoc()), Sema::LookupMemberName, /*Scope=*/nullptr, /*SS=*/nullptr, CCC, - Sema::CTK_ErrorRecovery, RT->getDecl())) { + Sema::CTK_ErrorRecovery, RD)) { SemaRef.diagnoseTypo( Corrected, SemaRef.PDiag(diag::err_field_designator_unknown_suggest) @@ -2633,12 +2725,12 @@ } unsigned NumBases = 0; - if (auto *CXXRD = dyn_cast(RT->getDecl())) + if (auto *CXXRD = dyn_cast(RD)) NumBases = CXXRD->getNumBases(); unsigned FieldIndex = NumBases; - for (auto *FI : RT->getDecl()->fields()) { + for (auto *FI : RD->fields()) { if (FI->isUnnamedBitfield()) continue; if (declaresSameEntity(KnownField, FI)) { @@ -2653,7 +2745,7 @@ // All of the fields of a union are located at the same place in // the initializer list. - if (RT->getDecl()->isUnion()) { + if (RD->isUnion()) { FieldIndex = 0; if (StructuredList) { FieldDecl *CurrentField = StructuredList->getInitializedFieldInUnion(); @@ -2705,15 +2797,14 @@ // cases where a designator takes us backwards too. if (IsFirstDesignator && !VerifyOnly && SemaRef.getLangOpts().CPlusPlus && NextField && - (*NextField == RT->getDecl()->field_end() || + (*NextField == RD->field_end() || (*NextField)->getFieldIndex() > Field->getFieldIndex() + 1)) { // Find the field that we just initialized. FieldDecl *PrevField = nullptr; - for (auto FI = RT->getDecl()->field_begin(); - FI != RT->getDecl()->field_end(); ++FI) { + for (auto FI = RD->field_begin(); FI != RD->field_end(); ++FI) { if (FI->isUnnamedBitfield()) continue; - if (*NextField != RT->getDecl()->field_end() && + if (*NextField != RD->field_end() && declaresSameEntity(*FI, **NextField)) break; PrevField = *FI; @@ -2838,7 +2929,7 @@ return false; // We've already initialized something in the union; we're done. - if (RT->getDecl()->isUnion()) + if (RD->isUnion()) return hadError; // Check the remaining fields within this class/struct/union subobject. @@ -3146,7 +3237,8 @@ NumElements = VType->getNumElements(); } else if (CurrentObjectType->isRecordType()) { NumElements = numStructUnionElements(CurrentObjectType); - } + } else if (CurrentObjectType->isDependentType()) + NumElements = 1; Result->reserveInits(SemaRef.Context, NumElements); @@ -10131,6 +10223,72 @@ bool HasAnyDeductionGuide = false; bool AllowExplicit = !Kind.isCopyInit() || ListInit; + // Return true is the candidate is added successfully, false otherwise. + auto addDeductionCandidate = [&](FunctionTemplateDecl *TD, + CXXDeductionGuideDecl *GD, + DeclAccessPair FoundDecl, + bool OnlyListConstructors, + bool AllowAggregateDeductionCandidate) { + // C++ [over.match.ctor]p1: (non-list copy-initialization from non-class) + // For copy-initialization, the candidate functions are all the + // converting constructors (12.3.1) of that class. + // C++ [over.match.copy]p1: (non-list copy-initialization from class) + // The converting constructors of T are candidate functions. + if (!AllowExplicit) { + // Overload resolution checks whether the deduction guide is declared + // explicit for us. + + // When looking for a converting constructor, deduction guides that + // could never be called with one argument are not interesting to + // check or note. + if (GD->getMinRequiredArguments() > 1 || + (GD->getNumParams() == 0 && !GD->isVariadic())) + return; + } + + // C++ [over.match.list]p1.1: (first phase list initialization) + // Initially, the candidate functions are the initializer-list + // constructors of the class T + if (OnlyListConstructors && !isInitListConstructor(GD)) + return; + + if (!AllowAggregateDeductionCandidate && + GD->isAggregateDeductionCandidate()) + return; + + // C++ [over.match.list]p1.2: (second phase list initialization) + // the candidate functions are all the constructors of the class T + // C++ [over.match.ctor]p1: (all other cases) + // the candidate functions are all the constructors of the class of + // the object being initialized + + // C++ [over.best.ics]p4: + // When [...] the constructor [...] is a candidate by + // - [over.match.copy] (in all cases) + // FIXME: The "second phase of [over.match.list] case can also + // theoretically happen here, but it's not clear whether we can + // ever have a parameter of the right type. + bool SuppressUserConversions = Kind.isCopyInit(); + + if (TD) { + SmallVector TmpInits; + for (Expr *E : Inits) + if (auto *DI = dyn_cast(E)) + TmpInits.push_back(DI->getInit()); + else + TmpInits.push_back(E); + AddTemplateOverloadCandidate(TD, FoundDecl, /*ExplicitArgs*/ nullptr, + TmpInits, Candidates, + SuppressUserConversions, + /*PartialOverloading*/ false, AllowExplicit, + ADLCallKind::NotADL, {}, true); + } else { + AddOverloadCandidate(GD, FoundDecl, Inits, Candidates, + SuppressUserConversions, + /*PartialOverloading*/ false, AllowExplicit); + } + }; + auto tryToResolveOverload = [&](bool OnlyListConstructors) -> OverloadingResult { Candidates.clear(OverloadCandidateSet::CSK_Normal); @@ -10150,53 +10308,70 @@ if (!GD->isImplicit()) HasAnyDeductionGuide = true; - // C++ [over.match.ctor]p1: (non-list copy-initialization from non-class) - // For copy-initialization, the candidate functions are all the - // converting constructors (12.3.1) of that class. - // C++ [over.match.copy]p1: (non-list copy-initialization from class) - // The converting constructors of T are candidate functions. - if (!AllowExplicit) { - // Overload resolution checks whether the deduction guide is declared - // explicit for us. - - // When looking for a converting constructor, deduction guides that - // could never be called with one argument are not interesting to - // check or note. - if (GD->getMinRequiredArguments() > 1 || - (GD->getNumParams() == 0 && !GD->isVariadic())) - continue; - } - - // C++ [over.match.list]p1.1: (first phase list initialization) - // Initially, the candidate functions are the initializer-list - // constructors of the class T - if (OnlyListConstructors && !isInitListConstructor(GD)) - continue; + addDeductionCandidate(TD, GD, I.getPair(), OnlyListConstructors, false); + } + + // C++ [over.match.class.deduct]p1.4: + // if C is defined and its definition satisfies the conditions for an + // aggregate class ([dcl.init.aggr]) with the assumption that any + // dependent base class has no virtual functions and no virtual base + // classes, and the initializer is a non-empty braced-init-list or + // parenthesized expression-list, and there are no deduction-guides for + // C, the set contains an additional function template, called the + // aggregate deduction candidate, defined as follows. + if (!HasAnyDeductionGuide && ListInit && ListInit->getNumInits()) { + // TODO: support parenthesized expression-list + auto *RD = cast(Template->getTemplatedDecl()); + if (RD->getDefinition() && RD->isAggregate()) { + QualType Ty = Context.getRecordType(RD); + SmallVector 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 or + // string-literal, T_i is an rvalue reference to the declared type + // of e_i + for (int i = 0, e = ListInit->getNumInits(); + i < e && !isa(ElementTypes[i]); ++i) + if (ElementTypes[i]->isArrayType()) { + if (isa(ListInit->getInit(i))) + ElementTypes[i] = + Context.getRValueReferenceType(ElementTypes[i]); + else if (isa(ListInit->getInit(i))) + // This deviates from the wording which is incorrect. + // See https://github.com/cplusplus/draft/issues/3898. + ElementTypes[i] = + Context.getLValueReferenceType(ElementTypes[i]); + } - // C++ [over.match.list]p1.2: (second phase list initialization) - // the candidate functions are all the constructors of the class T - // C++ [over.match.ctor]p1: (all other cases) - // the candidate functions are all the constructors of the class of - // the object being initialized - - // C++ [over.best.ics]p4: - // When [...] the constructor [...] is a candidate by - // - [over.match.copy] (in all cases) - // FIXME: The "second phase of [over.match.list] case can also - // theoretically happen here, but it's not clear whether we can - // ever have a parameter of the right type. - bool SuppressUserConversions = Kind.isCopyInit(); - - if (TD) - AddTemplateOverloadCandidate(TD, I.getPair(), /*ExplicitArgs*/ nullptr, - Inits, Candidates, SuppressUserConversions, - /*PartialOverloading*/ false, - AllowExplicit); - else - AddOverloadCandidate(GD, I.getPair(), Inits, Candidates, - SuppressUserConversions, - /*PartialOverloading*/ false, AllowExplicit); + llvm::FoldingSetNodeID ID; + ID.AddPointer(Template); + for (auto &T : ElementTypes) + T.getCanonicalType().Profile(ID); + unsigned Hash = ID.ComputeHash(); + if (AggregateDeductionCandidates.count(Hash) == 0) { + if (FunctionTemplateDecl *TD = + DeclareImplicitDeductionGuideFromInitList( + Template, ElementTypes, + TSInfo->getTypeLoc().getEndLoc())) { + auto *GD = cast(TD->getTemplatedDecl()); + GD->setDeductionCandidateKind(DeductionCandidateKind::Aggregate); + AggregateDeductionCandidates[Hash] = GD; + addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public), + OnlyListConstructors, true); + } + } else { + CXXDeductionGuideDecl *GD = AggregateDeductionCandidates[Hash]; + FunctionTemplateDecl *TD = GD->getDescribedFunctionTemplate(); + assert(TD); + addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public), + OnlyListConstructors, true); + } + } + } } + return Candidates.BestViableFunction(*this, Kind.getLocation(), Best); }; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6426,7 +6426,7 @@ OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit, bool AllowExplicitConversions, ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions, - OverloadCandidateParamOrder PO) { + OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) { const FunctionProtoType *Proto = dyn_cast(Function->getType()->getAs()); assert(Proto && "Functions without a prototype cannot be overloaded"); @@ -6593,7 +6593,8 @@ // parameter list is truncated on the right, so that there are // exactly m parameters. unsigned MinRequiredArgs = Function->getMinRequiredArguments(); - if (Args.size() < MinRequiredArgs && !PartialOverloading) { + if (!AggregateCandidateDeduction && Args.size() < MinRequiredArgs && + !PartialOverloading) { // Not enough arguments. Candidate.Viable = false; Candidate.FailureKind = ovl_fail_too_few_arguments; @@ -7216,7 +7217,7 @@ ConversionSequenceList Conversions; if (TemplateDeductionResult Result = DeduceTemplateArguments( MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info, - PartialOverloading, [&](ArrayRef ParamTypes) { + PartialOverloading, false, [&](ArrayRef ParamTypes) { return CheckNonDependentConversions( MethodTmpl, ParamTypes, Args, CandidateSet, Conversions, SuppressUserConversions, ActingContext, ObjectType, @@ -7269,7 +7270,7 @@ TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate, - OverloadCandidateParamOrder PO) { + OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) { if (!CandidateSet.isNewCandidate(FunctionTemplate, PO)) return; @@ -7299,7 +7300,8 @@ ConversionSequenceList Conversions; if (TemplateDeductionResult Result = DeduceTemplateArguments( FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info, - PartialOverloading, [&](ArrayRef ParamTypes) { + PartialOverloading, AggregateCandidateDeduction, + [&](ArrayRef ParamTypes) { return CheckNonDependentConversions( FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions, SuppressUserConversions, nullptr, QualType(), {}, PO); @@ -7335,7 +7337,8 @@ AddOverloadCandidate( Specialization, FoundDecl, Args, CandidateSet, SuppressUserConversions, PartialOverloading, AllowExplicit, - /*AllowExplicitConversions*/ false, IsADLCandidate, Conversions, PO); + /*AllowExplicitConversions*/ false, IsADLCandidate, Conversions, PO, + Info.AggregateDeductionCandidateHasMismatchedArity); } /// Check that implicit conversion sequences can be formed for each argument diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2564,6 +2564,39 @@ }; } +FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList( + TemplateDecl *Template, MutableArrayRef ParamTypes, + SourceLocation Loc) { + if (CXXRecordDecl *DefRecord = + cast(Template->getTemplatedDecl())->getDefinition()) { + TemplateDecl *DescribedTemplate = DefRecord->getDescribedClassTemplate(); + Template = DescribedTemplate ? DescribedTemplate : Template; + } + + DeclContext *DC = Template->getDeclContext(); + if (DC->isDependentContext()) + return nullptr; + + ConvertConstructorToDeductionGuideTransform Transform( + *this, cast(Template)); + if (!isCompleteType(Loc, Transform.DeducedType)) + return nullptr; + + // In case we were expanding a pack when we attempted to declare deduction + // guides, turn off pack expansion for everything we're about to do. + ArgumentPackSubstitutionIndexRAII SubstIndex(*this, -1); + // Create a template instantiation record to track the "instantiation" of + // constructors into deduction guides. + // FIXME: Add a kind for this to give more meaningful diagnostics. But can + // this substitution process actually fail? + InstantiatingTemplate BuildingDeductionGuides(*this, Loc, Template); + if (BuildingDeductionGuides.isInvalid()) + return nullptr; + + return cast( + Transform.buildSimpleDeductionGuide(ParamTypes)); +} + void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template, SourceLocation Loc) { if (CXXRecordDecl *DefRecord = @@ -2641,7 +2674,7 @@ cast( Transform.buildSimpleDeductionGuide(Transform.DeducedType)) ->getTemplatedDecl()) - ->setIsCopyDeductionCandidate(); + ->setDeductionCandidateKind(DeductionCandidateKind::Copy); } /// Diagnose the presence of a default template argument on a diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -211,7 +211,8 @@ static DeducedTemplateArgument checkDeducedTemplateArguments(ASTContext &Context, const DeducedTemplateArgument &X, - const DeducedTemplateArgument &Y) { + const DeducedTemplateArgument &Y, + bool AggregateCandidateDeduction = false) { // We have no deduction for one or both of the arguments; they're compatible. if (X.isNull()) return Y; @@ -349,20 +350,25 @@ case TemplateArgument::Pack: { if (Y.getKind() != TemplateArgument::Pack || - X.pack_size() != Y.pack_size()) + (!AggregateCandidateDeduction && X.pack_size() != Y.pack_size())) return DeducedTemplateArgument(); llvm::SmallVector NewPack; - for (TemplateArgument::pack_iterator XA = X.pack_begin(), - XAEnd = X.pack_end(), - YA = Y.pack_begin(); - XA != XAEnd; ++XA, ++YA) { + for (TemplateArgument::pack_iterator + XA = X.pack_begin(), + XAEnd = X.pack_end(), YA = Y.pack_begin(), YAEnd = Y.pack_end(); + XA != XAEnd; ++XA) { + if (YA != YAEnd) { TemplateArgument Merged = checkDeducedTemplateArguments( Context, DeducedTemplateArgument(*XA, X.wasDeducedFromArrayBound()), DeducedTemplateArgument(*YA, Y.wasDeducedFromArrayBound())); if (Merged.isNull() && !(XA->isNull() && YA->isNull())) return DeducedTemplateArgument(); NewPack.push_back(Merged); + ++YA; + } else { + NewPack.push_back(*XA); + } } return DeducedTemplateArgument( @@ -693,8 +699,10 @@ /// Prepare to deduce the packs named within Pattern. PackDeductionScope(Sema &S, TemplateParameterList *TemplateParams, SmallVectorImpl &Deduced, - TemplateDeductionInfo &Info, TemplateArgument Pattern) - : S(S), TemplateParams(TemplateParams), Deduced(Deduced), Info(Info) { + TemplateDeductionInfo &Info, TemplateArgument Pattern, + bool DeducePackIfNotAlreadyDeduced = false) + : S(S), TemplateParams(TemplateParams), Deduced(Deduced), Info(Info), + DeducePackIfNotAlreadyDeduced(DeducePackIfNotAlreadyDeduced){ unsigned NumNamedPacks = addPacks(Pattern); finishConstruction(NumNamedPacks); } @@ -941,8 +949,13 @@ // Check the new pack matches any previous value. DeducedTemplateArgument OldPack = *Loc; - DeducedTemplateArgument Result = - checkDeducedTemplateArguments(S.Context, OldPack, NewPack); + DeducedTemplateArgument Result = checkDeducedTemplateArguments( + S.Context, OldPack, NewPack, DeducePackIfNotAlreadyDeduced); + + Info.AggregateDeductionCandidateHasMismatchedArity = + OldPack.getKind() == TemplateArgument::Pack && + NewPack.getKind() == TemplateArgument::Pack && + OldPack.pack_size() != NewPack.pack_size() && !Result.isNull(); // If we deferred a deduction of this pack, check that one now too. if (!Result.isNull() && !Pack.DeferredDeduction.isNull()) { @@ -982,6 +995,7 @@ TemplateDeductionInfo &Info; unsigned PackElements = 0; bool IsPartiallyExpanded = false; + bool DeducePackIfNotAlreadyDeduced; /// The number of expansions, if we have a fully-expanded pack in this scope. Optional FixedNumExpansions; @@ -4065,7 +4079,7 @@ FunctionTemplateDecl *FunctionTemplate, TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, TemplateDeductionInfo &Info, - bool PartialOverloading, + bool PartialOverloading, bool AggregateDeductionCandidate, llvm::function_ref)> CheckNonDependent) { if (FunctionTemplate->isInvalidDecl()) return TDK_Invalid; @@ -4152,9 +4166,12 @@ continue; } + bool IsTrailingPack = ParamIdx + 1 == NumParamTypes; + QualType ParamPattern = ParamExpansion->getPattern(); PackDeductionScope PackScope(*this, TemplateParams, Deduced, Info, - ParamPattern); + ParamPattern, + AggregateDeductionCandidate && IsTrailingPack); // C++0x [temp.deduct.call]p1: // For a function parameter pack that occurs at the end of the @@ -4172,7 +4189,7 @@ // the length of the explicitly-specified pack if it's expanded by the // parameter pack and 0 otherwise, and we treat each deduction as a // non-deduced context. - if (ParamIdx + 1 == NumParamTypes || PackScope.hasFixedArity()) { + if (IsTrailingPack || PackScope.hasFixedArity()) { for (; ArgIdx < Args.size() && PackScope.hasNextElement(); PackScope.nextPackElement(), ++ArgIdx) { ParamTypesForArgChecking.push_back(ParamPattern); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2108,8 +2108,8 @@ SemaRef.Context, DC, D->getInnerLocStart(), InstantiatedExplicitSpecifier, NameInfo, T, TInfo, D->getSourceRange().getEnd()); - if (DGuide->isCopyDeductionCandidate()) - cast(Function)->setIsCopyDeductionCandidate(); + cast(Function)->setDeductionCandidateKind( + DGuide->getDeductionCandidateKind()); Function->setAccess(D->getAccess()); } else { Function = FunctionDecl::Create( diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2153,7 +2153,8 @@ D->setExplicitSpecifier(Record.readExplicitSpec()); D->Ctor = readDeclAs(); VisitFunctionDecl(D); - D->setIsCopyDeductionCandidate(Record.readInt()); + D->setDeductionCandidateKind( + static_cast(Record.readInt())); } void ASTDeclReader::VisitCXXMethodDecl(CXXMethodDecl *D) { diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -686,7 +686,7 @@ addExplicitSpecifier(D->getExplicitSpecifier(), Record); Record.AddDeclRef(D->Ctor); VisitFunctionDecl(D); - Record.push_back(D->isCopyDeductionCandidate()); + Record.push_back(static_cast(D->getDeductionCandidateKind())); Code = serialization::DECL_CXX_DEDUCTION_GUIDE; } diff --git a/clang/test/SemaTemplate/aggregate-deduction-candidate.cpp b/clang/test/SemaTemplate/aggregate-deduction-candidate.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaTemplate/aggregate-deduction-candidate.cpp @@ -0,0 +1,297 @@ +// RUN: %clang_cc1 -std=c++20 -verify -ast-dump -ast-dump-decl-types -ast-dump-filter "deduction guide" %s | FileCheck %s --strict-whitespace + +namespace Basic { + template struct A { + T x; + T y; + }; + + A a1{3.0, 4.0}; + A a2{.x = 3.0, .y = 4.0}; + + // CHECK-LABEL: Dumping Basic::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced class depth 0 index 0 T + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (T, T) -> A' + // CHECK: | |-ParmVarDecl {{.*}} 'T' + // CHECK: | `-ParmVarDecl {{.*}} 'T' + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used 'auto (double, double) -> Basic::A' + // CHECK: |-TemplateArgument type 'double' + // CHECK: | `-BuiltinType {{.*}} 'double' + // CHECK: |-ParmVarDecl {{.*}} 'double':'double' + // CHECK: `-ParmVarDecl {{.*}} 'double':'double' + // CHECK: FunctionProtoType {{.*}} 'auto (T, T) -> A' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'A' dependent + // CHECK: | `-CXXRecord {{.*}} 'A' + // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: | `-TemplateTypeParm {{.*}} 'T' + // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: `-TemplateTypeParm {{.*}} 'T' + + template struct S { + T x; + T y; + }; + + template struct C { // expected-note 5 {{candidate}} + S s; + T t; + }; + + template struct D { // expected-note 3 {{candidate}} + S s; + T t; + }; + + C c1 = {1, 2}; // expected-error {{no viable}} + C c2 = {1, 2, 3}; // expected-error {{no viable}} + C c3 = {{1u, 2u}, 3}; + + D d1 = {1, 2}; // expected-error {{no viable}} + D d2 = {1, 2, 3}; + + // CHECK-LABEL: Dumping Basic::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (S, T) -> C' + // CHECK: | |-ParmVarDecl {{.*}} 'S':'S' + // CHECK: | `-ParmVarDecl {{.*}} 'T' + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used 'auto (S, int) -> Basic::C' + // CHECK: |-TemplateArgument type 'int' + // CHECK: | `-BuiltinType {{.*}} 'int' + // CHECK: |-ParmVarDecl {{.*}} 'S':'Basic::S' + // CHECK: `-ParmVarDecl {{.*}} 'int':'int' + // CHECK: FunctionProtoType {{.*}} 'auto (S, T) -> C' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'C' dependent + // CHECK: | `-CXXRecord {{.*}} 'C' + // CHECK: |-ElaboratedType {{.*}} 'S' sugar dependent + // CHECK: | `-TemplateSpecializationType {{.*}} 'S' dependent S + // CHECK: | `-TemplateArgument type 'T' + // CHECK: | `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: | `-TemplateTypeParm {{.*}} 'T' + // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: `-TemplateTypeParm {{.*}} 'T' + + // CHECK-LABEL: Dumping Basic::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit 'auto (int, int) -> D' + // CHECK: |-ParmVarDecl {{.*}} 'int':'int' + // CHECK: `-ParmVarDecl {{.*}} 'int':'int' + // CHECK: FunctionProtoType {{.*}} 'auto (int, int) -> D' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'D' dependent + // CHECK: | `-CXXRecord {{.*}} 'D' + // CHECK: |-SubstTemplateTypeParmType {{.*}} 'int' sugar typename depth 0 index 0 T + // CHECK: | |-ClassTemplateSpecialization {{.*}} 'S' + // CHECK: | `-BuiltinType {{.*}} 'int' + // CHECK: `-SubstTemplateTypeParmType {{.*}} 'int' sugar typename depth 0 index 0 T + // CHECK: |-ClassTemplateSpecialization {{.*}} 'S' + // CHECK: `-BuiltinType {{.*}} 'int' + + template struct E { + T t; + decltype(t) t2; + }; + + E e1 = {1, 2}; + + // CHECK-LABEL: Dumping Basic::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (T, decltype(t)) -> E' + // CHECK: | |-ParmVarDecl {{.*}} 'T' + // CHECK: | `-ParmVarDecl {{.*}} 'decltype(t)' + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used 'auto (int, decltype(t)) -> Basic::E' + // CHECK: |-TemplateArgument type 'int' + // CHECK: | `-BuiltinType {{.*}} 'int' + // CHECK: |-ParmVarDecl {{.*}} 'int':'int' + // CHECK: `-ParmVarDecl {{.*}} 'decltype(t)':'int' + // CHECK: FunctionProtoType {{.*}} 'auto (T, decltype(t)) -> E' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'E' dependent + // CHECK: | `-CXXRecord {{.*}} 'E' + // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: | `-TemplateTypeParm {{.*}} 'T' + // CHECK: `-DecltypeType {{.*}} 'decltype(t)' dependent + // CHECK: `-DeclRefExpr {{.*}} 'T' lvalue Field {{.*}} 't' 'T' non_odr_use_unevaluated +} + +namespace Array { + typedef __SIZE_TYPE__ size_t; + template struct A { + T array[N]; + }; + + A a1 = {{1, 2, 3}}; + A a2 = {"meow"}; + + // CHECK-LABEL: Dumping Array::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T + // CHECK: |-NonTypeTemplateParmDecl {{.*}} 'size_t':'unsigned long' depth 0 index 1 N + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (T (&&)[N]) -> A' + // CHECK: | `-ParmVarDecl {{.*}} 'T (&&)[N]' + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used 'auto (int (&&)[3]) -> Array::A' + // CHECK: |-TemplateArgument type 'int' + // CHECK: | `-BuiltinType {{.*}} 'int' + // CHECK: |-TemplateArgument integral 3 + // CHECK: `-ParmVarDecl {{.*}} 'int (&&)[3]' + // CHECK: FunctionProtoType {{.*}} 'auto (T (&&)[N]) -> A' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'A' dependent + // CHECK: | `-CXXRecord {{.*}} 'A' + // CHECK: `-RValueReferenceType {{.*}} 'T (&&)[N]' dependent + // CHECK: `-DependentSizedArrayType {{.*}} 'T[N]' dependent + // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: | `-TemplateTypeParm {{.*}} 'T' + // CHECK: `-DeclRefExpr {{.*}} 'size_t':'unsigned long' NonTypeTemplateParm {{.*}} 'N' 'size_t':'unsigned long' + + // CHECK: Dumping Array::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T + // CHECK: |-NonTypeTemplateParmDecl {{.*}} 'size_t':'unsigned long' depth 0 index 1 N + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (T (&)[N]) -> A' + // CHECK: | `-ParmVarDecl {{.*}} 'T (&)[N]' + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used 'auto (const char (&)[5]) -> Array::A' + // CHECK: |-TemplateArgument type 'const char' + // CHECK: | `-QualType {{.*}} 'const char' const + // CHECK: | `-BuiltinType {{.*}} 'char' + // CHECK: |-TemplateArgument integral 5 + // CHECK: `-ParmVarDecl {{.*}} 'const char (&)[5]' + // CHECK: FunctionProtoType {{.*}} 'auto (T (&)[N]) -> A' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'A' dependent + // CHECK: | `-CXXRecord {{.*}} 'A' + // CHECK: `-LValueReferenceType {{.*}} 'T (&)[N]' dependent + // CHECK: `-DependentSizedArrayType {{.*}} 'T[N]' dependent + // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: | `-TemplateTypeParm {{.*}} 'T' + // CHECK: `-DeclRefExpr {{.*}} 'size_t':'unsigned long' NonTypeTemplateParm {{.*}} 'N' 'size_t':'unsigned long' +} + +namespace BraceElision { + template struct A { + T array[2]; + }; + + A a = {0, 1}; + + // CHECK-LABEL: Dumping BraceElision::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (T, T) -> A' + // CHECK: | |-ParmVarDecl {{.*}} 'T' + // CHECK: | `-ParmVarDecl {{.*}} 'T' + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used 'auto (int, int) -> BraceElision::A' + // CHECK: |-TemplateArgument type 'int' + // CHECK: | `-BuiltinType {{.*}} 'int' + // CHECK: |-ParmVarDecl {{.*}} 'int':'int' + // CHECK: `-ParmVarDecl {{.*}} 'int':'int' + // CHECK: FunctionProtoType {{.*}} 'auto (T, T) -> A' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'A' dependent + // CHECK: | `-CXXRecord {{.*}} 'A' + // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: | `-TemplateTypeParm {{.*}} 'T' + // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: `-TemplateTypeParm {{.*}} 'T' +} + +namespace TrailingPack { + template struct A : T... { + }; + + A a = { + []{ return 1; }, + []{ return 2; } + }; + + // CHECK-LABEL: Dumping TrailingPack::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 ... T + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (T...) -> A' + // CHECK: | `-ParmVarDecl {{.*}} 'T...' pack + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used + // CHECK-SAME: 'auto (TrailingPack::(lambda at {{.*}}), TrailingPack::(lambda at {{.*}})) -> + // CHECK-SAME: TrailingPack::A' + // CHECK: |-TemplateArgument pack + // CHECK: | |-TemplateArgument type 'TrailingPack::(lambda at {{.*}})' + // CHECK: | | `-RecordType {{.*}} 'TrailingPack::(lambda at {{.*}})' + // CHECK: | | `-CXXRecord {{.*}} '' + // CHECK: | `-TemplateArgument type 'TrailingPack::(lambda at {{.*}})' + // CHECK: | `-RecordType {{.*}} 'TrailingPack::(lambda at {{.*}})' + // CHECK: | `-CXXRecord {{.*}} '' + // CHECK: |-ParmVarDecl {{.*}} 'TrailingPack::(lambda at {{.*}})':'TrailingPack::(lambda at {{.*}})' + // CHECK: `-ParmVarDecl {{.*}} 'TrailingPack::(lambda at {{.*}})':'TrailingPack::(lambda at {{.*}})' + // CHECK: FunctionProtoType {{.*}} 'auto (T...) -> A' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'A' dependent + // CHECK: | `-CXXRecord {{.*}} 'A' + // CHECK: `-PackExpansionType {{.*}} 'T...' dependent + // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack + // CHECK: `-TemplateTypeParm {{.*}} 'T' +} + +namespace NonTrailingPack { + template struct A : T... { // expected-note 2 {{candidate}} + int a; + }; + + A a = { // expected-error {{no viable}} + []{ return 1; }, + []{ return 2; } + }; +} + +namespace DeduceArity { + template struct Types {}; + template struct F : Types, T... {}; // expected-note 6 {{candidate}} + + struct X {}; + struct Y {}; + struct Z {}; + struct W { operator Y(); }; + + F f1 = {Types{}, {}, {}}; + F f2 = {Types{}, X{}, Y{}}; + F f3 = {Types{}, X{}, W{}}; // expected-error {{no viable}} + F f4 = {Types{}, {}, {}}; // expected-error {{no viable}} + + // CHECK-LABEL: Dumping DeduceArity::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 ... T + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (Types, T...) -> F' + // CHECK: | |-ParmVarDecl {{.*}} 'Types':'Types' + // CHECK: | `-ParmVarDecl {{.*}} 'T...' pack + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit used + // CHECK-SAME: 'auto (Types, DeduceArity::X, DeduceArity::Y, DeduceArity::Z) -> + // CHECK-SAME: DeduceArity::F' + // CHECK: | |-TemplateArgument pack + // CHECK: | | |-TemplateArgument type 'DeduceArity::X' + // CHECK: | | | `-RecordType {{.*}} 'DeduceArity::X' + // CHECK: | | | `-CXXRecord {{.*}} 'X' + // CHECK: | | |-TemplateArgument type 'DeduceArity::Y' + // CHECK: | | | `-RecordType {{.*}} 'DeduceArity::Y' + // CHECK: | | | `-CXXRecord {{.*}} 'Y' + // CHECK: | | `-TemplateArgument type 'DeduceArity::Z' + // CHECK: | | `-RecordType {{.*}} 'DeduceArity::Z' + // CHECK: | | `-CXXRecord {{.*}} 'Z' + // CHECK: | |-ParmVarDecl {{.*}} 'Types':'DeduceArity::Types' + // CHECK: | |-ParmVarDecl {{.*}} 'DeduceArity::X':'DeduceArity::X' + // CHECK: | |-ParmVarDecl {{.*}} 'DeduceArity::Y':'DeduceArity::Y' + // CHECK: | `-ParmVarDecl {{.*}} 'DeduceArity::Z':'DeduceArity::Z' + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit 'auto (Types, DeduceArity::X) -> DeduceArity::F' + // CHECK: |-TemplateArgument pack + // CHECK: | `-TemplateArgument type 'DeduceArity::X' + // CHECK: | `-RecordType {{.*}} 'DeduceArity::X' + // CHECK: | `-CXXRecord {{.*}} 'X' + // CHECK: |-ParmVarDecl {{.*}} 'Types':'DeduceArity::Types' + // CHECK: `-ParmVarDecl {{.*}} 'DeduceArity::X':'DeduceArity::X' + // CHECK: FunctionProtoType {{.*}} 'auto (Types, T...) -> F' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'F' dependent + // CHECK: | `-CXXRecord {{.*}} 'F' + // CHECK: |-ElaboratedType {{.*}} 'Types' sugar dependent + // CHECK: | `-TemplateSpecializationType {{.*}} 'Types' dependent Types + // CHECK: | `-TemplateArgument type 'T...' + // CHECK: | `-PackExpansionType {{.*}} 'T...' dependent + // CHECK: | `-TemplateTypeParmType {{.*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack + // CHECK: | `-TemplateTypeParm {{.*}} 'T' + // CHECK: `-PackExpansionType {{.*}} 'T...' dependent + // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack + // CHECK: `-TemplateTypeParm {{.*}} 'T' +}