diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -112,6 +112,8 @@ - Clang now supports `requires cplusplus20` for module maps. - Implemented missing parts of `P2002R1: Consistent comparison operators `_ - Clang now defines `__cpp_consteval` macro. +- Implemented `P1816R0: `_ and `P2082R1: `_, + which allows CTAD for aggregates. C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ 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 @@ -1382,6 +1382,13 @@ } }; +/// Only used by CXXDeductionGuideDecl. +enum class DeductionCandidate : 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): @@ -1620,10 +1627,10 @@ /// Stores the bits used by FunctionDecl. /// If modified NumFunctionDeclBits and the accessor /// methods in FunctionDecl and CXXDeductionGuideDecl - /// (for IsCopyDeductionCandidate) should be updated appropriately. + /// (for DeductionCandidateKind) should be updated appropriately. class FunctionDeclBitfields { friend class FunctionDecl; - /// For IsCopyDeductionCandidate + /// For DeductionCandidateKind friend class CXXDeductionGuideDecl; /// For the bits in DeclContextBitfields. uint64_t : NumDeclContextBits; @@ -1678,10 +1685,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 implicitly generated + /// (used during overload resolution). + uint64_t DeductionCandidateKind : 2; /// Store the ODRHash after first calculation. uint64_t HasODRHash : 1; 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 @@ -1942,13 +1942,13 @@ ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, SourceLocation EndLocation, - CXXConstructorDecl *Ctor) + CXXConstructorDecl *Ctor, DeductionCandidate Kind) : FunctionDecl(CXXDeductionGuide, C, DC, StartLoc, NameInfo, T, TInfo, SC_None, false, false, ConstexprSpecKind::Unspecified), Ctor(Ctor), ExplicitSpec(ES) { if (EndLocation.isValid()) setRangeEnd(EndLocation); - setIsCopyDeductionCandidate(false); + setDeductionCandidateKind(Kind); } CXXConstructorDecl *Ctor; @@ -1963,7 +1963,8 @@ Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, SourceLocation EndLocation, - CXXConstructorDecl *Ctor = nullptr); + CXXConstructorDecl *Ctor = nullptr, + DeductionCandidate Kind = DeductionCandidate::Normal); static CXXDeductionGuideDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -1980,16 +1981,15 @@ /// 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 setIsCopyDeductionCandidate(bool isCDC = true) { - FunctionDeclBits.IsCopyDeductionCandidate = isCDC; + void setDeductionCandidateKind(DeductionCandidate K) { + FunctionDeclBits.DeductionCandidateKind = static_cast(K); } - bool isCopyDeductionCandidate() const { - return FunctionDeclBits.IsCopyDeductionCandidate; + DeductionCandidate getDeductionCandidateKind() const { + return static_cast( + FunctionDeclBits.DeductionCandidateKind); } // 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 @@ -3996,7 +3996,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, @@ -4037,7 +4038,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, @@ -9112,7 +9114,7 @@ FunctionTemplateDecl *FunctionTemplate, TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, - bool PartialOverloading, + bool PartialOverloading, bool AggregateDeductionCandidate, llvm::function_ref)> CheckNonDependent); TemplateDeductionResult @@ -9172,10 +9174,16 @@ /// 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, - const InitializationKind &Kind, MultiExprArg Init); + const InitializationKind &Kind, MultiExprArg Init, + ParenListExpr *PL = nullptr); QualType deduceVarTypeFromInitializer(VarDecl *VDecl, DeclarationName Name, QualType Type, TypeSourceInfo *TSI, @@ -9355,7 +9363,10 @@ /// Memoization means we are _not_ instantiating a template because /// it is already instantiated (but we entered a context where we /// would have had to if it was not already instantiated). - Memoization + Memoization, + + /// We are building deduction guides for a class. + BuildingDeductionGuides, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -9668,6 +9679,13 @@ const RequiresExpr *E, sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange); + + struct BuildingDeductionGuidesTag {}; + /// \brief Note that we are building deduction guides. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + TemplateDecl *Entity, BuildingDeductionGuidesTag, + SourceRange InstantiationRange = SourceRange()); + /// Note that we have finished instantiating this template. void Clear(); 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++20 [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 @@ -3699,7 +3699,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 @@ -3009,7 +3009,8 @@ FunctionDeclBits.HasSkippedBody = false; FunctionDeclBits.WillHaveBody = false; FunctionDeclBits.IsMultiVersion = false; - FunctionDeclBits.IsCopyDeductionCandidate = false; + FunctionDeclBits.DeductionCandidateKind = + static_cast(DeductionCandidate::Normal); FunctionDeclBits.HasODRHash = false; FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false; if (TrailingRequiresClause) diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -2112,21 +2112,21 @@ } } -CXXDeductionGuideDecl * -CXXDeductionGuideDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation StartLoc, ExplicitSpecifier ES, - const DeclarationNameInfo &NameInfo, QualType T, - TypeSourceInfo *TInfo, SourceLocation EndLocation, - CXXConstructorDecl *Ctor) { +CXXDeductionGuideDecl *CXXDeductionGuideDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation StartLoc, + ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T, + TypeSourceInfo *TInfo, SourceLocation EndLocation, CXXConstructorDecl *Ctor, + DeductionCandidate Kind) { return new (C, DC) CXXDeductionGuideDecl(C, DC, StartLoc, ES, NameInfo, T, - TInfo, EndLocation, Ctor); + TInfo, EndLocation, Ctor, Kind); } CXXDeductionGuideDecl *CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) CXXDeductionGuideDecl( C, nullptr, SourceLocation(), ExplicitSpecifier(), DeclarationNameInfo(), - QualType(), nullptr, SourceLocation(), nullptr); + QualType(), nullptr, SourceLocation(), nullptr, + DeductionCandidate::Normal); } RequiresExprBodyDecl *RequiresExprBodyDecl::Create( diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -412,6 +412,8 @@ return "MarkingClassDllexported"; case CodeSynthesisContext::BuildingBuiltinDumpStructCall: return "BuildingBuiltinDumpStructCall"; + case CodeSynthesisContext::BuildingDeductionGuides: + return "BuildingDeductionGuides"; } return ""; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -12700,10 +12700,9 @@ if (Init) DeduceInits = Init; - if (DirectInit) { - if (auto *PL = dyn_cast_or_null(Init)) - DeduceInits = PL->exprs(); - } + auto *PL = dyn_cast_if_present(Init); + if (DirectInit && PL) + DeduceInits = PL->exprs(); if (isa(Deduced)) { assert(VDecl && "non-auto type for init capture deduction?"); @@ -12713,7 +12712,7 @@ // FIXME: Initialization should not be taking a mutable list of inits. SmallVector InitsCopy(DeduceInits.begin(), DeduceInits.end()); return DeduceTemplateSpecializationFromInitializer(TSI, Entity, Kind, - InitsCopy); + InitsCopy, PL); } if (DirectInit) { 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 @@ -25,8 +25,10 @@ #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/SmallString.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -305,6 +307,7 @@ bool InOverloadResolution; InitListExpr *FullyStructuredList = nullptr; NoInitExpr *DummyExpr = nullptr; + SmallVectorImpl *AggrDeductionCandidateParamTypes = nullptr; NoInitExpr *getDummyInit() { if (!DummyExpr) @@ -354,7 +357,7 @@ unsigned &StructuredIndex); void CheckStructUnionTypes(const InitializedEntity &Entity, InitListExpr *IList, QualType DeclType, - CXXRecordDecl::base_class_range Bases, + CXXRecordDecl::base_class_const_range Bases, RecordDecl::field_iterator Field, bool SubobjectIsDesignatorContext, unsigned &Index, InitListExpr *StructuredList, @@ -391,6 +394,7 @@ unsigned ExpectedNumInits); int numArrayElements(QualType DeclType); int numStructUnionElements(QualType DeclType); + static RecordDecl *getRecordDecl(QualType DeclType); ExprResult PerformEmptyInit(SourceLocation Loc, const InitializedEntity &Entity); @@ -493,9 +497,19 @@ SourceLocation Loc); public: + InitListChecker( + Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T, + bool VerifyOnly, bool TreatUnavailableAsInvalid, + bool InOverloadResolution = false, + SmallVectorImpl *AggrDeductionCandidateParamTypes = nullptr); InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL, - QualType &T, bool VerifyOnly, bool TreatUnavailableAsInvalid, - bool InOverloadResolution = false); + QualType &T, + SmallVectorImpl &AggrDeductionCandidateParamTypes) + : InitListChecker(S, Entity, IL, T, /*VerifyOnly=*/true, + /*TreatUnavailableAsInvalid=*/false, + /*InOverloadResolution=*/false, + &AggrDeductionCandidateParamTypes){}; + bool HadError() { return hadError; } // Retrieves the fully-structured initializer list used for @@ -955,18 +969,19 @@ static bool hasAnyDesignatedInits(const InitListExpr *IL) { for (const Stmt *Init : *IL) - if (Init && isa(Init)) + if (isa_and_nonnull(Init)) return true; 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, + SmallVectorImpl *AggrDeductionCandidateParamTypes) : SemaRef(S), VerifyOnly(VerifyOnly), TreatUnavailableAsInvalid(TreatUnavailableAsInvalid), - InOverloadResolution(InOverloadResolution) { + InOverloadResolution(InOverloadResolution), + AggrDeductionCandidateParamTypes(AggrDeductionCandidateParamTypes) { if (!VerifyOnly || hasAnyDesignatedInits(IL)) { FullyStructuredList = createInitListExpr(T, IL->getSourceRange(), IL->getNumInits()); @@ -980,7 +995,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); @@ -1016,6 +1031,14 @@ return InitializableMembers - structDecl->hasFlexibleArrayMember(); } +RecordDecl *InitListChecker::getRecordDecl(QualType DeclType) { + if (const auto *RT = DeclType->getAs()) + return RT->getDecl(); + if (const auto *Inject = DeclType->getAs()) + 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) { @@ -1310,15 +1333,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 (const 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(); + CXXRecordDecl::base_class_const_range(CXXRecordDecl::base_class_const_iterator(), + CXXRecordDecl::base_class_const_iterator()); + 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); @@ -1348,6 +1374,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) @@ -1405,31 +1438,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 @@ -1645,6 +1693,8 @@ } UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr); ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(DeclType); } void InitListChecker::CheckReferenceType(const InitializedEntity &Entity, @@ -1700,6 +1750,8 @@ UpdateStructuredListElement(StructuredList, StructuredIndex, expr); ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(DeclType); } void InitListChecker::CheckVectorType(const InitializedEntity &Entity, @@ -1751,6 +1803,8 @@ } UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr); ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(elementType); return; } @@ -1912,6 +1966,8 @@ StructuredList->resizeInits(SemaRef.Context, StructuredIndex); } ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(DeclType); return; } } @@ -2067,24 +2123,22 @@ void InitListChecker::CheckStructUnionTypes( const InitializedEntity &Entity, InitListExpr *IList, QualType DeclType, - CXXRecordDecl::base_class_range Bases, RecordDecl::field_iterator Field, + CXXRecordDecl::base_class_const_range Bases, RecordDecl::field_iterator Field, bool SubobjectIsDesignatorContext, unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex, bool TopLevelObject) { - RecordDecl *structDecl = DeclType->castAs()->getDecl(); + const 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()); @@ -2128,7 +2182,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 @@ -2136,6 +2191,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 P_j 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); @@ -2158,7 +2241,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); @@ -2242,7 +2324,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. @@ -2283,7 +2365,7 @@ StructuredList, StructuredIndex); InitializedSomething = true; - if (DeclType->isUnionType() && StructuredList) { + if (RD->isUnion() && StructuredList) { // Initialize the first field within the union. StructuredList->setInitializedFieldInUnion(*Field); } @@ -2294,7 +2376,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(); @@ -2309,7 +2391,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()) @@ -2348,7 +2430,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 @@ -2406,7 +2489,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 { @@ -2419,7 +2502,7 @@ } private: - RecordDecl *Record; + const RecordDecl *Record; }; } // end anonymous namespace @@ -2495,6 +2578,8 @@ Result.get()); } ++Index; + if (AggrDeductionCandidateParamTypes) + AggrDeductionCandidateParamTypes->push_back(CurrentObjectType); return !Seq; } @@ -2588,8 +2673,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(); @@ -2603,7 +2688,7 @@ FieldDecl *KnownField = D->getFieldDecl(); if (!KnownField) { const 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; @@ -2637,11 +2722,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) @@ -2666,12 +2751,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)) { @@ -2686,7 +2771,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(); @@ -2741,15 +2826,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; @@ -2874,7 +2958,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. @@ -3182,6 +3266,8 @@ NumElements = VType->getNumElements(); } else if (CurrentObjectType->isRecordType()) { NumElements = numStructUnionElements(CurrentObjectType); + } else if (CurrentObjectType->isDependentType()) { + NumElements = 1; } Result->reserveInits(SemaRef.Context, NumElements); @@ -10459,7 +10545,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( TypeSourceInfo *TSInfo, const InitializedEntity &Entity, - const InitializationKind &Kind, MultiExprArg Inits) { + const InitializationKind &Kind, MultiExprArg Inits, ParenListExpr *PL) { auto *DeducedTST = dyn_cast( TSInfo->getType()->getContainedDeducedType()); assert(DeducedTST && "not a deduced template specialization type"); @@ -10529,13 +10615,136 @@ OverloadCandidateSet::CSK_Normal); OverloadCandidateSet::iterator Best; - bool HasAnyDeductionGuide = false; bool AllowExplicit = !Kind.isCopyInit() || ListInit; - auto tryToResolveOverload = + // 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->getDeductionCandidateKind() == DeductionCandidate::Aggregate) + 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, + /*PO=*/{}, /*AggregateCandidateDeduction=*/true); + } else { + AddOverloadCandidate(GD, FoundDecl, Inits, Candidates, + SuppressUserConversions, + /*PartialOverloading=*/false, AllowExplicit); + } + }; + + bool FoundDeductionGuide = false; + + auto TryToResolveOverload = [&](bool OnlyListConstructors) -> OverloadingResult { Candidates.clear(OverloadCandidateSet::CSK_Normal); - HasAnyDeductionGuide = false; + bool HasAnyDeductionGuide = false; + + auto SynthesizeAggrGuide = [&](InitListExpr *ListInit) { + auto *RD = cast(Template->getTemplatedDecl()); + if (!(RD->getDefinition() && RD->isAggregate())) + return; + 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, 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 + // 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(); + 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)->IgnoreParenImpCasts())) + ElementTypes[I] = Context.getLValueReferenceType(ElementTypes[I]); + } + + 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(DeductionCandidate::Aggregate); + AggregateDeductionCandidates[Hash] = GD; + addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public), + OnlyListConstructors, + /*AllowAggregateDeductionCandidate=*/true); + } + } else { + CXXDeductionGuideDecl *GD = AggregateDeductionCandidates[Hash]; + FunctionTemplateDecl *TD = GD->getDescribedFunctionTemplate(); + assert(TD && "aggregate deduction candidate is function template"); + addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public), + OnlyListConstructors, + /*AllowAggregateDeductionCandidate=*/true); + } + HasAnyDeductionGuide = true; + } + }; for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) { NamedDecl *D = (*I)->getUnderlyingDecl(); @@ -10543,7 +10752,7 @@ continue; auto *TD = dyn_cast(D); - auto *GD = dyn_cast_or_null( + auto *GD = dyn_cast_if_present( TD ? TD->getTemplatedDecl() : dyn_cast(D)); if (!GD) continue; @@ -10551,53 +10760,30 @@ 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; + addDeductionCandidate(TD, GD, I.getPair(), OnlyListConstructors, + /*AllowAggregateDeductionCandidate=*/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) { + if (ListInit && ListInit->getNumInits()) { + SynthesizeAggrGuide(ListInit); + } else if (PL && PL->getNumExprs()) { + InitListExpr TempListInit(getASTContext(), PL->getLParenLoc(), + PL->exprs(), PL->getRParenLoc()); + SynthesizeAggrGuide(&TempListInit); } + } - // 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; + FoundDeductionGuide = FoundDeductionGuide || HasAnyDeductionGuide; - // 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); - } return Candidates.BestViableFunction(*this, Kind.getLocation(), Best); }; @@ -10633,7 +10819,7 @@ } if (TryListConstructors) - Result = tryToResolveOverload(/*OnlyListConstructor*/true); + Result = TryToResolveOverload(/*OnlyListConstructor*/true); // Then unwrap the initializer list and try again considering all // constructors. Inits = MultiExprArg(ListInit->getInits(), ListInit->getNumInits()); @@ -10642,7 +10828,7 @@ // If list-initialization fails, or if we're doing any other kind of // initialization, we (eventually) consider constructors. if (Result == OR_No_Viable_Function) - Result = tryToResolveOverload(/*OnlyListConstructor*/false); + Result = TryToResolveOverload(/*OnlyListConstructor*/false); switch (Result) { case OR_Ambiguous: @@ -10712,7 +10898,7 @@ // Warn if CTAD was used on a type that does not have any user-defined // deduction guides. - if (!HasAnyDeductionGuide) { + if (!FoundDeductionGuide) { Diag(TSInfo->getTypeLoc().getBeginLoc(), diag::warn_ctad_maybe_unsupported) << TemplateName; 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 @@ -6468,7 +6468,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"); @@ -6635,7 +6635,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; @@ -7261,7 +7262,8 @@ ConversionSequenceList Conversions; if (TemplateDeductionResult Result = DeduceTemplateArguments( MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info, - PartialOverloading, [&](ArrayRef ParamTypes) { + PartialOverloading, /*AggregateDeductionCandidate=*/false, + [&](ArrayRef ParamTypes) { return CheckNonDependentConversions( MethodTmpl, ParamTypes, Args, CandidateSet, Conversions, SuppressUserConversions, ActingContext, ObjectType, @@ -7314,7 +7316,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; @@ -7344,7 +7346,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); @@ -7380,7 +7383,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 @@ -10147,7 +10151,7 @@ return Guide2->isImplicit(); // -- F1 is the copy deduction candidate(16.3.1.8) and F2 is not - if (Guide1->isCopyDeductionCandidate()) + if (Guide1->getDeductionCandidateKind() == DeductionCandidate::Copy) return true; } } 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 @@ -2570,12 +2570,47 @@ }; } +FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList( + TemplateDecl *Template, MutableArrayRef ParamTypes, + SourceLocation Loc) { + if (CXXRecordDecl *DefRecord = + cast(Template->getTemplatedDecl())->getDefinition()) { + if (TemplateDecl *DescribedTemplate = + DefRecord->getDescribedClassTemplate()) + Template = DescribedTemplate; + } + + 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, + /*NewSubstitutionIndex=*/-1); + // Create a template instantiation record to track the "instantiation" of + // constructors into deduction guides. + InstantiatingTemplate BuildingDeductionGuides( + *this, Loc, Template, + Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{}); + if (BuildingDeductionGuides.isInvalid()) + return nullptr; + + return cast( + Transform.buildSimpleDeductionGuide(ParamTypes)); +} + void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template, SourceLocation Loc) { if (CXXRecordDecl *DefRecord = cast(Template->getTemplatedDecl())->getDefinition()) { - TemplateDecl *DescribedTemplate = DefRecord->getDescribedClassTemplate(); - Template = DescribedTemplate ? DescribedTemplate : Template; + if (TemplateDecl *DescribedTemplate = DefRecord->getDescribedClassTemplate()) + Template = DescribedTemplate; } DeclContext *DC = Template->getDeclContext(); @@ -2599,9 +2634,9 @@ 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); + InstantiatingTemplate BuildingDeductionGuides( + *this, Loc, Template, + Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{}); if (BuildingDeductionGuides.isInvalid()) return; @@ -2656,7 +2691,7 @@ cast( Transform.buildSimpleDeductionGuide(Transform.DeducedType)) ->getTemplatedDecl()) - ->setIsCopyDeductionCandidate(); + ->setDeductionCandidateKind(DeductionCandidate::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 @@ -212,7 +212,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; @@ -350,20 +351,24 @@ 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(); + for (TemplateArgument::pack_iterator + XA = X.pack_begin(), + XAEnd = X.pack_end(), YA = Y.pack_begin(), YAEnd = Y.pack_end(); XA != XAEnd; ++XA, ++YA) { - 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); + 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); + } else { + NewPack.push_back(*XA); + } } return DeducedTemplateArgument( @@ -694,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); } @@ -939,8 +946,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()) { @@ -980,6 +992,7 @@ TemplateDeductionInfo &Info; unsigned PackElements = 0; bool IsPartiallyExpanded = false; + bool DeducePackIfNotAlreadyDeduced = false; /// The number of expansions, if we have a fully-expanded pack in this scope. std::optional FixedNumExpansions; @@ -4090,7 +4103,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; @@ -4177,9 +4190,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 @@ -4197,7 +4213,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/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -404,6 +404,7 @@ case MarkingClassDllexported: case BuildingBuiltinDumpStructCall: case LambdaExpressionSubstitution: + case BuildingDeductionGuides: return false; // This function should never be called when Kind's value is Memoization. @@ -620,6 +621,13 @@ SemaRef, CodeSynthesisContext::ParameterMappingSubstitution, PointOfInstantiation, InstantiationRange, Template) {} +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Entity, + BuildingDeductionGuidesTag, SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::BuildingDeductionGuides, + PointOfInstantiation, InstantiationRange, Entity) {} + void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) { Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext; @@ -1051,6 +1059,8 @@ diag::note_parameter_mapping_substitution_here) << Active->InstantiationRange; break; + case CodeSynthesisContext::BuildingDeductionGuides: + llvm_unreachable("unexpected deduction guide in instantiation stack"); } } } @@ -1122,6 +1132,7 @@ case CodeSynthesisContext::InitializingStructuredBinding: case CodeSynthesisContext::MarkingClassDllexported: case CodeSynthesisContext::BuildingBuiltinDumpStructCall: + case CodeSynthesisContext::BuildingDeductionGuides: // This happens in a context unrelated to template instantiation, so // there is no SFINAE. return std::nullopt; 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 @@ -2129,9 +2129,8 @@ Function = CXXDeductionGuideDecl::Create( SemaRef.Context, DC, D->getInnerLocStart(), InstantiatedExplicitSpecifier, NameInfo, T, TInfo, - D->getSourceRange().getEnd()); - if (DGuide->isCopyDeductionCandidate()) - cast(Function)->setIsCopyDeductionCandidate(); + D->getSourceRange().getEnd(), /*Ctor=*/nullptr, + 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 @@ -2219,7 +2219,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 @@ -730,7 +730,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,364 @@ +// 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}; + + A a3(3.0, 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 { // expected-note 2 {{candidate}} + T x; + T y; + }; + + template struct C { // expected-note 10 {{candidate}} + S s; + T t; + }; + + template struct D { // expected-note 6 {{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}; + + C c4(1, 2); // expected-error {{no viable}} + C c5(1, 2, 3); // expected-error {{no viable}} + C c6({1u, 2u}, 3); + + D d1 = {1, 2}; // expected-error {{no viable}} + D d2 = {1, 2, 3}; + + D d3(1, 2); // expected-error {{no viable}} + // CTAD succeed but brace elision is not allowed for parenthesized aggregate init. + D d4(1, 2, 3); // expected-error {{no viable}} + + // 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}; + + E e2(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 + + template + struct I { + using type = T; + }; + + template + struct F { + typename I::type i; + T t; + }; + + F f1 = {1, 2}; + + // CHECK-LABEL: Dumping Basic::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T + // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (typename I::type, T) -> F' + // CHECK: | |-ParmVarDecl {{.*}} 'typename I::type' + // CHECK: | `-ParmVarDecl {{.*}} 'T' + // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used 'auto (typename I::type, int) -> Basic::F' + // CHECK: |-TemplateArgument type 'int' + // CHECK: | `-BuiltinType {{.*}} 'int' + // CHECK: |-ParmVarDecl {{.*}} 'typename I::type':'int' + // CHECK: `-ParmVarDecl {{.*}} 'int':'int' + // CHECK: FunctionProtoType {{.*}} 'auto (typename I::type, T) -> F' dependent trailing_return cdecl + // CHECK: |-InjectedClassNameType {{.*}} 'F' dependent + // CHECK: | `-CXXRecord {{.*}} 'F' + // CHECK: |-DependentNameType {{.*}} 'typename I::type' dependent + // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0 + // CHECK: `-TemplateTypeParm {{.*}} 'T' +} + +namespace Array { + typedef __SIZE_TYPE__ size_t; + template struct A { // expected-note 2 {{candidate}} + T array[N]; + }; + + A a1 = {{1, 2, 3}}; + A a2 = {1, 2, 3}; // expected-error {{no viable}} + A a3 = {"meow"}; + A a4 = {("meow")}; + + A a5({1, 2, 3}); + A a6("meow"); + A a7(("meow")); + + // CHECK-LABEL: Dumping Array::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T + // CHECK: |-NonTypeTemplateParmDecl {{.*}} 'size_t':'unsigned long{{( 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{{( long)?}}' NonTypeTemplateParm {{.*}} 'N' 'size_t':'unsigned long{{( long)?}}' + + // CHECK: Dumping Array::: + // CHECK: FunctionTemplateDecl {{.*}} implicit + // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T + // CHECK: |-NonTypeTemplateParmDecl {{.*}} 'size_t':'unsigned long{{( 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{{( long)?}}' NonTypeTemplateParm {{.*}} 'N' 'size_t':'unsigned long{{( long)?}}' +} + +namespace BraceElision { + template struct A { + T array[2]; + }; + + A a1 = {0, 1}; + + // CTAD succeed but brace elision is not allowed for parenthesized aggregate init. + A a2(0, 1); // expected-error {{array initializer must be an initializer list}} + + // 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 a1 = { + []{ return 1; }, + []{ return 2; } + }; + + A a2( + []{ 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 4 {{candidate}} + int a; + }; + + A a1 = { // expected-error {{no viable}} + []{ return 1; }, + []{ return 2; } + }; + + A a2( // expected-error {{no viable}} + []{ return 1; }, + []{ return 2; } + ); +} + +namespace DeduceArity { + template struct Types {}; + template struct F : Types, T... {}; // expected-note 12 {{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}} + + F f5(Types{}, {}, {}); + F f6(Types{}, X{}, Y{}); + F f7(Types{}, X{}, W{}); // expected-error {{no viable}} + F f8(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' +} diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -7372,7 +7372,7 @@ cxxDeductionGuideDecl(hasParameter(0, hasType(asString("A"))))); auto *ToD = Import(FromD, Lang_CXX17); ASSERT_TRUE(ToD); - EXPECT_TRUE(ToD->isCopyDeductionCandidate()); + EXPECT_EQ(ToD->getDeductionCandidateKind(), DeductionCandidate::Copy); // Check that the deduced class template is also imported. EXPECT_TRUE(findFromTU(FromD)->Importer->GetAlreadyImportedOrNull( FromD->getDeducedTemplate()));