llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-modules Author: None (antangelo) <details> <summary>Changes</summary> 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 --- Patch is 53.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/98788.diff 13 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+2) - (modified) clang/include/clang/AST/DeclCXX.h (+33-3) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4) - (modified) clang/lib/AST/ASTImporter.cpp (+5-3) - (modified) clang/lib/AST/DeclCXX.cpp (+6-4) - (modified) clang/lib/Sema/SemaOverload.cpp (+67) - (modified) clang/lib/Sema/SemaTemplate.cpp (+359-30) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+2-1) - (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+2) - (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+2) - (added) clang/test/SemaCXX/cxx23-ctad-inherited-ctors.cpp (+260) - (modified) clang/unittests/AST/ASTImporterTest.cpp (+43) - (modified) clang/www/cxx_status.html (+1-1) ``````````diff 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(), AliasTemp... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/98788 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits