https://github.com/antangelo created https://github.com/llvm/llvm-project/pull/98788
There are two known issues with this initial implementation: 1. Deduction guides are not generated for explicit guides of the base defined after the initial usage of the derived class. This is a caused by a similar issue in the alias template deduction guide implementation. 2. It is currently unable to detect bases whose constructors are inherited by a using declaration with a dependent name, such as `using Derived::Base::Base;`. These using declarations in general are unresolved, so it is difficult to look them up and match them to the corresponding base before instantiation. I intend to target clang 20 for merging this patch. Closes #92606 >From 3f0dfdfdbad3921da42be23fb0bbf1f1f00a42fb Mon Sep 17 00:00:00 2001 From: antangelo <cont...@antangelo.com> Date: Sat, 1 Jun 2024 13:15:40 -0400 Subject: [PATCH] [clang] Implement P2582R1: CTAD from inherited constructors --- clang/docs/ReleaseNotes.rst | 2 + clang/include/clang/AST/DeclCXX.h | 36 +- .../clang/Basic/DiagnosticSemaKinds.td | 4 + clang/lib/AST/ASTImporter.cpp | 8 +- clang/lib/AST/DeclCXX.cpp | 10 +- clang/lib/Sema/SemaOverload.cpp | 67 +++ clang/lib/Sema/SemaTemplate.cpp | 389 ++++++++++++++++-- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 +- clang/lib/Serialization/ASTReaderDecl.cpp | 2 + clang/lib/Serialization/ASTWriterDecl.cpp | 2 + .../SemaCXX/cxx23-ctad-inherited-ctors.cpp | 260 ++++++++++++ clang/unittests/AST/ASTImporterTest.cpp | 43 ++ clang/www/cxx_status.html | 2 +- 13 files changed, 786 insertions(+), 42 deletions(-) create mode 100644 clang/test/SemaCXX/cxx23-ctad-inherited-ctors.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5dc0f8b7e0bbb..b6924f61814fb 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -263,6 +263,8 @@ C++23 Feature Support - Implemented `P2797R0: Static and explicit object member functions with the same parameter-type-lists <https://wg21.link/P2797R0>`_. This completes the support for "deducing this". +- Implemented `P2582R1: Wording for class template argument deduction from inherited constructors <https://wg21.link/P2582R1>`_. + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index fb52ac804849d..c8e74f32b2ccb 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1957,10 +1957,14 @@ class CXXDeductionGuideDecl : public FunctionDecl { ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, SourceLocation EndLocation, - CXXConstructorDecl *Ctor, DeductionCandidate Kind) + CXXConstructorDecl *Ctor, DeductionCandidate Kind, + CXXDeductionGuideDecl *GeneratedFrom, + bool IsGeneratedFromInheritedConstructor) : FunctionDecl(CXXDeductionGuide, C, DC, StartLoc, NameInfo, T, TInfo, SC_None, false, false, ConstexprSpecKind::Unspecified), - Ctor(Ctor), ExplicitSpec(ES) { + Ctor(Ctor), ExplicitSpec(ES), + SourceDeductionGuide(GeneratedFrom, + IsGeneratedFromInheritedConstructor) { if (EndLocation.isValid()) setRangeEnd(EndLocation); setDeductionCandidateKind(Kind); @@ -1968,6 +1972,11 @@ class CXXDeductionGuideDecl : public FunctionDecl { CXXConstructorDecl *Ctor; ExplicitSpecifier ExplicitSpec; + // The deduction guide, if any, that this deduction guide was generated from, + // in the case of alias template deduction or CTAD from inherited + // constructors. The bool member indicates whether this deduction guide is + // generated from an inherited constructor. + llvm::PointerIntPair<CXXDeductionGuideDecl *, 1, bool> SourceDeductionGuide; void setExplicitSpecifier(ExplicitSpecifier ES) { ExplicitSpec = ES; } public: @@ -1979,7 +1988,9 @@ class CXXDeductionGuideDecl : public FunctionDecl { ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, SourceLocation EndLocation, CXXConstructorDecl *Ctor = nullptr, - DeductionCandidate Kind = DeductionCandidate::Normal); + DeductionCandidate Kind = DeductionCandidate::Normal, + CXXDeductionGuideDecl *SourceDG = nullptr, + bool IsGeneratedFromInheritedConstructor = false); static CXXDeductionGuideDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); @@ -1999,6 +2010,25 @@ class CXXDeductionGuideDecl : public FunctionDecl { /// this is an implicit deduction guide. CXXConstructorDecl *getCorrespondingConstructor() const { return Ctor; } + /// Get the deduction guide from which this deduction guide was generated, + /// if it was generated as part of alias template deduction or from an + /// inherited constructor. + CXXDeductionGuideDecl *getSourceDeductionGuide() const { + return SourceDeductionGuide.getPointer(); + } + + void setSourceDeductionGuide(CXXDeductionGuideDecl *DG) { + SourceDeductionGuide.setPointer(DG); + } + + bool isGeneratedFromInheritedConstructor() const { + return SourceDeductionGuide.getInt(); + } + + void setGeneratedFromInheritedConstructor(bool G = true) { + SourceDeductionGuide.setInt(G); + } + void setDeductionCandidateKind(DeductionCandidate K) { FunctionDeclBits.DeductionCandidateKind = static_cast<unsigned char>(K); } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0ea3677355169..da144704957ca 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4808,6 +4808,10 @@ def note_ovl_candidate_underqualified : Note< "make %2 equal %1">; def note_ovl_candidate_substitution_failure : Note< "candidate template ignored: substitution failure%0%1">; +def note_ovl_candidate_inherited_constructor_deduction_failure: Note< + "candidate template ignored: could not deduce template arguments for %0 from %1%2">; +def note_ovl_candidate_inherited_constructor_deduction_failure_source: Note< + "generated from %0 constructor">; def note_ovl_candidate_disabled_by_enable_if : Note< "candidate template ignored: disabled by %0%1">; def note_ovl_candidate_disabled_by_requirement : Note< diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 9bb035c07b8ae..e4c6ef256872a 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3937,14 +3937,16 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { importExplicitSpecifier(Err, Guide->getExplicitSpecifier()); CXXConstructorDecl *Ctor = importChecked(Err, Guide->getCorrespondingConstructor()); + CXXDeductionGuideDecl *SourceDG = + importChecked(Err, Guide->getSourceDeductionGuide()); if (Err) return std::move(Err); if (GetImportedOrCreateDecl<CXXDeductionGuideDecl>( ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart, ESpec, - NameInfo, T, TInfo, ToEndLoc, Ctor)) + NameInfo, T, TInfo, ToEndLoc, Ctor, + Guide->getDeductionCandidateKind(), SourceDG, + Guide->isGeneratedFromInheritedConstructor())) return ToFunction; - cast<CXXDeductionGuideDecl>(ToFunction) - ->setDeductionCandidateKind(Guide->getDeductionCandidateKind()); } else { if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart, diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index d5c140fd34389..91877a49dcf5e 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -2157,9 +2157,11 @@ 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, Kind); + DeductionCandidate Kind, CXXDeductionGuideDecl *SourceDG, + bool IsGeneratedFromInheritedConstructor) { + return new (C, DC) CXXDeductionGuideDecl( + C, DC, StartLoc, ES, NameInfo, T, TInfo, EndLocation, Ctor, Kind, + SourceDG, IsGeneratedFromInheritedConstructor); } CXXDeductionGuideDecl * @@ -2167,7 +2169,7 @@ CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { return new (C, ID) CXXDeductionGuideDecl( C, nullptr, SourceLocation(), ExplicitSpecifier(), DeclarationNameInfo(), QualType(), nullptr, SourceLocation(), nullptr, - DeductionCandidate::Normal); + DeductionCandidate::Normal, nullptr, false); } RequiresExprBodyDecl *RequiresExprBodyDecl::Create( diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index d4a48858ec419..df2b00edaf940 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10531,6 +10531,40 @@ bool clang::isBetterOverloadCandidate( auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function); auto *Guide2 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand2.Function); if (Guide1 && Guide2) { + // -- F1 and F2 are generated from class template argument deduction + // for a class D, and F2 is generated from inheriting constructors + // from a base class of D while F1 is not, ... + if (Guide1->isImplicit() && Guide2->isImplicit() && + Guide1->isGeneratedFromInheritedConstructor() != + Guide2->isGeneratedFromInheritedConstructor()) { + const FunctionProtoType *FPT1 = + Guide1->getType()->getAs<FunctionProtoType>(); + const FunctionProtoType *FPT2 = + Guide2->getType()->getAs<FunctionProtoType>(); + assert(FPT1 && FPT2); + + // ... and for each explicit function argument, the parameters of F1 and + // F2 are either both ellipses or have the same type + if (FPT1->isVariadic() == FPT2->isVariadic() && + FPT1->getNumParams() == FPT2->getNumParams()) { + bool ParamsHaveSameType = true; + const auto &A1 = FPT1->getParamTypes(); + const auto &A2 = FPT2->getParamTypes(); + for (unsigned I = 0, N = FPT1->getNumParams(); I != N; ++I) { + llvm::FoldingSetNodeID ID1, ID2; + S.Context.getCanonicalType(A1[I]).Profile(ID1); + S.Context.getCanonicalType(A2[I]).Profile(ID2); + if (ID1 != ID2) { + ParamsHaveSameType = false; + break; + } + } + + if (ParamsHaveSameType) + return Guide2->isGeneratedFromInheritedConstructor(); + } + } + // -- F1 is generated from a deduction-guide and F2 is not if (Guide1->isImplicit() != Guide2->isImplicit()) return Guide2->isImplicit(); @@ -11671,6 +11705,39 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated, return; } + // Errors in deduction guides from inherited constructors + // will present as substitution failures in the mapping + // partial specialization, so we show a generic diagnostic + // in this case. + if (auto *DG = dyn_cast<CXXDeductionGuideDecl>(Templated); + DG && DG->isGeneratedFromInheritedConstructor()) { + CXXDeductionGuideDecl *Source = DG->getSourceDeductionGuide(); + assert(Source && + "Inherited constructor deduction guides must have a source"); + auto DeducedRecordType = + QualType(cast<ClassTemplateDecl>(DG->getDeducedTemplate()) + ->getTemplatedDecl() + ->getTypeForDecl(), + 0); + auto InheritedRecordType = + QualType(cast<ClassTemplateDecl>(Source->getDeducedTemplate()) + ->getTemplatedDecl() + ->getTypeForDecl(), + 0); + S.Diag(Templated->getLocation(), + diag::note_ovl_candidate_inherited_constructor_deduction_failure) + << DeducedRecordType << InheritedRecordType << TemplateArgString; + + CXXConstructorDecl *Ctor = DG->getCorrespondingConstructor(); + if (Ctor && Ctor->getBeginLoc().isValid()) + S.Diag( + Ctor->getBeginLoc(), + diag:: + note_ovl_candidate_inherited_constructor_deduction_failure_source) + << InheritedRecordType; + return; + } + // Format the SFINAE diagnostic into the argument string. // FIXME: Add a general mechanism to include a PartialDiagnostic *'s // formatted message in another diagnostic. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 9296cc66d6918..ec939a2fb718d 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2746,7 +2746,7 @@ struct ConvertConstructorToDeductionGuideTransform { } }; -unsigned getTemplateParameterDepth(NamedDecl *TemplateParam) { +unsigned getTemplateParameterDepth(const NamedDecl *TemplateParam) { if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam)) return TTP->getDepth(); if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam)) @@ -2768,7 +2768,7 @@ unsigned getTemplateParameterIndex(NamedDecl *TemplateParam) { // Find all template parameters that appear in the given DeducedArgs. // Return the indices of the template parameters in the TemplateParams. -SmallVector<unsigned> TemplateParamsReferencedInTemplateArgumentList( +llvm::SmallSet<unsigned, 8> TemplateParamsReferencedInTemplateArgumentList( const TemplateParameterList *TemplateParamsList, ArrayRef<TemplateArgument> DeducedArgs) { struct TemplateParamsReferencedFinder @@ -2806,17 +2806,19 @@ SmallVector<unsigned> TemplateParamsReferencedInTemplateArgumentList( } void Mark(unsigned Depth, unsigned Index) { if (Index < TemplateParamList->size() && - TemplateParamList->getParam(Index)->getTemplateDepth() == Depth) + getTemplateParameterDepth(TemplateParamList->getParam(Index)) == + Depth) { ReferencedTemplateParams.set(Index); + } } }; TemplateParamsReferencedFinder Finder(TemplateParamsList); Finder.TraverseTemplateArguments(DeducedArgs); - SmallVector<unsigned> Results; + llvm::SmallSet<unsigned, 8> Results; for (unsigned Index = 0; Index < TemplateParamsList->size(); ++Index) { if (Finder.ReferencedTemplateParams[Index]) - Results.push_back(Index); + Results.insert(Index); } return Results; } @@ -3011,9 +3013,12 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, Expr *buildIsDeducibleConstraint(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, QualType ReturnType, - SmallVector<NamedDecl *> TemplateParams) { + SmallVector<NamedDecl *> TemplateParams, + TemplateDecl *DeducingTemplate = nullptr) { ASTContext &Context = SemaRef.Context; // Constraint AST nodes must use uninstantiated depth. + assert(!DeducingTemplate || DeducingTemplate->getTemplateDepth() == + AliasTemplate->getTemplateDepth()); if (auto *PrimaryTemplate = AliasTemplate->getInstantiatedFromMemberTemplate(); PrimaryTemplate && TemplateParams.size() > 0) { @@ -3046,19 +3051,21 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef, Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate)); }; + TemplateDecl *TD = DeducingTemplate ? DeducingTemplate : AliasTemplate; + SmallVector<TypeSourceInfo *> IsDeducibleTypeTraitArgs = { Context.getTrivialTypeSourceInfo( Context.getDeducedTemplateSpecializationType( - TemplateName(AliasTemplate), /*DeducedType=*/QualType(), + TemplateName(TD), /*DeducedType=*/QualType(), /*IsDependent=*/true)), // template specialization type whose // arguments will be deduced. Context.getTrivialTypeSourceInfo( ReturnType), // type from which template arguments are deduced. }; - return TypeTraitExpr::Create( - Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(), - TypeTrait::BTT_IsDeducible, IsDeducibleTypeTraitArgs, - AliasTemplate->getLocation(), /*Value*/ false); + return TypeTraitExpr::Create(Context, Context.getLogicalOperationType(), + TD->getLocation(), TypeTrait::BTT_IsDeducible, + IsDeducibleTypeTraitArgs, TD->getLocation(), + /*Value*/ false); } std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>> @@ -3090,12 +3097,59 @@ getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) { return {Template, AliasRhsTemplateArgs}; } +// Build the type for a deduction guide generated from an inherited constructor +// [over.match.class.deduct]p1.10: +// ... the set contains the guides of A with the return type R +// of each guide replaced with `typename CC<R>::type` ... +std::pair<TypeSourceInfo *, QualType> +buildInheritedConstructorDeductionGuideType( + Sema &SemaRef, TypeSourceInfo *DerivedClassMapperType, + TemplateDecl *DeducingTemplate, TypeSourceInfo *SourceGuideTSI) { + auto &Context = SemaRef.Context; + const auto *FPT = SourceGuideTSI->getType()->getAs<FunctionProtoType>(); + assert(FPT && "Source Guide type should be a FunctionProtoType"); + + // This substitution can fail in cases where the source return type + // is not dependent and the derived class is not deducible + Sema::SFINAETrap Trap(SemaRef); + + MultiLevelTemplateArgumentList Args; + Args.addOuterTemplateArguments(DeducingTemplate, + TemplateArgument(FPT->getReturnType()), false); + Args.addOuterRetainedLevels(DeducingTemplate->getTemplateDepth()); + TypeSourceInfo *ReturnTypeTSI = + SemaRef.SubstType(DerivedClassMapperType, Args, + DeducingTemplate->getBeginLoc(), DeclarationName()); + if (!ReturnTypeTSI || Trap.hasErrorOccurred()) + return {nullptr, QualType()}; + const QualType &ReturnType = ReturnTypeTSI->getType(); + + TypeLocBuilder TLB; + TLB.pushFullCopy(ReturnTypeTSI->getTypeLoc()); + + QualType FT = Context.getFunctionType(ReturnType, FPT->getParamTypes(), + FPT->getExtProtoInfo()); + FunctionProtoTypeLoc NewTL = TLB.push<FunctionProtoTypeLoc>(FT); + const auto &TL = SourceGuideTSI->getTypeLoc().getAs<FunctionProtoTypeLoc>(); + NewTL.setLocalRangeBegin(TL.getLocalRangeBegin()); + NewTL.setLParenLoc(TL.getLParenLoc()); + NewTL.setRParenLoc(TL.getRParenLoc()); + NewTL.setExceptionSpecRange(TL.getExceptionSpecRange()); + NewTL.setLocalRangeEnd(TL.getLocalRangeEnd()); + for (unsigned I = 0, E = NewTL.getNumParams(); I != E; ++I) + NewTL.setParam(I, TL.getParam(I)); + + TypeSourceInfo *TSI = TLB.getTypeSourceInfo(Context, FT); + return {TSI, ReturnType}; +} + // Build deduction guides for a type alias template from the given underlying // deduction guide F. -FunctionTemplateDecl * -BuildDeductionGuideForTypeAlias(Sema &SemaRef, - TypeAliasTemplateDecl *AliasTemplate, - FunctionTemplateDecl *F, SourceLocation Loc) { +FunctionTemplateDecl *BuildDeductionGuideForTypeAlias( + Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, + FunctionTemplateDecl *F, SourceLocation Loc, + TemplateDecl *DeducingTemplate = nullptr, + TypeSourceInfo *DerivedClassMapperType = nullptr) { LocalInstantiationScope Scope(SemaRef); Sema::InstantiatingTemplate BuildingDeductionGuides( SemaRef, AliasTemplate->getLocation(), F, @@ -3103,6 +3157,9 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, if (BuildingDeductionGuides.isInvalid()) return nullptr; + if (!DeducingTemplate) + DeducingTemplate = AliasTemplate; + auto &Context = SemaRef.Context; auto [Template, AliasRhsTemplateArgs] = getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate); @@ -3268,8 +3325,17 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, Sema::CodeSynthesisContext::BuildingDeductionGuides)) { auto *GG = cast<CXXDeductionGuideDecl>(FPrime); - Expr *IsDeducible = buildIsDeducibleConstraint( - SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams); + TypeSourceInfo *TSI = GG->getTypeSourceInfo(); + QualType ReturnType = FPrime->getReturnType(); + if (DerivedClassMapperType) + std::tie(TSI, ReturnType) = buildInheritedConstructorDeductionGuideType( + SemaRef, DerivedClassMapperType, DeducingTemplate, TSI); + if (!TSI) + return nullptr; + + Expr *IsDeducible = + buildIsDeducibleConstraint(SemaRef, AliasTemplate, ReturnType, + FPrimeTemplateParams, DeducingTemplate); Expr *RequiresClause = buildAssociatedConstraints(SemaRef, F, AliasTemplate, DeduceResults, FirstUndeducedParamIdx, IsDeducible); @@ -3281,27 +3347,37 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, AliasTemplate->getTemplateParameters()->getRAngleLoc(), /*RequiresClause=*/RequiresClause); auto *Result = cast<FunctionTemplateDecl>(buildDeductionGuide( - SemaRef, AliasTemplate, FPrimeTemplateParamList, - GG->getCorrespondingConstructor(), GG->getExplicitSpecifier(), - GG->getTypeSourceInfo(), AliasTemplate->getBeginLoc(), - AliasTemplate->getLocation(), AliasTemplate->getEndLoc(), - F->isImplicit())); - cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl()) - ->setDeductionCandidateKind(GG->getDeductionCandidateKind()); + SemaRef, DeducingTemplate, FPrimeTemplateParamList, + GG->getCorrespondingConstructor(), GG->getExplicitSpecifier(), TSI, + AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(), + AliasTemplate->getEndLoc(), F->isImplicit())); + auto *DGuide = cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl()); + DGuide->setDeductionCandidateKind(GG->getDeductionCandidateKind()); + DGuide->setSourceDeductionGuide( + cast<CXXDeductionGuideDecl>(F->getTemplatedDecl())); + if (DerivedClassMapperType) + DGuide->setGeneratedFromInheritedConstructor(); return Result; } return nullptr; } void DeclareImplicitDeductionGuidesForTypeAlias( - Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc) { + Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc, + TemplateDecl *DeducingTemplate = nullptr, + TypeSourceInfo *DerivedClassMapperType = nullptr) { if (AliasTemplate->isInvalidDecl()) return; + if (!DeducingTemplate) + DeducingTemplate = AliasTemplate; auto &Context = SemaRef.Context; // FIXME: if there is an explicit deduction guide after the first use of the // type alias usage, we will not cover this explicit deduction guide. fix this // case. - if (hasDeclaredDeductionGuides( + // This check is already performed in the inherited constructor case by + // DeclareImplicitDeductionGuides + if (!DerivedClassMapperType && + hasDeclaredDeductionGuides( Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate), AliasTemplate->getDeclContext())) return; @@ -3334,17 +3410,29 @@ void DeclareImplicitDeductionGuidesForTypeAlias( NewParam->setScopeInfo(0, I); FPTL.setParam(I, NewParam); } - auto *Transformed = cast<FunctionDecl>(buildDeductionGuide( - SemaRef, AliasTemplate, /*TemplateParams=*/nullptr, + + QualType ReturnType = + cast<FunctionProtoType>(FunctionType->getType())->getReturnType(); + if (DerivedClassMapperType) + std::tie(FunctionType, ReturnType) = + buildInheritedConstructorDeductionGuideType( + SemaRef, DerivedClassMapperType, DeducingTemplate, + FunctionType); + if (!FunctionType) + continue; + + auto *Transformed = cast<CXXDeductionGuideDecl>(buildDeductionGuide( + SemaRef, DeducingTemplate, /*TemplateParams=*/nullptr, /*Constructor=*/nullptr, DG->getExplicitSpecifier(), FunctionType, AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(), AliasTemplate->getEndLoc(), DG->isImplicit())); + Transformed->setSourceDeductionGuide(DG); // FIXME: Here the synthesized deduction guide is not a templated // function. Per [dcl.decl]p4, the requires-clause shall be present only // if the declarator declares a templated function, a bug in standard? auto *Constraint = buildIsDeducibleConstraint( - SemaRef, AliasTemplate, Transformed->getReturnType(), {}); + SemaRef, AliasTemplate, ReturnType, {}, DeducingTemplate); if (auto *RC = DG->getTrailingRequiresClause()) { auto Conjunction = SemaRef.BuildBinOp(SemaRef.getCurScope(), SourceLocation{}, @@ -3364,8 +3452,221 @@ void DeclareImplicitDeductionGuidesForTypeAlias( ->getDeductionCandidateKind() == DeductionCandidate::Aggregate) continue; - BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, F, Loc); + BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, F, Loc, + DeducingTemplate, DerivedClassMapperType); + } +} + +void DeclareImplicitDeductionGuidesFromInheritedConstructors( + Sema &SemaRef, TemplateDecl *Template, ClassTemplateDecl *Pattern, + const CXXBaseSpecifier &Base, unsigned BaseIdx) { + auto &Context = SemaRef.Context; + DeclContext *DC = Template->getDeclContext(); + const auto *BaseTST = Base.getType()->getAs<TemplateSpecializationType>(); + if (!BaseTST) + return; + + TemplateDecl *BaseTD = BaseTST->getTemplateName().getAsTemplateDecl(); + if (!BaseTD) + return; + + // Subsitute any parameters with default arguments not present in the base, + // since partial specializations cannot have default parameters + TemplateParameterList *TemplateTPL = Pattern->getTemplateParameters(); + auto BaseDeducedTemplateParams = + TemplateParamsReferencedInTemplateArgumentList( + TemplateTPL, BaseTST->template_arguments()); + SmallVector<NamedDecl *, 8> PartialSpecParams; + SmallVector<TemplateArgument, 8> SubstArgs; + PartialSpecParams.reserve(TemplateTPL->size()); + SubstArgs.reserve(TemplateTPL->size()); + LocalInstantiationScope Scope(SemaRef); + for (unsigned I = 0, N = TemplateTPL->size(); I < N; ++I) { + NamedDecl *Param = TemplateTPL->getParam(I); + if (!BaseDeducedTemplateParams.contains(I)) { + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param); + TTP && TTP->hasDefaultArgument()) { + SubstArgs.push_back(TTP->getDefaultArgument().getArgument()); + continue; + } + + if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param); + NTTP && NTTP->hasDefaultArgument()) { + SubstArgs.push_back(NTTP->getDefaultArgument().getArgument()); + continue; + } + + if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Param); + TTP && TTP->hasDefaultArgument()) { + SubstArgs.push_back(TTP->getDefaultArgument().getArgument()); + continue; + } + + // We have a template parameter that is not present in the base + // and does not have a default argument. We create the deduction + // guide anyway to display a diagnostic. + } + + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(SubstArgs); + Args.addOuterRetainedLevels(Template->getTemplateDepth()); + + NamedDecl *NewParam = transformTemplateParameter( + SemaRef, DC, Param, Args, PartialSpecParams.size(), + Template->getTemplateDepth()); + if (!NewParam) + return; + + PartialSpecParams.push_back(NewParam); + SubstArgs.push_back(Context.getInjectedTemplateArg(NewParam)); } + + Expr *RequiresClause = nullptr; + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(SubstArgs); + Args.addOuterRetainedLevels(Template->getTemplateDepth()); + if (Expr *TemplateRC = TemplateTPL->getRequiresClause()) { + ExprResult E = SemaRef.SubstExpr(TemplateRC, Args); + if (E.isInvalid()) + return; + RequiresClause = E.getAs<Expr>(); + } + auto *PartialSpecTPL = TemplateParameterList::Create( + Context, TemplateTPL->getTemplateLoc(), TemplateTPL->getLAngleLoc(), + PartialSpecParams, TemplateTPL->getRAngleLoc(), RequiresClause); + + // [over.match.class.deduct]p1.10 + // Let A be an alias template whose template parameter list is that of + // [Template] and whose defining-type-id is [Base] ... + auto *TransformedBase = + SemaRef.SubstType(Base.getTypeSourceInfo(), Args, Base.getBaseTypeLoc(), + DeclarationName(), true); + auto *BaseAD = TypeAliasDecl::Create( + Context, DC, SourceLocation(), Base.getBaseTypeLoc(), + TransformedBase->getType().getBaseTypeIdentifier(), TransformedBase); + std::string AliasDeclName = + (Twine("__ctad_alias_") + BaseTD->getName() + "_to_" + + Template->getName() + "_" + Twine(BaseIdx)) + .str(); + IdentifierInfo *AliasIdentifier = &Context.Idents.get(AliasDeclName); + auto *BaseATD = TypeAliasTemplateDecl::Create( + Context, DC, Base.getBaseTypeLoc(), DeclarationName(AliasIdentifier), + PartialSpecTPL, BaseAD); + BaseAD->setDescribedAliasTemplate(BaseATD); + BaseAD->setImplicit(); + BaseATD->setImplicit(); + + DC->addDecl(BaseATD); + + // ... given a class template `template <typename> class CC;` + // whose primary template is not defined ... + auto *TParam = TemplateTypeParmDecl::Create( + Context, DC, SourceLocation(), SourceLocation(), + Template->getTemplateDepth(), 0, nullptr, false, false); + auto *MapperTPL = TemplateParameterList::Create( + Context, SourceLocation(), SourceLocation(), + ArrayRef<NamedDecl *>(TParam), SourceLocation(), nullptr); + + std::string MapperDeclName = + (Twine("__ctad_mapper_") + BaseTD->getName() + "_to_" + + Template->getName() + "_" + Twine(BaseIdx)) + .str(); + IdentifierInfo *MapperII = &Context.Idents.get(MapperDeclName); + CXXRecordDecl *MapperRD = CXXRecordDecl::Create( + Context, CXXRecordDecl::TagKind::Struct, DC, SourceLocation(), + SourceLocation(), nullptr, nullptr); + ClassTemplateDecl *MapperTD = + ClassTemplateDecl::Create(Context, DC, SourceLocation(), + DeclarationName(MapperII), MapperTPL, MapperRD); + MapperRD->setDescribedClassTemplate(MapperTD); + MapperTD->setImplicit(); + MapperRD->setImplicit(); + + DC->addDecl(MapperTD); + + // ... and with a single partial specialization whose template parameter list + // is that of A with the template argument list of A ... + TemplateArgument AliasTA(TransformedBase->getType()); + ArrayRef<TemplateArgument> TAL(AliasTA); + auto MapperTemplateName = + Context.getCanonicalTemplateName(TemplateName(MapperTD)); + QualType CanonType = + Context.getTemplateSpecializationType(MapperTemplateName, TAL); + + auto *MapperSpecialization = ClassTemplatePartialSpecializationDecl::Create( + Context, ClassTemplatePartialSpecializationDecl::TagKind::Struct, DC, + SourceLocation(), SourceLocation(), PartialSpecTPL, MapperTD, TAL, + CanonType, nullptr); + MapperSpecialization->setImplicit(); + MapperSpecialization->startDefinition(); + + TemplateArgumentListInfo TemplateArgs; + TemplateArgs.addArgument({AliasTA, TransformedBase}); + MapperSpecialization->setTemplateArgsAsWritten(TemplateArgs); + + // ... having a member typedef `type` designating a template specialization + // with the template argument list of A + // but with [Template] as the template + auto DerivedTN = Context.getCanonicalTemplateName(TemplateName(Template)); + QualType DerivedTST = + Context.getTemplateSpecializationType(DerivedTN, SubstArgs); + + TypeLocBuilder MapperTypedefTLB; + TemplateSpecializationTypeLoc TSTL = + MapperTypedefTLB.push<TemplateSpecializationTypeLoc>(DerivedTST); + TSTL.setTemplateNameLoc(Template->getLocation()); + TSTL.setLAngleLoc(Template->getTemplateParameters()->getLAngleLoc()); + TSTL.setRAngleLoc(Template->getTemplateParameters()->getRAngleLoc()); + TSTL.setTemplateKeywordLoc(Template->getBeginLoc()); + for (unsigned I = 0, C = SubstArgs.size(); I < C; ++I) + TSTL.setArgLocInfo(I, + TemplateArgumentLocInfo(Context.getTrivialTypeSourceInfo( + SubstArgs[I].getAsType(), + TemplateTPL->getParam(I)->getLocation()))); + + const auto &MapperTypedefII = Context.Idents.get("type"); + TypeSourceInfo *MapperTypedefTSI = + MapperTypedefTLB.getTypeSourceInfo(Context, DerivedTST); + TypedefDecl *DerivedTypedef = TypedefDecl::Create( + Context, MapperSpecialization, Base.getBeginLoc(), Base.getBeginLoc(), + &MapperTypedefII, MapperTypedefTSI); + + DerivedTypedef->setImplicit(); + DerivedTypedef->setAccess(AS_public); + MapperSpecialization->addDecl(DerivedTypedef); + + MapperSpecialization->completeDefinition(); + + MapperTD->AddPartialSpecialization(MapperSpecialization, nullptr); + DC->addDecl(MapperSpecialization); + + // ... the set contains the guides of A with the return type R + // of each guide replaced with `typename CC<R>::type` ... + QualType MapperSpecializationType = Context.getTemplateSpecializationType( + MapperTemplateName, Context.getInjectedTemplateArg(TParam)); + + auto *NNS = NestedNameSpecifier::Create( + Context, nullptr, false, MapperSpecializationType.getTypePtr()); + QualType MapperReturnType = Context.getDependentNameType( + ElaboratedTypeKeyword::Typename, NNS, &MapperTypedefII); + + NestedNameSpecifierLocBuilder NNSLocBuilder; + NNSLocBuilder.MakeTrivial(Context, NNS, SourceRange(Template->getBeginLoc())); + NestedNameSpecifierLoc QualifierLoc = + NNSLocBuilder.getWithLocInContext(Context); + + TypeLocBuilder ReturnTypeTLB; + DependentNameTypeLoc DepTL = + ReturnTypeTLB.push<DependentNameTypeLoc>(MapperReturnType); + DepTL.setQualifierLoc(QualifierLoc); + + TypeSourceInfo *MapperTSI = + ReturnTypeTLB.getTypeSourceInfo(Context, MapperReturnType); + + DeclareImplicitDeductionGuidesForTypeAlias( + SemaRef, BaseATD, Base.getBeginLoc(), Template, MapperTSI); } // Build an aggregate deduction guide for a type alias template. @@ -3535,6 +3836,34 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template, if (!AddedAny) Transform.buildSimpleDeductionGuide(std::nullopt); + // FIXME: Handle explicit deduction guides from inherited constructors + // when the base deduction guides are declared after this has first run + if (getLangOpts().CPlusPlus23 && + Pattern->getTemplatedDecl()->hasDefinition()) { + unsigned BaseIdx = 0; + for (const auto &Base : Pattern->getTemplatedDecl()->bases()) { + ++BaseIdx; + if (!Base.getType()->isDependentType()) + continue; + + // The InheritConstructors field is not set for dependent + // bases because a using decl that inherits constructors + // for that base is unresolved. + // FIXME: This does not work for name specifiers + // like `using Derived::Base::Base;` + CanQualType T = Context.getCanonicalType(Base.getType()); + DeclarationName Name = Context.DeclarationNames.getCXXConstructorName(T); + auto LR = Pattern->getTemplatedDecl()->lookup(Name); + assert((LR.empty() || LR.isSingleResult()) && + "Expected at most one UsingDecl for a given base"); + if (LR.empty() || !isa<UnresolvedUsingValueDecl>(LR.front())) + continue; + + DeclareImplicitDeductionGuidesFromInheritedConstructors( + *this, Template, Pattern, Base, BaseIdx - 1); + } + } + // -- An additional function template derived as above from a hypothetical // constructor C(C), called the copy deduction candidate. cast<CXXDeductionGuideDecl>( diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 01432301633ed..1378b0664601a 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2192,7 +2192,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( SemaRef.Context, DC, D->getInnerLocStart(), InstantiatedExplicitSpecifier, NameInfo, T, TInfo, D->getSourceRange().getEnd(), DGuide->getCorrespondingConstructor(), - DGuide->getDeductionCandidateKind()); + DGuide->getDeductionCandidateKind(), DGuide->getSourceDeductionGuide(), + DGuide->isGeneratedFromInheritedConstructor()); Function->setAccess(D->getAccess()); } else { Function = FunctionDecl::Create( diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index cbaf1b0a98c61..509a04708c059 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2293,6 +2293,8 @@ void ASTDeclReader::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) { VisitFunctionDecl(D); D->setDeductionCandidateKind( static_cast<DeductionCandidate>(Record.readInt())); + D->SourceDeductionGuide.setPointer(readDeclAs<CXXDeductionGuideDecl>()); + D->setGeneratedFromInheritedConstructor(Record.readBool()); } void ASTDeclReader::VisitCXXMethodDecl(CXXMethodDecl *D) { diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index b6583c54c9ba1..5b165096dbb50 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -790,6 +790,8 @@ void ASTDeclWriter::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) { Record.AddDeclRef(D->Ctor); VisitFunctionDecl(D); Record.push_back(static_cast<unsigned char>(D->getDeductionCandidateKind())); + Record.AddDeclRef(D->SourceDeductionGuide.getPointer()); + Record.push_back(D->isGeneratedFromInheritedConstructor()); Code = serialization::DECL_CXX_DEDUCTION_GUIDE; } diff --git a/clang/test/SemaCXX/cxx23-ctad-inherited-ctors.cpp b/clang/test/SemaCXX/cxx23-ctad-inherited-ctors.cpp new file mode 100644 index 0000000000000..cd55af9146734 --- /dev/null +++ b/clang/test/SemaCXX/cxx23-ctad-inherited-ctors.cpp @@ -0,0 +1,260 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++23 -verify %s + +namespace test1 { + template<typename T> struct Base { + template<typename V = T> requires true + Base(T); + }; + + template<typename T> struct InheritsCtors : public Base<T> { + using Base<T>::Base; + }; + + InheritsCtors inheritsCtors(1); + using IC = InheritsCtors<int>; + using IC = decltype(inheritsCtors); + + template<typename T> struct DoesNotInheritCtors : public Base<T> {}; // expected-note {{candidate template ignored: could not match 'DoesNotInheritCtors<T>' against 'int'}} \ + // expected-note 3{{implicit deduction guide declared as}} \ + // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \ + // expected-note {{candidate template ignored: could not match 'Base<T>' against 'int'}} + DoesNotInheritCtors doesNotInheritCtors(100); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}} + + template<typename T> struct InheritsSecond : public Base<T> { + using Base<T>::Base; + }; + + InheritsSecond inheritsSecond('a'); + using IS = InheritsSecond<char>; + using IS = decltype(inheritsSecond); + + template<typename T> struct NonTemplateDGuideBase { + NonTemplateDGuideBase(T); // expected-note {{generated from 'NonTemplateDGuideBase<T>' constructor}} + }; + NonTemplateDGuideBase(int) -> NonTemplateDGuideBase<char>; + NonTemplateDGuideBase(const char *) -> NonTemplateDGuideBase<const char *>; + + template<typename T> + concept NoPointers = !requires (T t) { *t; }; + + template<NoPointers T> + struct NonTemplateDGuideDerived : public NonTemplateDGuideBase<T> { // expected-note {{candidate function not viable: no known conversion from 'const char[1]' to 'int' for 1st argument}} \ + // expected-note {{candidate template ignored: could not match 'NonTemplateDGuideDerived<T>' against 'const char *'}} \ + // expected-note {{candidate template ignored: could not deduce template arguments for 'NonTemplateDGuideDerived<T>' from 'NonTemplateDGuideBase<T>' [with T = const char *]}} \ + // expected-note {{implicit deduction guide declared as 'template <NoPointers<> T> requires __is_deducible(test1::NonTemplateDGuideDerived, typename __ctad_mapper_NonTemplateDGuideBase_to_NonTemplateDGuideDerived_0<NonTemplateDGuideBase<type-parameter-0-0>>::type) NonTemplateDGuideDerived(type-parameter-0-0) -> typename __ctad_mapper_NonTemplateDGuideBase_to_NonTemplateDGuideDerived_0<NonTemplateDGuideBase<type-parameter-0-0>>::type'}} \ + // expected-note {{candidate template ignored: could not match 'NonTemplateDGuideBase<type-parameter-0-0>' against 'const char *'}} \ + // expected-note {{implicit deduction guide declared as 'template <NoPointers<> T> requires __is_deducible(test1::NonTemplateDGuideDerived, typename __ctad_mapper_NonTemplateDGuideBase_to_NonTemplateDGuideDerived_0<NonTemplateDGuideBase<type-parameter-0-0>>::type) NonTemplateDGuideDerived(NonTemplateDGuideBase<type-parameter-0-0>) -> typename __ctad_mapper_NonTemplateDGuideBase_to_NonTemplateDGuideDerived_0<NonTemplateDGuideBase<type-parameter-0-0>>::type'}} \ + // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \ + // expected-note 2{{implicit deduction guide declared as }} + using NonTemplateDGuideBase<T>::NonTemplateDGuideBase; + }; + + NonTemplateDGuideDerived ntdg(1); + using NTDG = NonTemplateDGuideDerived<char>; + using NTDG = decltype(ntdg); + + NonTemplateDGuideDerived ntdg_char(""); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}} + + template<typename T> + struct ExplicitBase { + template<typename V> + ExplicitBase(V); + }; + + template<typename T> + ExplicitBase(T) -> ExplicitBase<T>; + + template<NoPointers T> + struct ExplicitDerived : public ExplicitBase<T> { // expected-note {{candidate template ignored: couldn't infer template argument 'T'}} \ + // expected-note {{implicit deduction guide declared as 'template <NoPointers<> T, typename V> requires __is_deducible(test1::ExplicitDerived, typename __ctad_mapper_ExplicitBase_to_ExplicitDerived_0<ExplicitBase<type-parameter-0-0>>::type) ExplicitDerived(type-parameter-0-1) -> typename __ctad_mapper_ExplicitBase_to_ExplicitDerived_0<ExplicitBase<type-parameter-0-0>>::type'}} \ + // expected-note {{candidate template ignored: could not match 'ExplicitDerived<T>' against 'const char *'}} \ + // expected-note {{candidate template ignored: could not match 'ExplicitBase<type-parameter-0-0>' against 'const char *'}} \ + // expected-note {{implicit deduction guide declared as 'template <NoPointers<> T> requires __is_deducible(test1::ExplicitDerived, typename __ctad_mapper_ExplicitBase_to_ExplicitDerived_0<ExplicitBase<type-parameter-0-0>>::type) ExplicitDerived(ExplicitBase<type-parameter-0-0>) -> typename __ctad_mapper_ExplicitBase_to_ExplicitDerived_0<ExplicitBase<type-parameter-0-0>>::type'}} \ + // expected-note {{candidate template ignored: could not deduce template arguments for 'ExplicitDerived<T>' from 'ExplicitBase<T>' [with T = const char *]}} \ + // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \ + // expected-note 2{{implicit deduction guide declared as }} + + using ExplicitBase<T>::ExplicitBase; + }; + + ExplicitDerived ed(10); + using ED = ExplicitDerived<int>; + using ED = decltype(ed); + + ExplicitDerived substFail(""); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}} + + // FIXME: Support deduction guides that were declared + // after the initial implicit guides are declared for + // the derived template. +#if 0 + Base(int) -> Base<char>; + + InheritsCtors ic2(1); + using IC2 = InheritsCtors<char>; + using IC2 = decltype(ic2); +#endif +} + +namespace test2 { + template<typename T, typename U, typename V> struct Base { + Base(T, U, V); + }; + + template<typename T, typename U> struct Derived : public Base<U, T, int> { + // FIXME: Current implementation does not find Base's constructors correctly + // with the below using decl + //using Derived::Base::Base; + + using Base<U, T, int>::Base; + }; + + Derived derived(true, 'a', 1); + using D = Derived<char, bool>; + using D = decltype(derived); +} + +namespace test3 { + template<typename T> struct Base { + Base(T); // expected-note {{generated from 'Base<T>' constructor}} + }; + + template<typename T, typename U> struct NotEnoughParams : public Base<T> { // expected-note {{candidate template ignored: could not deduce template arguments for 'NotEnoughParams<T, U>' from 'Base<T>' [with T = int]}} \ + // expected-note {{implicit deduction guide declared as 'template <typename T> requires __is_deducible(test3::NotEnoughParams, typename __ctad_mapper_Base_to_NotEnoughParams_0<Base<type-parameter-0-0>>::type) NotEnoughParams(type-parameter-0-0) -> typename __ctad_mapper_Base_to_NotEnoughParams_0<Base<type-parameter-0-0>>::type'}} \ + // expected-note {{candidate template ignored: could not match 'Base<type-parameter-0-0>' against 'int'}} \ + // expected-note {{implicit deduction guide declared as 'template <typename T> requires __is_deducible(test3::NotEnoughParams, typename __ctad_mapper_Base_to_NotEnoughParams_0<Base<type-parameter-0-0>>::type) NotEnoughParams(Base<type-parameter-0-0>) -> typename __ctad_mapper_Base_to_NotEnoughParams_0<Base<type-parameter-0-0>>::type'}} \ + // expected-note {{candidate template ignored: could not match 'NotEnoughParams<T, U>' against 'int'}} \ + // expected-note {{candidate template ignored: could not match 'Base<T>' against 'int'}} \ + // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \ + // expected-note 3{{implicit deduction guide declared as}} + using Base<T>::Base; + }; + + NotEnoughParams notEnoughParams(1); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}} +} + +namespace test4 { + template<typename T> struct B { + B(T t); + }; + + template<typename T = int, typename V = int> + struct DefaultArgsNotInBase : public B<V> { + using B<V>::B; + }; + + DefaultArgsNotInBase d('d'); + using D = DefaultArgsNotInBase<int, char>; + using D = decltype(d); + + template<typename T> struct BaseEmptyCtor { + BaseEmptyCtor(); + }; + + template<typename T = int, typename V = int> + struct DefaultArgsNotInBaseEmpty : public BaseEmptyCtor<V> { + using BaseEmptyCtor<V>::BaseEmptyCtor; + }; + + DefaultArgsNotInBaseEmpty d2; + using D2 = DefaultArgsNotInBaseEmpty<>; + using D2 = decltype(d2); +} + +namespace test5 { + template<typename T> struct Base { + Base(T); + }; + template<typename T> struct Outer { + template<typename U> struct Inner : public Base<U> { + using Base<U>::Base; + }; + }; + + Outer<int>::Inner i(10); + using I = Outer<int>::Inner<int>; + using I = decltype(i); +} + +namespace test6 { + template<typename T> + concept False = false; + + template<typename T> + concept True = true; + + template<typename T> + struct Base { + Base(T); // expected-note {{generated from 'Base<T>' constructor}} + }; + + template<False F> + struct DerivedFalse : public Base<F> { // expected-note {{candidate template ignored: could not deduce template arguments for 'DerivedFalse<F>' from 'Base<T>' [with F = int]}} \ + // expected-note {{implicit deduction guide declared as 'template <False<> F> requires __is_deducible(test6::DerivedFalse, typename __ctad_mapper_Base_to_DerivedFalse_0<Base<type-parameter-0-0>>::type) DerivedFalse(type-parameter-0-0) -> typename __ctad_mapper_Base_to_DerivedFalse_0<Base<type-parameter-0-0>>::type'}} \ + // expected-note {{candidate template ignored: could not match 'Base<type-parameter-0-0>' against 'int'}} \ + // expected-note {{implicit deduction guide declared as 'template <False<> F> requires __is_deducible(test6::DerivedFalse, typename __ctad_mapper_Base_to_DerivedFalse_0<Base<type-parameter-0-0>>::type) DerivedFalse(Base<type-parameter-0-0>) -> typename __ctad_mapper_Base_to_DerivedFalse_0<Base<type-parameter-0-0>>::type'}} \ + // expected-note {{candidate template ignored: could not match 'DerivedFalse<F>' against 'int'}} \ + // expected-note {{candidate template ignored: could not match 'Base<F>' against 'int'}} \ + // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \ + // expected-note 3{{implicit deduction guide declared as}} + using Base<F>::Base; + }; + + template<True F> + struct DerivedTrue : public Base<F> { + using Base<F>::Base; + }; + + DerivedFalse df(10); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}} + + DerivedTrue dt(10); + using DT = DerivedTrue<int>; + using DT = decltype(dt); +} + +namespace test7 { + template<typename T, typename U> + struct Base1 { + Base1(); + Base1(T, U); + }; + + template<typename T, typename U> + struct Base2 { + Base2(); + Base2(T, U); + }; + + template<typename T = int, typename U = int> + struct MultipleInheritance : public Base1<T, U*> , Base2<U*, T> { // expected-note {{candidate function [with T = int, U = int]}} \ + // expected-note {{candidate function [with T = int, U = int]}} + using Base1<T, U*>::Base1; + using Base2<U*, T>::Base2; + }; + + MultipleInheritance mi1(1, ""); + using MI1 = MultipleInheritance<int, const char>; + using MI1 = decltype(mi1); + + MultipleInheritance mi2("", 1); + using MI2 = MultipleInheritance<int, const char>; + using MI2 = decltype(mi2); + + // This is an odd case. + // Since the base DGs have the deducible constraint, they are more specialized than MultipleInheritance's + // ctor, and take priority before the new clause in P2582R1 is applied. + MultipleInheritance mi3; // expected-error {{ambiguous deduction for template arguments of 'MultipleInheritance'}} + + template<typename T> + struct MultipleInheritanceSameBase : public Base1<T, const T*>, Base1<const T*, T> { + using Base1<T, const T*>::Base1; + using Base1<const T*, T>::Base1; + }; + + MultipleInheritanceSameBase misb1('a', ""); + using MISB1 = MultipleInheritanceSameBase<char>; + using MISB1 = decltype(misb1); + + MultipleInheritanceSameBase misb2("", 'a'); + using MISB2 = MultipleInheritanceSameBase<char>; + using MISB2 = decltype(misb2); +} diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 92f9bae6cb064..bb27280303c9b 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -8077,6 +8077,8 @@ TEST_P(ImportFunctions, CTADImplicit) { auto *ToD = Import(FromD, Lang_CXX17); ASSERT_TRUE(ToD); EXPECT_EQ(ToD->getDeductionCandidateKind(), DeductionCandidate::Copy); + EXPECT_EQ(ToD->getSourceDeductionGuide(), nullptr); + EXPECT_FALSE(ToD->isGeneratedFromInheritedConstructor()); // Check that the deduced class template is also imported. EXPECT_TRUE(findFromTU(FromD)->Importer->GetAlreadyImportedOrNull( FromD->getDeducedTemplate())); @@ -8101,6 +8103,8 @@ TEST_P(ImportFunctions, CTADUserDefinedExplicit) { ASSERT_TRUE(ToD); EXPECT_FALSE(FromD->isImplicit()); EXPECT_TRUE(ToD->isExplicit()); + EXPECT_EQ(ToD->getSourceDeductionGuide(), nullptr); + EXPECT_FALSE(ToD->isGeneratedFromInheritedConstructor()); } TEST_P(ImportFunctions, CTADWithLocalTypedef) { @@ -8119,6 +8123,45 @@ TEST_P(ImportFunctions, CTADWithLocalTypedef) { ASSERT_TRUE(ToD); } +TEST_P(ImportFunctions, CTADAliasTemplate) { + Decl *TU = getTuDecl( + R"( + template <typename T> struct A { + A(T); + }; + template<typename T> + using B = A<T>; + B b{(int)0}; + )", + Lang_CXX20, "input.cc"); + auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match( + TU, cxxDeductionGuideDecl(hasParameter(0, hasType(asString("int"))))); + auto *ToD = Import(FromD, Lang_CXX20); + ASSERT_TRUE(ToD); + EXPECT_FALSE(ToD->isGeneratedFromInheritedConstructor()); + EXPECT_TRUE(ToD->getSourceDeductionGuide()); +} + +TEST_P(ImportFunctions, CTADInheritedCtor) { + Decl *TU = getTuDecl( + R"( + template <typename T> struct A { + A(T); + }; + template <typename T> struct B : public A<T> { + using A<T>::A; + }; + B b{(int)0}; + )", + Lang_CXX23, "input.cc"); + auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match( + TU, cxxDeductionGuideDecl(hasParameter(0, hasType(asString("int"))))); + auto *ToD = Import(FromD, Lang_CXX23); + ASSERT_TRUE(ToD); + EXPECT_TRUE(ToD->isGeneratedFromInheritedConstructor()); + EXPECT_TRUE(ToD->getSourceDeductionGuide()); +} + TEST_P(ImportFunctions, ParmVarDeclDeclContext) { constexpr auto FromTUCode = R"( void f(int P); diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 27e2213e54caa..241e250e2bcc5 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -429,7 +429,7 @@ <h2 id="cxx23">C++23 implementation status</h2> <tr> <td>Class template argument deduction from inherited constructors</td> <td><a href="https://wg21.link/P2582R1">P2582R1</a></td> - <td class="none" align="center">No</td> + <td class="unreleased" align="center">Clang 20</td> </tr> <tr> <td>Portable assumptions</td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits