https://github.com/hokein created https://github.com/llvm/llvm-project/pull/96686
This patch addresses an issue where non-template explicit deduction guides were not considered when synthesized the deduction guides for alias templates. Fixes #94927. >From 92f9733b620f8d15283c520adceae161b11e8347 Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Tue, 25 Jun 2024 10:16:54 +0200 Subject: [PATCH] [clang] CTAD: Fix deduction guides for alias are not generated from non-template explicit deduction guides. --- clang/lib/Sema/SemaTemplate.cpp | 64 +++++++++++++++++--- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 42 +++++++++++++ 2 files changed, 98 insertions(+), 8 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index a032e3ec6f635..32879c0945bae 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2262,8 +2262,12 @@ class ExtractTypeForDeductionGuide } }; -// Build a deduction guide with the specified parameter types. -FunctionTemplateDecl *buildDeductionGuide( +// Build a deduction guide using the provided information. +// +// A deduction guide can be either a template or a non-template function +// declaration. If \p TemplateParams is null, a non-template function +// declaration will be created. +NamedDecl *buildDeductionGuide( Sema &SemaRef, TemplateDecl *OriginalTemplate, TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor, ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart, @@ -2289,16 +2293,21 @@ FunctionTemplateDecl *buildDeductionGuide( Param->setDeclContext(Guide); for (auto *TD : MaterializedTypedefs) TD->setDeclContext(Guide); + if (isa<CXXRecordDecl>(DC)) + Guide->setAccess(AS_public); + + if (!TemplateParams) { + DC->addDecl(Guide); + return Guide; + } auto *GuideTemplate = FunctionTemplateDecl::Create( SemaRef.Context, DC, Loc, DeductionGuideName, TemplateParams, Guide); GuideTemplate->setImplicit(IsImplicit); Guide->setDescribedFunctionTemplate(GuideTemplate); - if (isa<CXXRecordDecl>(DC)) { - Guide->setAccess(AS_public); + if (isa<CXXRecordDecl>(DC)) GuideTemplate->setAccess(AS_public); - } DC->addDecl(GuideTemplate); return GuideTemplate; @@ -2914,7 +2923,8 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef, ASTContext &Context = SemaRef.Context; // Constraint AST nodes must use uninstantiated depth. if (auto *PrimaryTemplate = - AliasTemplate->getInstantiatedFromMemberTemplate()) { + AliasTemplate->getInstantiatedFromMemberTemplate(); + PrimaryTemplate && TemplateParams.size() > 0) { LocalInstantiationScope Scope(SemaRef); // Adjust the depth for TemplateParams. @@ -3180,12 +3190,12 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, FPrimeTemplateParams, AliasTemplate->getTemplateParameters()->getRAngleLoc(), /*RequiresClause=*/RequiresClause); - FunctionTemplateDecl *Result = buildDeductionGuide( + auto *Result = cast<FunctionTemplateDecl>(buildDeductionGuide( SemaRef, AliasTemplate, FPrimeTemplateParamList, GG->getCorrespondingConstructor(), GG->getExplicitSpecifier(), GG->getTypeSourceInfo(), AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(), AliasTemplate->getEndLoc(), - F->isImplicit()); + F->isImplicit())); cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl()) ->setDeductionCandidateKind(GG->getDeductionCandidateKind()); return Result; @@ -3216,6 +3226,44 @@ void DeclareImplicitDeductionGuidesForTypeAlias( Guides.suppressDiagnostics(); for (auto *G : Guides) { + if (auto *DG = dyn_cast<CXXDeductionGuideDecl>(G)) { + // The deduction guide is a non-template function decl, we just clone it. + auto *FunctionType = + SemaRef.Context.getTrivialTypeSourceInfo(DG->getType()); + FunctionProtoTypeLoc FPTL = + FunctionType->getTypeLoc().castAs<FunctionProtoTypeLoc>(); + + // Clone the parameters. + unsigned ProcessedParamIndex = 0; + for (auto *P : DG->parameters()) { + auto *TSI = SemaRef.Context.getTrivialTypeSourceInfo(P->getType()); + ParmVarDecl *NewParam = ParmVarDecl::Create( + SemaRef.Context, G->getDeclContext(), P->getBeginLoc(), + P->getLocation(), nullptr, TSI->getType(), TSI, SC_None, nullptr); + NewParam->setScopeInfo(0, ProcessedParamIndex); + FPTL.setParam(ProcessedParamIndex, NewParam); + ProcessedParamIndex++; + } + auto *Transformed = cast<FunctionDecl>(buildDeductionGuide( + SemaRef, AliasTemplate, /*TemplateParams=*/nullptr, + /*Constructor=*/nullptr, DG->getExplicitSpecifier(), FunctionType, + AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(), + AliasTemplate->getEndLoc(), DG->isImplicit())); + + // 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(), {}); + if (auto *RC = DG->getTrailingRequiresClause()) { + auto Conjunction = + SemaRef.BuildBinOp(SemaRef.getCurScope(), SourceLocation{}, + BinaryOperatorKind::BO_LAnd, RC, Constraint); + if (!Conjunction.isInvalid()) + Constraint = Conjunction.getAs<Expr>(); + } + Transformed->setTrailingRequiresClause(Constraint); + } FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(G); if (!F) continue; diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index b71dfc6ccaf4f..0881ce65631d7 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -402,3 +402,45 @@ template <typename U> using AFoo = A1<int>::A2<int>::Foo<U>; AFoo case3(1); } // namespace test24 + +// GH94927 +namespace test25 { +template <typename T> +struct A { + A(T); +}; +A(int) -> A<char>; + +template <typename U> +using B1 = A<U>; +B1 b1(100); // deduce to A<char>; +static_assert(__is_same(decltype(b1), A<char>)); + +template <typename U> +requires (!__is_same(U, char)) // filter out the explicit deduction guide. +using B2 = A<U>; +template <typename V> +using B3 = B2<V>; + +B2 b2(100); // deduced to A<int>; +static_assert(__is_same(decltype(b2), A<int>)); +B3 b3(100); // decuded to A<int>; +static_assert(__is_same(decltype(b3), A<int>)); + + +// the nested case +template <typename T1> +struct Out { + template <typename T2> + struct A { + A(T2); + }; + A(int) -> A<T1>; + + template <typename T3> + using B = A<T3>; +}; + +Out<float>::B out(100); // deduced to Out<float>::A<float>; +static_assert(__is_same(decltype(out), Out<float>::A<float>)); +} // namespace test25 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits