[clang] [clang] Implement CTAD for type alias template. (PR #77890)
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 27 11:24:08 PST 2024
https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/77890
>From 334cb49012443961720e29df92211213f5a7c043 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Wed, 24 Jan 2024 14:55:03 +0100
Subject: [PATCH 1/6] [clang] Implement Class Template Argument Deduction
(CTAD) for type alias templates P1814R0.
This patch implements the C++20 feature -- CTAD for alias templates.
This is an initial patch, which covers most of pieces, the major missing
piece is to implement the associated constraints (over.match.class.deduct#3.3)
for the synthesized deduction guides (we can address in a followup).
This patch also refactors the existing `ConvertConstructorToDeductionGuideTransform`
to allow code reuse.
---
clang/include/clang/Sema/Sema.h | 14 +-
clang/lib/Sema/CMakeLists.txt | 1 +
clang/lib/Sema/CTAD.cpp | 209 ++++++++++
clang/lib/Sema/CTAD.h | 65 +++
clang/lib/Sema/SemaInit.cpp | 374 +++++++++++++++++-
clang/lib/Sema/SemaTemplate.cpp | 168 ++------
clang/lib/Sema/SemaTemplateDeduction.cpp | 9 +
clang/lib/Sema/SemaTemplateInstantiate.cpp | 25 +-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 25 +-
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 133 +++++++
10 files changed, 856 insertions(+), 167 deletions(-)
create mode 100644 clang/lib/Sema/CTAD.cpp
create mode 100644 clang/lib/Sema/CTAD.h
create mode 100644 clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 53f06bcaeca166..6ad80e6f4b1b05 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9335,6 +9335,12 @@ class Sema final {
ArrayRef<TemplateArgument> TemplateArgs,
sema::TemplateDeductionInfo &Info);
+ TemplateDeductionResult DeduceTemplateArguments(
+ TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps,
+ ArrayRef<TemplateArgument> As, sema::TemplateDeductionInfo &Info,
+ SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+ bool NumberOfArgumentsMustMatch);
+
TemplateDeductionResult SubstituteExplicitTemplateArguments(
FunctionTemplateDecl *FunctionTemplate,
TemplateArgumentListInfo &ExplicitTemplateArgs,
@@ -10525,9 +10531,11 @@ class Sema final {
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction);
- FunctionDecl *InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD,
- const TemplateArgumentList *Args,
- SourceLocation Loc);
+ FunctionDecl *InstantiateFunctionDeclaration(
+ FunctionTemplateDecl *FTD, const TemplateArgumentList *Args,
+ SourceLocation Loc,
+ CodeSynthesisContext::SynthesisKind CSC =
+ CodeSynthesisContext::ExplicitTemplateArgumentSubstitution);
void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
FunctionDecl *Function,
bool Recursive = false,
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 862f9d4ffb825d..ef8d2aed07d112 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -14,6 +14,7 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins
add_clang_library(clangSema
AnalysisBasedWarnings.cpp
+ CTAD.cpp
CodeCompleteConsumer.cpp
DeclSpec.cpp
DelayedDiagnostic.cpp
diff --git a/clang/lib/Sema/CTAD.cpp b/clang/lib/Sema/CTAD.cpp
new file mode 100644
index 00000000000000..745878c717fc4c
--- /dev/null
+++ b/clang/lib/Sema/CTAD.cpp
@@ -0,0 +1,209 @@
+//===--- CTAD.cpp - -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CTAD.h"
+#include "TreeTransform.h"
+#include "TypeLocBuilder.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTMutationListener.h"
+#include "clang/AST/ASTStructuralEquivalence.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Template.h"
+#include "llvm/ADT/ArrayRef.h"
+#include <optional>
+
+namespace clang {
+
+namespace {
+/// Tree transform to "extract" a transformed type from a class template's
+/// constructor to a deduction guide.
+class ExtractTypeForDeductionGuide
+ : public TreeTransform<ExtractTypeForDeductionGuide> {
+ llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs;
+
+public:
+ typedef TreeTransform<ExtractTypeForDeductionGuide> Base;
+ ExtractTypeForDeductionGuide(
+ Sema &SemaRef,
+ llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs)
+ : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs) {}
+
+ TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); }
+
+ QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) {
+ ASTContext &Context = SemaRef.getASTContext();
+ TypedefNameDecl *OrigDecl = TL.getTypedefNameDecl();
+ TypedefNameDecl *Decl = OrigDecl;
+ // Transform the underlying type of the typedef and clone the Decl only if
+ // the typedef has a dependent context.
+ if (OrigDecl->getDeclContext()->isDependentContext()) {
+ TypeLocBuilder InnerTLB;
+ QualType Transformed =
+ TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc());
+ TypeSourceInfo *TSI = InnerTLB.getTypeSourceInfo(Context, Transformed);
+ if (isa<TypeAliasDecl>(OrigDecl))
+ Decl = TypeAliasDecl::Create(
+ Context, Context.getTranslationUnitDecl(), OrigDecl->getBeginLoc(),
+ OrigDecl->getLocation(), OrigDecl->getIdentifier(), TSI);
+ else {
+ assert(isa<TypedefDecl>(OrigDecl) && "Not a Type alias or typedef");
+ Decl = TypedefDecl::Create(
+ Context, Context.getTranslationUnitDecl(), OrigDecl->getBeginLoc(),
+ OrigDecl->getLocation(), OrigDecl->getIdentifier(), TSI);
+ }
+ MaterializedTypedefs.push_back(Decl);
+ }
+
+ QualType TDTy = Context.getTypedefType(Decl);
+ TypedefTypeLoc TypedefTL = TLB.push<TypedefTypeLoc>(TDTy);
+ TypedefTL.setNameLoc(TL.getNameLoc());
+
+ return TDTy;
+ }
+};
+} // namespace
+
+ParmVarDecl *transformFunctionTypeParam(
+ Sema &SemaRef, ParmVarDecl *OldParam, DeclContext *DC,
+ MultiLevelTemplateArgumentList &Args,
+ llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
+ TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
+ TypeSourceInfo *NewDI;
+ if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
+ // Expand out the one and only element in each inner pack.
+ Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, 0);
+ NewDI = SemaRef.SubstType(PackTL.getPatternLoc(), Args,
+ OldParam->getLocation(), OldParam->getDeclName());
+ if (!NewDI)
+ return nullptr;
+ NewDI = SemaRef.CheckPackExpansion(NewDI, PackTL.getEllipsisLoc(),
+ PackTL.getTypePtr()->getNumExpansions());
+ } else
+ NewDI = SemaRef.SubstType(OldDI, Args, OldParam->getLocation(),
+ OldParam->getDeclName());
+ if (!NewDI)
+ return nullptr;
+
+ // Extract the type. This (for instance) replaces references to typedef
+ // members of the current instantiations with the definitions of those
+ // typedefs, avoiding triggering instantiation of the deduced type during
+ // deduction.
+ NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs)
+ .transform(NewDI);
+
+ // Resolving a wording defect, we also inherit default arguments from the
+ // constructor.
+ ExprResult NewDefArg;
+ if (OldParam->hasDefaultArg()) {
+ // We don't care what the value is (we won't use it); just create a
+ // placeholder to indicate there is a default argument.
+ QualType ParamTy = NewDI->getType();
+ NewDefArg = new (SemaRef.Context)
+ OpaqueValueExpr(OldParam->getDefaultArgRange().getBegin(),
+ ParamTy.getNonLValueExprType(SemaRef.Context),
+ ParamTy->isLValueReferenceType() ? VK_LValue
+ : ParamTy->isRValueReferenceType() ? VK_XValue
+ : VK_PRValue);
+ }
+ // Handle arrays and functions decay.
+ auto NewType = NewDI->getType();
+ if (NewType->isArrayType() || NewType->isFunctionType())
+ NewType = SemaRef.Context.getDecayedType(NewType);
+
+ ParmVarDecl *NewParam = ParmVarDecl::Create(
+ SemaRef.Context, DC, OldParam->getInnerLocStart(),
+ OldParam->getLocation(), OldParam->getIdentifier(), NewType, NewDI,
+ OldParam->getStorageClass(), NewDefArg.get());
+ NewParam->setScopeInfo(OldParam->getFunctionScopeDepth(),
+ OldParam->getFunctionScopeIndex());
+ SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, NewParam);
+ return NewParam;
+}
+
+TemplateTypeParmDecl *
+transformTemplateTypeParam(Sema &SemaRef, DeclContext *DC,
+ TemplateTypeParmDecl *TTP,
+ MultiLevelTemplateArgumentList &Args,
+ unsigned NewDepth, unsigned NewIndex) {
+ // TemplateTypeParmDecl's index cannot be changed after creation, so
+ // substitute it directly.
+ auto *NewTTP = TemplateTypeParmDecl::Create(
+ SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(), NewDepth,
+ NewIndex, TTP->getIdentifier(), TTP->wasDeclaredWithTypename(),
+ TTP->isParameterPack(), TTP->hasTypeConstraint(),
+ TTP->isExpandedParameterPack()
+ ? std::optional<unsigned>(TTP->getNumExpansionParameters())
+ : std::nullopt);
+ if (const auto *TC = TTP->getTypeConstraint())
+ SemaRef.SubstTypeConstraint(NewTTP, TC, Args,
+ /*EvaluateConstraint*/ true);
+ if (TTP->hasDefaultArgument()) {
+ TypeSourceInfo *InstantiatedDefaultArg =
+ SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
+ TTP->getDefaultArgumentLoc(), TTP->getDeclName());
+ if (InstantiatedDefaultArg)
+ NewTTP->setDefaultArgument(InstantiatedDefaultArg);
+ }
+ SemaRef.CurrentInstantiationScope->InstantiatedLocal(TTP, NewTTP);
+ return NewTTP;
+}
+
+FunctionTemplateDecl *
+buildDeductionGuide(Sema &SemaRef, TemplateDecl *OriginalTemplate,
+ TemplateParameterList *TemplateParams,
+ CXXConstructorDecl *Ctor, ExplicitSpecifier ES,
+ TypeSourceInfo *TInfo, SourceLocation LocStart,
+ SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
+ llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs) {
+ DeclContext *DC = OriginalTemplate->getDeclContext();
+ auto DeductionGuideName =
+ SemaRef.Context.DeclarationNames.getCXXDeductionGuideName(
+ OriginalTemplate);
+
+ DeclarationNameInfo Name(DeductionGuideName, Loc);
+ ArrayRef<ParmVarDecl *> Params =
+ TInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams();
+
+ // Build the implicit deduction guide template.
+ auto *Guide =
+ CXXDeductionGuideDecl::Create(SemaRef.Context, DC, LocStart, ES, Name,
+ TInfo->getType(), TInfo, LocEnd, Ctor);
+ Guide->setImplicit(IsImplicit);
+ Guide->setParams(Params);
+
+ for (auto *Param : Params)
+ Param->setDeclContext(Guide);
+ for (auto *TD : MaterializedTypedefs)
+ TD->setDeclContext(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);
+ GuideTemplate->setAccess(AS_public);
+ }
+
+ DC->addDecl(GuideTemplate);
+ return GuideTemplate;
+}
+
+} // namespace clang
diff --git a/clang/lib/Sema/CTAD.h b/clang/lib/Sema/CTAD.h
new file mode 100644
index 00000000000000..88110230318f81
--- /dev/null
+++ b/clang/lib/Sema/CTAD.h
@@ -0,0 +1,65 @@
+//===--- CTAD.h - Helper functions for CTAD -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines helper functions for the class template argument deduction
+// (CTAD) implementation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTMutationListener.h"
+#include "clang/AST/ASTStructuralEquivalence.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Template.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace clang {
+
+// Transform a given function parameter decl into a deduction guide parameter
+// decl.
+ParmVarDecl *transformFunctionTypeParam(
+ Sema &SemaRef, ParmVarDecl *OldParam, DeclContext *DC,
+ MultiLevelTemplateArgumentList &Args,
+ llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs);
+
+// Transform a given template type parameter into a deduction guide template
+// parameter, rebuilding any internal references to earlier parameters and
+// re-indexing as we go.
+TemplateTypeParmDecl *transformTemplateTypeParam(
+ Sema &SemaRef, DeclContext *DC, TemplateTypeParmDecl *TPT,
+ MultiLevelTemplateArgumentList &Args, unsigned NewDepth, unsigned NewIndex);
+// Similar to above, but for non-type template or template template parameters.
+template <typename NonTypeTemplateOrTemplateTemplateParmDecl>
+NonTypeTemplateOrTemplateTemplateParmDecl *
+transformTemplateParam(Sema &SemaRef, DeclContext *DC,
+ NonTypeTemplateOrTemplateTemplateParmDecl *OldParam,
+ MultiLevelTemplateArgumentList &Args,
+ unsigned NewIndex) {
+ // Ask the template instantiator to do the heavy lifting for us, then adjust
+ // the index of the parameter once it's done.
+ auto *NewParam = cast<NonTypeTemplateOrTemplateTemplateParmDecl>(
+ SemaRef.SubstDecl(OldParam, DC, Args));
+ NewParam->setPosition(NewIndex);
+ return NewParam;
+}
+
+// Build a deduction guide with the specified parameter types.
+FunctionTemplateDecl *buildDeductionGuide(
+ Sema &SemaRef, TemplateDecl *OriginalTemplate,
+ TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor,
+ ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart,
+ SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
+ llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {});
+
+} // namespace clang
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 0fd458837163e5..812cb870180321 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10,13 +10,19 @@
//
//===----------------------------------------------------------------------===//
+#include "CTAD.h"
+#include "TypeLocBuilder.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclAccessPair.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
-#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.h"
#include "clang/AST/IgnoreExpr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceManager.h"
@@ -28,7 +34,9 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/SemaInternal.h"
+#include "clang/Sema/Template.h"
#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallString.h"
@@ -10587,6 +10595,213 @@ static bool isOrIsDerivedFromSpecializationOf(CXXRecordDecl *RD,
return !(NotSpecialization(RD) && RD->forallBases(NotSpecialization));
}
+// Transform to form a corresponding deduction guide for type alias template
+// decl.
+//
+// This class implements the C++ [over.match.class.deduct]p3:
+// ... Let g denote the result of substituting these deductions into f. If
+// substitution succeeds, form a function or function template f' with the
+// following properties and add it to the set of guides of A...
+class AliasTemplateDeductionGuideTransform {
+public:
+ AliasTemplateDeductionGuideTransform(Sema &S, TypeAliasTemplateDecl *Alias)
+ : SemaRef(S), AliasTemplate(Alias), DC(Alias->getDeclContext()) {}
+ // Returns the result of substituting the deduced template arguments into F.
+ NamedDecl *transform(CXXDeductionGuideDecl *F,
+ ArrayRef<TemplateArgument> DeducedArgs,
+ ArrayRef<NamedDecl *> NonDeducedTemplateParamsInF) {
+ // Template parameters of the f'.
+ //
+ // C++ [over.match.class.deduct]p3.2:
+ // If f is a function template, f' is a function template whose template
+ // parameter list consists of all the template parameters of A (including
+ // their default template arguments) that appear in the above deductions
+ // or (recursively) in their default template arguments
+ SmallVector<NamedDecl *> TemplateParamsInFPrime =
+ FindAppearedTemplateParamsInAlias(DeducedArgs);
+ // ...followed by the template parameters of f that were not deduced
+ // (including their default template arguments)
+ TemplateParamsInFPrime.append(NonDeducedTemplateParamsInF.begin(),
+ NonDeducedTemplateParamsInF.end());
+
+ LocalInstantiationScope Scope(SemaRef);
+ SmallVector<TemplateArgument, 16> Depth1Args;
+ SmallVector<NamedDecl *, 16> AllParams;
+ SmallVector<TemplateArgument, 16> SubstArgs;
+ unsigned TemplateParamIndex = 0;
+ TemplateParameterList *TemplateParams = nullptr;
+
+ for (NamedDecl *Param : TemplateParamsInFPrime) {
+ MultiLevelTemplateArgumentList Args;
+
+ Args.setKind(TemplateSubstitutionKind::Rewrite);
+ Args.addOuterTemplateArguments(Depth1Args);
+ Args.addOuterRetainedLevel();
+ NamedDecl *NewParam =
+ transformTemplateParameter(Param, Args, TemplateParamIndex++);
+ if (!NewParam) {
+ llvm::errs() << "Faile to generate new param!\n";
+ return nullptr;
+ }
+ auto NewArgumentForNewParam =
+ SemaRef.Context.getCanonicalTemplateArgument(
+ SemaRef.Context.getInjectedTemplateArg(NewParam));
+ Depth1Args.push_back(NewArgumentForNewParam);
+ AllParams.push_back(NewParam);
+ SubstArgs.push_back(NewArgumentForNewParam);
+ }
+ // FIXME: substitute new template parameters into the requires-clause.
+ TemplateParams = TemplateParameterList::Create(
+ SemaRef.Context,
+ AliasTemplate->getTemplateParameters()->getTemplateLoc(),
+ AliasTemplate->getTemplateParameters()->getLAngleLoc(), AllParams,
+ AliasTemplate->getTemplateParameters()->getRAngleLoc(),
+ /*RequiresClause=*/nullptr);
+
+ MultiLevelTemplateArgumentList Args;
+ Args.setKind(TemplateSubstitutionKind::Rewrite);
+ Args.addOuterTemplateArguments(SubstArgs);
+ Args.addOuterRetainedLevel();
+
+ FunctionProtoTypeLoc FPTL = F->getTypeSourceInfo()
+ ->getTypeLoc()
+ .getAsAdjusted<FunctionProtoTypeLoc>();
+ assert(FPTL && "no prototype for underlying deduction guides");
+
+ // Transform the type of the function, adjusting the return type and
+ // replacing references to the old parameters with references to the
+ // new ones.
+ TypeLocBuilder TLB;
+ SmallVector<ParmVarDecl *, 8> Params;
+ SmallVector<TypedefNameDecl *, 4> MaterializedTypedefs;
+ QualType NewType = transformFunctionProtoType(
+ TLB, FPTL, Params, Args, F->getReturnType(), MaterializedTypedefs);
+ if (NewType.isNull())
+ return nullptr;
+ TypeSourceInfo *NewTInfo = TLB.getTypeSourceInfo(SemaRef.Context, NewType);
+
+ return clang::buildDeductionGuide(
+ SemaRef, AliasTemplate, TemplateParams,
+ F->getCorrespondingConstructor(), F->getExplicitSpecifier(), NewTInfo,
+ AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(),
+ AliasTemplate->getEndLoc(), F->isImplicit(), MaterializedTypedefs);
+ }
+
+private:
+ // Find all template parameters of the AliasTemplate that appear in the
+ // DeducedArgs.
+ SmallVector<NamedDecl *>
+ FindAppearedTemplateParamsInAlias(ArrayRef<TemplateArgument> DeducedArgs) {
+ struct FindAppearedTemplateParams
+ : public RecursiveASTVisitor<FindAppearedTemplateParams> {
+ llvm::DenseSet<NamedDecl *> TemplateParamsInAlias;
+ llvm::DenseSet<const NamedDecl *> AppearedTemplateParams;
+
+ FindAppearedTemplateParams(ArrayRef<NamedDecl *> TemplateParamsInAlias)
+ : TemplateParamsInAlias(TemplateParamsInAlias.begin(),
+ TemplateParamsInAlias.end()) {}
+
+ bool VisitTemplateTypeParmType(TemplateTypeParmType *TTP) {
+ MarkAppeared(TTP->getDecl());
+ return true;
+ }
+ bool VisitDeclRefExpr(DeclRefExpr *DRE) {
+ MarkAppeared(DRE->getFoundDecl());
+ return true;
+ }
+
+ void MarkAppeared(NamedDecl *ND) {
+ if (TemplateParamsInAlias.contains(ND))
+ AppearedTemplateParams.insert(ND);
+ }
+ };
+ ArrayRef<NamedDecl *> TemplateParamsInAlias =
+ AliasTemplate->getTemplateParameters()->asArray();
+ FindAppearedTemplateParams MarkAppeared(TemplateParamsInAlias);
+ MarkAppeared.TraverseTemplateArguments(DeducedArgs);
+
+ SmallVector<NamedDecl *> Results;
+ for (auto *TP : TemplateParamsInAlias)
+ if (MarkAppeared.AppearedTemplateParams.contains(TP))
+ Results.push_back(TP);
+ return Results;
+ }
+
+ // Transform a template parameter of f into a template parameter of f'
+ // rebuilding any internal references to earlier parameters and renumbering as
+ // we go.
+ NamedDecl *transformTemplateParameter(NamedDecl *TemplateParam,
+ MultiLevelTemplateArgumentList &Args,
+ int NewIndex) {
+ if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam))
+ return clang::transformTemplateTypeParam(SemaRef, DC, TTP, Args,
+ TTP->getDepth(), NewIndex);
+
+ if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam))
+ return clang::transformTemplateParam(SemaRef, DC, TTP, Args, NewIndex);
+ auto *NTTP = cast<NonTypeTemplateParmDecl>(TemplateParam);
+ return clang::transformTemplateParam(SemaRef, DC, NTTP, Args, NewIndex);
+ }
+
+ // Transform the function proto type of f into a function prototype of f'.
+ QualType transformFunctionProtoType(
+ TypeLocBuilder &TLB, FunctionProtoTypeLoc TL,
+ SmallVectorImpl<ParmVarDecl *> &Params,
+ MultiLevelTemplateArgumentList &Args, QualType ReturnType,
+ SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
+ SmallVector<QualType, 4> ParamTypes;
+ const FunctionProtoType *T = TL.getTypePtr();
+
+ for (auto *OldParam : TL.getParams()) {
+ ParmVarDecl *NewParam = clang::transformFunctionTypeParam(
+ SemaRef, OldParam, DC, Args, MaterializedTypedefs);
+ if (!NewParam)
+ return QualType();
+ ParamTypes.push_back(NewParam->getType());
+ Params.push_back(NewParam);
+ }
+
+ // The return type of the deduction guide f is InjectedClassNameType,
+ // transform it to a TemplateSpecializationType.
+ if (const auto *ET = ReturnType->getAs<InjectedClassNameType>())
+ ReturnType = ET->getInjectedSpecializationType();
+ auto DeductionGuideName =
+ SemaRef.Context.DeclarationNames.getCXXDeductionGuideName(
+ AliasTemplate);
+ ReturnType = SemaRef.SubstType(ReturnType, Args, SourceLocation(),
+ DeductionGuideName);
+
+ // Resolving a wording defect, we also inherit the variadicness of the
+ // constructor.
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.Variadic = T->isVariadic();
+ EPI.HasTrailingReturn = true;
+
+ QualType FunctionTy = SemaRef.BuildFunctionType(
+ ReturnType, ParamTypes, TL.getBeginLoc(), DeductionGuideName, EPI);
+ if (FunctionTy.isNull())
+ return QualType();
+ assert(FunctionTy->getTypeClass() == Type::FunctionProto);
+ // Pushes spaces for the new FunctionProtoTypeLoc.
+ TLB.pushTrivial(SemaRef.Context,
+ TypeLoc(FunctionTy, nullptr).getNextTypeLoc().getType(),
+ SourceLocation());
+ FunctionProtoTypeLoc TargetTL = TLB.push<FunctionProtoTypeLoc>(FunctionTy);
+ TargetTL.setLocalRangeBegin(TL.getLocalRangeBegin());
+ TargetTL.setLParenLoc(TL.getLParenLoc());
+ TargetTL.setRParenLoc(TL.getRParenLoc());
+ TargetTL.setExceptionSpecRange(SourceRange());
+ TargetTL.setLocalRangeEnd(TL.getLocalRangeEnd());
+ for (unsigned I = 0, E = TargetTL.getNumParams(); I != E; ++I)
+ TargetTL.setParam(I, Params[I]);
+ return FunctionTy;
+ }
+
+ Sema &SemaRef;
+ TypeAliasTemplateDecl *AliasTemplate = nullptr;
+ DeclContext *DC = nullptr;
+};
+
QualType Sema::DeduceTemplateSpecializationFromInitializer(
TypeSourceInfo *TSInfo, const InitializedEntity &Entity,
const InitializationKind &Kind, MultiExprArg Inits) {
@@ -10598,10 +10813,48 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
if (TemplateName.isDependent())
return SubstAutoTypeDependent(TSInfo->getType());
- // We can only perform deduction for class templates.
+ // We can only perform deduction for class templates or alias templates.
auto *Template =
dyn_cast_or_null<ClassTemplateDecl>(TemplateName.getAsTemplateDecl());
+
+ TypeAliasTemplateDecl *AliasTemplate = nullptr;
+ llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs;
+ if (!Template && getLangOpts().CPlusPlus20) { // type alias template
+ if (AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>(
+ TemplateName.getAsTemplateDecl());
+ AliasTemplate) {
+ // Unrap the sugar ElaboratedType.
+ auto RhsType = AliasTemplate->getTemplatedDecl()
+ ->getUnderlyingType()
+ .getSingleStepDesugaredType(Context);
+ if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) {
+ // TemplateName in TEST can be a TypeAliasTemplateDecl if
+ // the right hand side of the alias is also a type alias, e.g.
+ //
+ // template<typename T>
+ // using AliasFoo1 = Foo<T>; // Foo<T> is a class template
+ // specialization
+ //
+ // template<typename T>
+ // using AliasFoo2 = AliasFoo1<T>; // AliasFoo1<T> is a type alias
+ // FIXME: support this case, we need to recursively perform deductions.
+ Template = dyn_cast_or_null<ClassTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl());
+ AliasRhsTemplateArgs = TST->template_arguments();
+ } else if (const auto *RT = RhsType->getAs<RecordType>()) {
+ // Cases where template arguments in the RHS of the alias are not
+ // dependent. e.g.
+ // using AliasFoo = Foo<bool>;
+ if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(
+ RT->getAsCXXRecordDecl())) {
+ Template = CTSD->getSpecializedTemplate();
+ AliasRhsTemplateArgs = CTSD->getTemplateArgs().asArray();
+ }
+ }
+ }
+ }
if (!Template) {
+ // FIXME: update the diagnostic message to include C++20 alias templates
Diag(Kind.getLocation(),
diag::err_deduced_non_class_template_specialization_type)
<< (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName;
@@ -10640,6 +10893,113 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// clear on this, but they're not found by name so access does not apply.
Guides.suppressDiagnostics();
+ SmallVector<DeclAccessPair> GuidesCandidates;
+ if (AliasTemplate) {
+ for (auto *G : Guides) {
+ FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(G);
+ if (!F)
+ // FIXME: handle the non-template deduction guide case.
+ continue;
+ auto RType = F->getTemplatedDecl()->getReturnType();
+ // The (trailing) return type of the deduction guide.
+ const TemplateSpecializationType *FReturnType = nullptr;
+ if (const auto *InjectedCNT = RType->getAs<InjectedClassNameType>()) {
+ // implicitly-generated deduction guide.
+ FReturnType = InjectedCNT->getInjectedTST();
+ } else if (const auto *ET = RType->getAs<ElaboratedType>()) {
+ // explicit deduction guide.
+ FReturnType = ET->getNamedType()->getAs<TemplateSpecializationType>();
+ }
+ assert(FReturnType);
+ if (FReturnType) {
+ sema::TemplateDeductionInfo TDeduceInfo(Kind.getLocation());
+ SmallVector<DeducedTemplateArgument> DeduceResults;
+ // DeduceResults.resize(FReturnType->template_arguments().size());
+ DeduceResults.resize(F->getTemplateParameters()->size());
+
+ // Deduce template arguments of the deduction guide f from the RHS of
+ // the alias.
+ //
+ // C++ [over.match.class.deduct]p3: ...For each function or function
+ // template f in the guides of the template named by the
+ // simple-template-id of the defining-type-id, the template arguments
+ // of the return type of f are deduced from the defining-type-id of A
+ // according to the process in [temp.deduct.type] with the exception
+ // that deduction does not fail if not all template arguments are
+ // deduced.
+ //
+ //
+ // template<typename X, typename Y>
+ // f(X, Y) -> f<Y, X>;
+ //
+ // template<typename U>
+ // using alias = f<int, U>;
+ //
+ // The RHS of alias is f<int, U>, we deduced the template arguments of
+ // the return type of the deduction guide from it: Y->int, X->U
+ //
+ // FIXME: DeduceTemplateArguments stops immediately at the first
+ // non-deduced template parameter, extend it to continue performing
+ // deduction for rest of parameters.
+ DeduceTemplateArguments(
+ F->getTemplateParameters(), FReturnType->template_arguments(),
+ AliasRhsTemplateArgs, TDeduceInfo, DeduceResults,
+ /*NumberOfArgumentsMustMatch=*/false);
+
+ SmallVector<TemplateArgument> DeducedArgs;
+ SmallVector<NamedDecl *> NonDeducedTemplateParamsInF;
+ // !!NOTE: DeduceResults respects the sequence of template parameters.
+ for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
+ const auto &D = DeduceResults[Index];
+ if (!D.isNull()) // Deduced
+ DeducedArgs.push_back(D);
+ else
+ NonDeducedTemplateParamsInF.push_back(
+ F->getTemplateParameters()->getParam(Index));
+ }
+ auto *DeducedArgList =
+ TemplateArgumentList::CreateCopy(this->Context, DeducedArgs);
+
+ // Let g denote the result of substituting these deductions into f.
+ //
+ // FIXME: is using the InstantiateFunctionDeclaration API a right
+ // implement choice? It has some side effects which creates a
+ // specialization for the deduction guide function template, and
+ // the specialization is added to the the FunctionTemplateDecl, this
+ // is not specified by the standard.
+ //
+ // FIXME: Cache the result.
+ if (auto *G = InstantiateFunctionDeclaration(
+ F, DeducedArgList, AliasTemplate->getLocation(),
+ Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
+ InstantiatingTemplate BuildingDeductionGuides(
+ *this, AliasTemplate->getLocation(), AliasTemplate,
+ Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{});
+ AliasTemplateDeductionGuideTransform Transform(*this, AliasTemplate);
+ // If substitution succeeds, form a function or function template
+ // f' with the following properties and add it to the set of
+ // guides of A:
+ if (auto *FPrime = Transform.transform(
+ dyn_cast<CXXDeductionGuideDecl>(G), DeducedArgs,
+ NonDeducedTemplateParamsInF)) {
+ // FIXME: implement the assoicated constraint per C++
+ // [over.match.class.deduct]p3.3:
+ // The associated constraints ([temp.constr.decl]) are the
+ // conjunction of the associated constraints of g and a
+ // constraint that is satisfied if and only if the arguments
+ // of A are deducible (see below) from the return type.
+ // This could be implemented as part of function overload
+ // resolution below.
+ GuidesCandidates.push_back(
+ DeclAccessPair::make(FPrime, AccessSpecifier::AS_public));
+ }
+ }
+ }
+ }
+ } else {
+ for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I)
+ GuidesCandidates.push_back(I.getPair());
+ }
// Figure out if this is list-initialization.
InitListExpr *ListInit =
(Inits.size() == 1 && Kind.getKind() != InitializationKind::IK_Direct)
@@ -10797,9 +11157,8 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
HasAnyDeductionGuide = true;
}
};
-
- for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) {
- NamedDecl *D = (*I)->getUnderlyingDecl();
+ for (auto I : GuidesCandidates) {
+ NamedDecl *D = (I)->getUnderlyingDecl();
if (D->isInvalidDecl())
continue;
@@ -10812,7 +11171,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
if (!GD->isImplicit())
HasAnyDeductionGuide = true;
- addDeductionCandidate(TD, GD, I.getPair(), OnlyListConstructors,
+ addDeductionCandidate(TD, GD, I, OnlyListConstructors,
/*AllowAggregateDeductionCandidate=*/false);
}
@@ -10852,7 +11211,8 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// Try list constructors unless the list is empty and the class has one or
// more default constructors, in which case those constructors win.
if (!ListInit->getNumInits()) {
- for (NamedDecl *D : Guides) {
+ for (auto D : GuidesCandidates) {
+
auto *FD = dyn_cast<FunctionDecl>(D->getUnderlyingDecl());
if (FD && FD->getMinRequiredArguments() == 0) {
TryListConstructors = false;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 9e516da2aa27a1..c93563b2047b10 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -8,10 +8,12 @@
// This file implements semantic analysis for C++ templates.
//===----------------------------------------------------------------------===//
+#include "CTAD.h"
#include "TreeTransform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
@@ -2334,7 +2336,6 @@ struct ConvertConstructorToDeductionGuideTransform {
NamedDecl *NewParam = transformTemplateParameter(Param, Args);
if (!NewParam)
return nullptr;
-
// Constraints require that we substitute depth-1 arguments
// to match depths when substituted for evaluation later
Depth1Args.push_back(SemaRef.Context.getCanonicalTemplateArgument(
@@ -2407,9 +2408,10 @@ struct ConvertConstructorToDeductionGuideTransform {
return nullptr;
TypeSourceInfo *NewTInfo = TLB.getTypeSourceInfo(SemaRef.Context, NewType);
- return buildDeductionGuide(TemplateParams, CD, CD->getExplicitSpecifier(),
- NewTInfo, CD->getBeginLoc(), CD->getLocation(),
- CD->getEndLoc(), MaterializedTypedefs);
+ return clang::buildDeductionGuide(
+ SemaRef, Template, TemplateParams, CD, CD->getExplicitSpecifier(),
+ NewTInfo, CD->getBeginLoc(), CD->getLocation(), CD->getEndLoc(),
+ /*IsImplicit=*/true, MaterializedTypedefs);
}
/// Build a deduction guide with the specified parameter types.
@@ -2444,8 +2446,9 @@ struct ConvertConstructorToDeductionGuideTransform {
Params.push_back(NewParam);
}
- return buildDeductionGuide(GetTemplateParameterList(Template), nullptr,
- ExplicitSpecifier(), TSI, Loc, Loc, Loc);
+ return clang::buildDeductionGuide(
+ SemaRef, Template, GetTemplateParameterList(Template), nullptr,
+ ExplicitSpecifier(), TSI, Loc, Loc, Loc, /*IsImplicit=*/true);
}
private:
@@ -2454,50 +2457,16 @@ struct ConvertConstructorToDeductionGuideTransform {
/// renumbering as we go.
NamedDecl *transformTemplateParameter(NamedDecl *TemplateParam,
MultiLevelTemplateArgumentList &Args) {
- if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam)) {
- // TemplateTypeParmDecl's index cannot be changed after creation, so
- // substitute it directly.
- auto *NewTTP = TemplateTypeParmDecl::Create(
- SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(),
- TTP->getDepth() - 1, Depth1IndexAdjustment + TTP->getIndex(),
- TTP->getIdentifier(), TTP->wasDeclaredWithTypename(),
- TTP->isParameterPack(), TTP->hasTypeConstraint(),
- TTP->isExpandedParameterPack()
- ? std::optional<unsigned>(TTP->getNumExpansionParameters())
- : std::nullopt);
- if (const auto *TC = TTP->getTypeConstraint())
- SemaRef.SubstTypeConstraint(NewTTP, TC, Args,
- /*EvaluateConstraint*/ true);
- if (TTP->hasDefaultArgument()) {
- TypeSourceInfo *InstantiatedDefaultArg =
- SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
- TTP->getDefaultArgumentLoc(), TTP->getDeclName());
- if (InstantiatedDefaultArg)
- NewTTP->setDefaultArgument(InstantiatedDefaultArg);
- }
- SemaRef.CurrentInstantiationScope->InstantiatedLocal(TemplateParam,
- NewTTP);
- return NewTTP;
- }
-
+ if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam))
+ return clang::transformTemplateTypeParam(
+ SemaRef, DC, TTP, Args, TTP->getDepth() - 1,
+ Depth1IndexAdjustment + TTP->getIndex());
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam))
- return transformTemplateParameterImpl(TTP, Args);
-
- return transformTemplateParameterImpl(
- cast<NonTypeTemplateParmDecl>(TemplateParam), Args);
- }
- template<typename TemplateParmDecl>
- TemplateParmDecl *
- transformTemplateParameterImpl(TemplateParmDecl *OldParam,
- MultiLevelTemplateArgumentList &Args) {
- // Ask the template instantiator to do the heavy lifting for us, then adjust
- // the index of the parameter once it's done.
- auto *NewParam =
- cast<TemplateParmDecl>(SemaRef.SubstDecl(OldParam, DC, Args));
- assert(NewParam->getDepth() == OldParam->getDepth() - 1 &&
- "unexpected template param depth");
- NewParam->setPosition(NewParam->getPosition() + Depth1IndexAdjustment);
- return NewParam;
+ return clang::transformTemplateParam(
+ SemaRef, DC, TTP, Args, Depth1IndexAdjustment + TTP->getIndex());
+ auto *NTTP = cast<NonTypeTemplateParmDecl>(TemplateParam);
+ return clang::transformTemplateParam(
+ SemaRef, DC, NTTP, Args, Depth1IndexAdjustment + NTTP->getIndex());
}
QualType transformFunctionProtoType(
@@ -2510,11 +2479,12 @@ struct ConvertConstructorToDeductionGuideTransform {
// -- The types of the function parameters are those of the constructor.
for (auto *OldParam : TL.getParams()) {
- ParmVarDecl *NewParam =
- transformFunctionTypeParam(OldParam, Args, MaterializedTypedefs);
+ ParmVarDecl *NewParam = clang::transformFunctionTypeParam(
+ SemaRef, OldParam, DC, Args, MaterializedTypedefs);
if (NestedPattern && NewParam)
- NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs,
- MaterializedTypedefs);
+ NewParam = clang::transformFunctionTypeParam(SemaRef, NewParam, DC,
+ OuterInstantiationArgs,
+ MaterializedTypedefs);
if (!NewParam)
return QualType();
ParamTypes.push_back(NewParam->getType());
@@ -2555,98 +2525,6 @@ struct ConvertConstructorToDeductionGuideTransform {
return Result;
}
-
- ParmVarDecl *transformFunctionTypeParam(
- ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args,
- llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
- TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
- TypeSourceInfo *NewDI;
- if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
- // Expand out the one and only element in each inner pack.
- Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, 0);
- NewDI =
- SemaRef.SubstType(PackTL.getPatternLoc(), Args,
- OldParam->getLocation(), OldParam->getDeclName());
- if (!NewDI) return nullptr;
- NewDI =
- SemaRef.CheckPackExpansion(NewDI, PackTL.getEllipsisLoc(),
- PackTL.getTypePtr()->getNumExpansions());
- } else
- NewDI = SemaRef.SubstType(OldDI, Args, OldParam->getLocation(),
- OldParam->getDeclName());
- if (!NewDI)
- return nullptr;
-
- // Extract the type. This (for instance) replaces references to typedef
- // members of the current instantiations with the definitions of those
- // typedefs, avoiding triggering instantiation of the deduced type during
- // deduction.
- NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs)
- .transform(NewDI);
-
- // Resolving a wording defect, we also inherit default arguments from the
- // constructor.
- ExprResult NewDefArg;
- if (OldParam->hasDefaultArg()) {
- // We don't care what the value is (we won't use it); just create a
- // placeholder to indicate there is a default argument.
- QualType ParamTy = NewDI->getType();
- NewDefArg = new (SemaRef.Context)
- OpaqueValueExpr(OldParam->getDefaultArg()->getBeginLoc(),
- ParamTy.getNonLValueExprType(SemaRef.Context),
- ParamTy->isLValueReferenceType() ? VK_LValue
- : ParamTy->isRValueReferenceType() ? VK_XValue
- : VK_PRValue);
- }
- // Handle arrays and functions decay.
- auto NewType = NewDI->getType();
- if (NewType->isArrayType() || NewType->isFunctionType())
- NewType = SemaRef.Context.getDecayedType(NewType);
-
- ParmVarDecl *NewParam = ParmVarDecl::Create(
- SemaRef.Context, DC, OldParam->getInnerLocStart(),
- OldParam->getLocation(), OldParam->getIdentifier(), NewType, NewDI,
- OldParam->getStorageClass(), NewDefArg.get());
- NewParam->setScopeInfo(OldParam->getFunctionScopeDepth(),
- OldParam->getFunctionScopeIndex());
- SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, NewParam);
- return NewParam;
- }
-
- FunctionTemplateDecl *buildDeductionGuide(
- TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor,
- ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart,
- SourceLocation Loc, SourceLocation LocEnd,
- llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {}) {
- DeclarationNameInfo Name(DeductionGuideName, Loc);
- ArrayRef<ParmVarDecl *> Params =
- TInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams();
-
- // Build the implicit deduction guide template.
- auto *Guide =
- CXXDeductionGuideDecl::Create(SemaRef.Context, DC, LocStart, ES, Name,
- TInfo->getType(), TInfo, LocEnd, Ctor);
- Guide->setImplicit();
- Guide->setParams(Params);
-
- for (auto *Param : Params)
- Param->setDeclContext(Guide);
- for (auto *TD : MaterializedTypedefs)
- TD->setDeclContext(Guide);
-
- auto *GuideTemplate = FunctionTemplateDecl::Create(
- SemaRef.Context, DC, Loc, DeductionGuideName, TemplateParams, Guide);
- GuideTemplate->setImplicit();
- Guide->setDescribedFunctionTemplate(GuideTemplate);
-
- if (isa<CXXRecordDecl>(DC)) {
- Guide->setAccess(AS_public);
- GuideTemplate->setAccess(AS_public);
- }
-
- DC->addDecl(GuideTemplate);
- return GuideTemplate;
- }
};
}
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 47cc22310c4eec..4024d3708ab311 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2531,6 +2531,15 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
return TemplateDeductionResult::Success;
}
+Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
+ TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps,
+ ArrayRef<TemplateArgument> As, sema::TemplateDeductionInfo &Info,
+ SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+ bool NumberOfArgumentsMustMatch) {
+ return ::DeduceTemplateArguments(*this, TemplateParams, Ps, As, Info, Deduced,
+ NumberOfArgumentsMustMatch);
+}
+
/// Determine whether two template arguments are the same.
static bool isSameTemplateArg(ASTContext &Context,
TemplateArgument X,
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 371378485626c2..32b5f3756307bf 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -21,6 +21,7 @@
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/PrettyDeclStackTrace.h"
#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Stack.h"
@@ -547,9 +548,9 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
: InstantiatingTemplate(SemaRef, Kind, PointOfInstantiation,
InstantiationRange, FunctionTemplate, nullptr,
TemplateArgs, &DeductionInfo) {
- assert(
- Kind == CodeSynthesisContext::ExplicitTemplateArgumentSubstitution ||
- Kind == CodeSynthesisContext::DeducedTemplateArgumentSubstitution);
+ assert(Kind == CodeSynthesisContext::ExplicitTemplateArgumentSubstitution ||
+ Kind == CodeSynthesisContext::DeducedTemplateArgumentSubstitution ||
+ Kind == CodeSynthesisContext::BuildingDeductionGuides);
}
Sema::InstantiatingTemplate::InstantiatingTemplate(
@@ -1446,6 +1447,24 @@ namespace {
return inherited::TransformFunctionProtoType(TLB, TL);
}
+ QualType TransformInjectedClassNameType(TypeLocBuilder &TLB,
+ InjectedClassNameTypeLoc TL) {
+ auto Type = inherited::TransformInjectedClassNameType(TLB, TL);
+ if (Type.isNull() &&
+ SemaRef.CodeSynthesisContexts.back().Kind ==
+ Sema::CodeSynthesisContext::BuildingDeductionGuides) {
+ // Return a TemplateSpecializationType for transforming a deduction
+ // guide.
+ if (auto *ICT = TL.getType()->getAs<InjectedClassNameType>()) {
+ auto TST = SemaRef.Context.getTemplateSpecializationType(
+ ICT->getTemplateName(), TemplateArgs.getOutermost());
+ TLB.pushTrivial(SemaRef.Context, TST, TL.getNameLoc());
+ return TST;
+ }
+ }
+ return Type;
+ }
+
template<typename Fn>
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
FunctionProtoTypeLoc TL,
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 9c696e072ba4a7..5e17abb92941e1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4853,16 +4853,13 @@ bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New,
///
/// Usually this should not be used, and template argument deduction should be
/// used in its place.
-FunctionDecl *
-Sema::InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD,
- const TemplateArgumentList *Args,
- SourceLocation Loc) {
+FunctionDecl *Sema::InstantiateFunctionDeclaration(
+ FunctionTemplateDecl *FTD, const TemplateArgumentList *Args,
+ SourceLocation Loc, CodeSynthesisContext::SynthesisKind CSC) {
FunctionDecl *FD = FTD->getTemplatedDecl();
sema::TemplateDeductionInfo Info(Loc);
- InstantiatingTemplate Inst(
- *this, Loc, FTD, Args->asArray(),
- CodeSynthesisContext::ExplicitTemplateArgumentSubstitution, Info);
+ InstantiatingTemplate Inst(*this, Loc, FTD, Args->asArray(), CSC, Info);
if (Inst.isInvalid())
return nullptr;
@@ -6286,8 +6283,18 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
QualType T = CheckTemplateIdType(TemplateName(TD), Loc, Args);
if (T.isNull())
return nullptr;
- auto *SubstRecord = T->getAsCXXRecordDecl();
- assert(SubstRecord && "class template id not a class type?");
+ CXXRecordDecl *SubstRecord = T->getAsCXXRecordDecl();
+
+ if (!SubstRecord) {
+ // The template id T is a TemplateSpecializationType when performing
+ // a substitution for a deduction guide,
+ assert(CodeSynthesisContexts.back().Kind ==
+ CodeSynthesisContext::BuildingDeductionGuides);
+ // Return a nullptr as a sentinel value, we handle it properly in
+ // the TemplateInstantiator::TransformInjectedClassNameType
+ // override.
+ return nullptr;
+ }
// Check that this template-id names the primary template and not a
// partial or explicit specialization. (In the latter cases, it's
// meaningless to attempt to find an instantiation of D within the
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
new file mode 100644
index 00000000000000..5a5691e48fd17e
--- /dev/null
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -0,0 +1,133 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-c++11-narrowing -Wno-literal-conversion -std=c++20 -verify %s
+
+namespace test1 {
+template <typename T>
+struct Foo {
+ T t;
+};
+template <typename U>
+using Bar = Foo<U>;
+
+Bar s = {1};
+} // namespace test1
+
+namespace test2 {
+template <typename X, typename Y>
+struct XYpair {
+ X x;
+ Y y;
+};
+// A tricky explicit deduction guide that swapping X and Y.
+template <typename X, typename Y>
+XYpair(X, Y) -> XYpair<Y, X>;
+template <typename U, typename V>
+using AliasXYpair = XYpair<U, V>;
+
+AliasXYpair xy = {1.1, 2}; // XYpair<int, double>
+static_assert(__is_same(decltype(xy.x), int));
+static_assert(__is_same(decltype(xy.y), double));
+} // namespace test2
+
+namespace test3 {
+template <typename T, class>
+struct container {
+ // test with default arguments.
+ container(T a, T b = T());
+};
+
+template <class T>
+using vector = container<T, int>;
+vector v(0, 0);
+} // namespace test3
+
+namespace test4 {
+template <class T>
+struct X {
+ T t;
+ X(T);
+};
+
+template <class T>
+X(T) -> X<double>;
+
+template <class T>
+using AX = X<T>;
+
+AX s = {1};
+static_assert(__is_same(decltype(s.t), double));
+} // namespace test4
+
+namespace test5 {
+template <int B>
+struct Foo {};
+
+template <int... C>
+using AF = Foo<1>;
+auto a = AF{};
+} // namespace test5
+
+namespace test6 {
+template <typename T, bool B = false>
+struct Foo {
+ Foo(T);
+};
+// non-type template argument.
+template <typename T>
+using AF = Foo<T, 1>;
+
+AF b{0}; //
+} // namespace test6
+
+namespace test7 {
+template <typename T>
+struct Foo {
+ Foo(T);
+};
+
+template <typename U>
+using AF1 = Foo<U>;
+template <typename K>
+using AF2 = AF1<K>; // expected-note {{template is declared here}}
+// FIXME: support this case.
+AF2 b = 1; // expected-error {{alias template 'AF2' requires template arguments; argument deduction only allowed for class templates}}
+} // namespace test7
+
+namespace test8 {
+template <typename T, int N>
+struct Foo {
+ Foo(T const (&)[N]);
+};
+
+template <typename X, int Y>
+using Bar = Foo<X, Y>;
+
+Bar s = {{1}};
+} // namespace test8
+
+namespace test9 {
+template <typename T, int N>
+struct Foo {
+ Foo(T const (&)[N]);
+};
+
+template <typename X, int Y>
+using Bar = Foo<X, sizeof(X)>;
+
+// FIXME: should we reject this case? GCC rejects it, MSVC accepts it.
+Bar s = {{1}};
+} // namespace test9
+
+namespace test10 {
+template <typename T>
+struct Foo {
+ template <typename U>
+ Foo(U);
+};
+
+template <typename U>
+Foo(U) -> Foo<U*>;
+
+template <typename K>
+using A = Foo<K>;
+A a(2); // Foo<int*>
+} // namespace test10
>From 05740ec2add59e99d159ec250d8959e7039c48a9 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 6 Feb 2024 09:40:51 +0100
Subject: [PATCH 2/6] Rewrite the implementations:
- Move the implementation to SemaTemplate.cpp where the typical CTAD
implementation lives, thus no sepatate CTAD.h/.cpp;
- Rewrite the implementation, we leverage more on clang's template
instantatiation mechanism to build the deduction guide, it
implementation is simplier;
- Some enhancements on TreeTransform and TemplateInstantiator to allow
running on BuildingDeductionGuides mode;
- Added more tests;
---
clang/include/clang/Sema/Sema.h | 3 +
clang/lib/Sema/CMakeLists.txt | 1 -
clang/lib/Sema/CTAD.cpp | 209 --------
clang/lib/Sema/CTAD.h | 65 ---
clang/lib/Sema/SemaInit.cpp | 372 +------------
clang/lib/Sema/SemaTemplate.cpp | 493 +++++++++++++++++-
clang/lib/Sema/SemaTemplateDeduction.cpp | 2 +-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 51 +-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 9 +-
clang/lib/Sema/TreeTransform.h | 8 +
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 79 ++-
11 files changed, 622 insertions(+), 670 deletions(-)
delete mode 100644 clang/lib/Sema/CTAD.cpp
delete mode 100644 clang/lib/Sema/CTAD.h
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 6ad80e6f4b1b05..e3f350ac8bfa92 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10424,6 +10424,9 @@ class Sema final {
const MultiLevelTemplateArgumentList &TemplateArgs,
bool EvaluateConstraints = true);
+ bool SubstTemplateArgument(const TemplateArgumentLoc &Input,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ TemplateArgumentLoc &Output);
bool
SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
const MultiLevelTemplateArgumentList &TemplateArgs,
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index ef8d2aed07d112..862f9d4ffb825d 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -14,7 +14,6 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins
add_clang_library(clangSema
AnalysisBasedWarnings.cpp
- CTAD.cpp
CodeCompleteConsumer.cpp
DeclSpec.cpp
DelayedDiagnostic.cpp
diff --git a/clang/lib/Sema/CTAD.cpp b/clang/lib/Sema/CTAD.cpp
deleted file mode 100644
index 745878c717fc4c..00000000000000
--- a/clang/lib/Sema/CTAD.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-//===--- CTAD.cpp - -------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "CTAD.h"
-#include "TreeTransform.h"
-#include "TypeLocBuilder.h"
-#include "clang/AST/ASTConsumer.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/ASTMutationListener.h"
-#include "clang/AST/ASTStructuralEquivalence.h"
-#include "clang/AST/CXXInheritance.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclObjC.h"
-#include "clang/AST/DeclTemplate.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/Type.h"
-#include "clang/AST/TypeLoc.h"
-#include "clang/Basic/SourceLocation.h"
-#include "clang/Basic/Specifiers.h"
-#include "clang/Sema/DeclSpec.h"
-#include "clang/Sema/ScopeInfo.h"
-#include "clang/Sema/Template.h"
-#include "llvm/ADT/ArrayRef.h"
-#include <optional>
-
-namespace clang {
-
-namespace {
-/// Tree transform to "extract" a transformed type from a class template's
-/// constructor to a deduction guide.
-class ExtractTypeForDeductionGuide
- : public TreeTransform<ExtractTypeForDeductionGuide> {
- llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs;
-
-public:
- typedef TreeTransform<ExtractTypeForDeductionGuide> Base;
- ExtractTypeForDeductionGuide(
- Sema &SemaRef,
- llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs)
- : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs) {}
-
- TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); }
-
- QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) {
- ASTContext &Context = SemaRef.getASTContext();
- TypedefNameDecl *OrigDecl = TL.getTypedefNameDecl();
- TypedefNameDecl *Decl = OrigDecl;
- // Transform the underlying type of the typedef and clone the Decl only if
- // the typedef has a dependent context.
- if (OrigDecl->getDeclContext()->isDependentContext()) {
- TypeLocBuilder InnerTLB;
- QualType Transformed =
- TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc());
- TypeSourceInfo *TSI = InnerTLB.getTypeSourceInfo(Context, Transformed);
- if (isa<TypeAliasDecl>(OrigDecl))
- Decl = TypeAliasDecl::Create(
- Context, Context.getTranslationUnitDecl(), OrigDecl->getBeginLoc(),
- OrigDecl->getLocation(), OrigDecl->getIdentifier(), TSI);
- else {
- assert(isa<TypedefDecl>(OrigDecl) && "Not a Type alias or typedef");
- Decl = TypedefDecl::Create(
- Context, Context.getTranslationUnitDecl(), OrigDecl->getBeginLoc(),
- OrigDecl->getLocation(), OrigDecl->getIdentifier(), TSI);
- }
- MaterializedTypedefs.push_back(Decl);
- }
-
- QualType TDTy = Context.getTypedefType(Decl);
- TypedefTypeLoc TypedefTL = TLB.push<TypedefTypeLoc>(TDTy);
- TypedefTL.setNameLoc(TL.getNameLoc());
-
- return TDTy;
- }
-};
-} // namespace
-
-ParmVarDecl *transformFunctionTypeParam(
- Sema &SemaRef, ParmVarDecl *OldParam, DeclContext *DC,
- MultiLevelTemplateArgumentList &Args,
- llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
- TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
- TypeSourceInfo *NewDI;
- if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
- // Expand out the one and only element in each inner pack.
- Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, 0);
- NewDI = SemaRef.SubstType(PackTL.getPatternLoc(), Args,
- OldParam->getLocation(), OldParam->getDeclName());
- if (!NewDI)
- return nullptr;
- NewDI = SemaRef.CheckPackExpansion(NewDI, PackTL.getEllipsisLoc(),
- PackTL.getTypePtr()->getNumExpansions());
- } else
- NewDI = SemaRef.SubstType(OldDI, Args, OldParam->getLocation(),
- OldParam->getDeclName());
- if (!NewDI)
- return nullptr;
-
- // Extract the type. This (for instance) replaces references to typedef
- // members of the current instantiations with the definitions of those
- // typedefs, avoiding triggering instantiation of the deduced type during
- // deduction.
- NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs)
- .transform(NewDI);
-
- // Resolving a wording defect, we also inherit default arguments from the
- // constructor.
- ExprResult NewDefArg;
- if (OldParam->hasDefaultArg()) {
- // We don't care what the value is (we won't use it); just create a
- // placeholder to indicate there is a default argument.
- QualType ParamTy = NewDI->getType();
- NewDefArg = new (SemaRef.Context)
- OpaqueValueExpr(OldParam->getDefaultArgRange().getBegin(),
- ParamTy.getNonLValueExprType(SemaRef.Context),
- ParamTy->isLValueReferenceType() ? VK_LValue
- : ParamTy->isRValueReferenceType() ? VK_XValue
- : VK_PRValue);
- }
- // Handle arrays and functions decay.
- auto NewType = NewDI->getType();
- if (NewType->isArrayType() || NewType->isFunctionType())
- NewType = SemaRef.Context.getDecayedType(NewType);
-
- ParmVarDecl *NewParam = ParmVarDecl::Create(
- SemaRef.Context, DC, OldParam->getInnerLocStart(),
- OldParam->getLocation(), OldParam->getIdentifier(), NewType, NewDI,
- OldParam->getStorageClass(), NewDefArg.get());
- NewParam->setScopeInfo(OldParam->getFunctionScopeDepth(),
- OldParam->getFunctionScopeIndex());
- SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, NewParam);
- return NewParam;
-}
-
-TemplateTypeParmDecl *
-transformTemplateTypeParam(Sema &SemaRef, DeclContext *DC,
- TemplateTypeParmDecl *TTP,
- MultiLevelTemplateArgumentList &Args,
- unsigned NewDepth, unsigned NewIndex) {
- // TemplateTypeParmDecl's index cannot be changed after creation, so
- // substitute it directly.
- auto *NewTTP = TemplateTypeParmDecl::Create(
- SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(), NewDepth,
- NewIndex, TTP->getIdentifier(), TTP->wasDeclaredWithTypename(),
- TTP->isParameterPack(), TTP->hasTypeConstraint(),
- TTP->isExpandedParameterPack()
- ? std::optional<unsigned>(TTP->getNumExpansionParameters())
- : std::nullopt);
- if (const auto *TC = TTP->getTypeConstraint())
- SemaRef.SubstTypeConstraint(NewTTP, TC, Args,
- /*EvaluateConstraint*/ true);
- if (TTP->hasDefaultArgument()) {
- TypeSourceInfo *InstantiatedDefaultArg =
- SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
- TTP->getDefaultArgumentLoc(), TTP->getDeclName());
- if (InstantiatedDefaultArg)
- NewTTP->setDefaultArgument(InstantiatedDefaultArg);
- }
- SemaRef.CurrentInstantiationScope->InstantiatedLocal(TTP, NewTTP);
- return NewTTP;
-}
-
-FunctionTemplateDecl *
-buildDeductionGuide(Sema &SemaRef, TemplateDecl *OriginalTemplate,
- TemplateParameterList *TemplateParams,
- CXXConstructorDecl *Ctor, ExplicitSpecifier ES,
- TypeSourceInfo *TInfo, SourceLocation LocStart,
- SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
- llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs) {
- DeclContext *DC = OriginalTemplate->getDeclContext();
- auto DeductionGuideName =
- SemaRef.Context.DeclarationNames.getCXXDeductionGuideName(
- OriginalTemplate);
-
- DeclarationNameInfo Name(DeductionGuideName, Loc);
- ArrayRef<ParmVarDecl *> Params =
- TInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams();
-
- // Build the implicit deduction guide template.
- auto *Guide =
- CXXDeductionGuideDecl::Create(SemaRef.Context, DC, LocStart, ES, Name,
- TInfo->getType(), TInfo, LocEnd, Ctor);
- Guide->setImplicit(IsImplicit);
- Guide->setParams(Params);
-
- for (auto *Param : Params)
- Param->setDeclContext(Guide);
- for (auto *TD : MaterializedTypedefs)
- TD->setDeclContext(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);
- GuideTemplate->setAccess(AS_public);
- }
-
- DC->addDecl(GuideTemplate);
- return GuideTemplate;
-}
-
-} // namespace clang
diff --git a/clang/lib/Sema/CTAD.h b/clang/lib/Sema/CTAD.h
deleted file mode 100644
index 88110230318f81..00000000000000
--- a/clang/lib/Sema/CTAD.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//===--- CTAD.h - Helper functions for CTAD -------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines helper functions for the class template argument deduction
-// (CTAD) implementation.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/ASTMutationListener.h"
-#include "clang/AST/ASTStructuralEquivalence.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclTemplate.h"
-#include "clang/AST/Type.h"
-#include "clang/Basic/SourceLocation.h"
-#include "clang/Sema/DeclSpec.h"
-#include "clang/Sema/ScopeInfo.h"
-#include "clang/Sema/Template.h"
-#include "llvm/ADT/ArrayRef.h"
-
-namespace clang {
-
-// Transform a given function parameter decl into a deduction guide parameter
-// decl.
-ParmVarDecl *transformFunctionTypeParam(
- Sema &SemaRef, ParmVarDecl *OldParam, DeclContext *DC,
- MultiLevelTemplateArgumentList &Args,
- llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs);
-
-// Transform a given template type parameter into a deduction guide template
-// parameter, rebuilding any internal references to earlier parameters and
-// re-indexing as we go.
-TemplateTypeParmDecl *transformTemplateTypeParam(
- Sema &SemaRef, DeclContext *DC, TemplateTypeParmDecl *TPT,
- MultiLevelTemplateArgumentList &Args, unsigned NewDepth, unsigned NewIndex);
-// Similar to above, but for non-type template or template template parameters.
-template <typename NonTypeTemplateOrTemplateTemplateParmDecl>
-NonTypeTemplateOrTemplateTemplateParmDecl *
-transformTemplateParam(Sema &SemaRef, DeclContext *DC,
- NonTypeTemplateOrTemplateTemplateParmDecl *OldParam,
- MultiLevelTemplateArgumentList &Args,
- unsigned NewIndex) {
- // Ask the template instantiator to do the heavy lifting for us, then adjust
- // the index of the parameter once it's done.
- auto *NewParam = cast<NonTypeTemplateOrTemplateTemplateParmDecl>(
- SemaRef.SubstDecl(OldParam, DC, Args));
- NewParam->setPosition(NewIndex);
- return NewParam;
-}
-
-// Build a deduction guide with the specified parameter types.
-FunctionTemplateDecl *buildDeductionGuide(
- Sema &SemaRef, TemplateDecl *OriginalTemplate,
- TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor,
- ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart,
- SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
- llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {});
-
-} // namespace clang
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 812cb870180321..6d288d74761319 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10,19 +10,13 @@
//
//===----------------------------------------------------------------------===//
-#include "CTAD.h"
-#include "TypeLocBuilder.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/DeclAccessPair.h"
-#include "clang/AST/DeclBase.h"
-#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.h"
#include "clang/AST/IgnoreExpr.h"
-#include "clang/AST/RecursiveASTVisitor.h"
-#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceManager.h"
@@ -34,9 +28,7 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/SemaInternal.h"
-#include "clang/Sema/Template.h"
#include "llvm/ADT/APInt.h"
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallString.h"
@@ -10595,213 +10587,6 @@ static bool isOrIsDerivedFromSpecializationOf(CXXRecordDecl *RD,
return !(NotSpecialization(RD) && RD->forallBases(NotSpecialization));
}
-// Transform to form a corresponding deduction guide for type alias template
-// decl.
-//
-// This class implements the C++ [over.match.class.deduct]p3:
-// ... Let g denote the result of substituting these deductions into f. If
-// substitution succeeds, form a function or function template f' with the
-// following properties and add it to the set of guides of A...
-class AliasTemplateDeductionGuideTransform {
-public:
- AliasTemplateDeductionGuideTransform(Sema &S, TypeAliasTemplateDecl *Alias)
- : SemaRef(S), AliasTemplate(Alias), DC(Alias->getDeclContext()) {}
- // Returns the result of substituting the deduced template arguments into F.
- NamedDecl *transform(CXXDeductionGuideDecl *F,
- ArrayRef<TemplateArgument> DeducedArgs,
- ArrayRef<NamedDecl *> NonDeducedTemplateParamsInF) {
- // Template parameters of the f'.
- //
- // C++ [over.match.class.deduct]p3.2:
- // If f is a function template, f' is a function template whose template
- // parameter list consists of all the template parameters of A (including
- // their default template arguments) that appear in the above deductions
- // or (recursively) in their default template arguments
- SmallVector<NamedDecl *> TemplateParamsInFPrime =
- FindAppearedTemplateParamsInAlias(DeducedArgs);
- // ...followed by the template parameters of f that were not deduced
- // (including their default template arguments)
- TemplateParamsInFPrime.append(NonDeducedTemplateParamsInF.begin(),
- NonDeducedTemplateParamsInF.end());
-
- LocalInstantiationScope Scope(SemaRef);
- SmallVector<TemplateArgument, 16> Depth1Args;
- SmallVector<NamedDecl *, 16> AllParams;
- SmallVector<TemplateArgument, 16> SubstArgs;
- unsigned TemplateParamIndex = 0;
- TemplateParameterList *TemplateParams = nullptr;
-
- for (NamedDecl *Param : TemplateParamsInFPrime) {
- MultiLevelTemplateArgumentList Args;
-
- Args.setKind(TemplateSubstitutionKind::Rewrite);
- Args.addOuterTemplateArguments(Depth1Args);
- Args.addOuterRetainedLevel();
- NamedDecl *NewParam =
- transformTemplateParameter(Param, Args, TemplateParamIndex++);
- if (!NewParam) {
- llvm::errs() << "Faile to generate new param!\n";
- return nullptr;
- }
- auto NewArgumentForNewParam =
- SemaRef.Context.getCanonicalTemplateArgument(
- SemaRef.Context.getInjectedTemplateArg(NewParam));
- Depth1Args.push_back(NewArgumentForNewParam);
- AllParams.push_back(NewParam);
- SubstArgs.push_back(NewArgumentForNewParam);
- }
- // FIXME: substitute new template parameters into the requires-clause.
- TemplateParams = TemplateParameterList::Create(
- SemaRef.Context,
- AliasTemplate->getTemplateParameters()->getTemplateLoc(),
- AliasTemplate->getTemplateParameters()->getLAngleLoc(), AllParams,
- AliasTemplate->getTemplateParameters()->getRAngleLoc(),
- /*RequiresClause=*/nullptr);
-
- MultiLevelTemplateArgumentList Args;
- Args.setKind(TemplateSubstitutionKind::Rewrite);
- Args.addOuterTemplateArguments(SubstArgs);
- Args.addOuterRetainedLevel();
-
- FunctionProtoTypeLoc FPTL = F->getTypeSourceInfo()
- ->getTypeLoc()
- .getAsAdjusted<FunctionProtoTypeLoc>();
- assert(FPTL && "no prototype for underlying deduction guides");
-
- // Transform the type of the function, adjusting the return type and
- // replacing references to the old parameters with references to the
- // new ones.
- TypeLocBuilder TLB;
- SmallVector<ParmVarDecl *, 8> Params;
- SmallVector<TypedefNameDecl *, 4> MaterializedTypedefs;
- QualType NewType = transformFunctionProtoType(
- TLB, FPTL, Params, Args, F->getReturnType(), MaterializedTypedefs);
- if (NewType.isNull())
- return nullptr;
- TypeSourceInfo *NewTInfo = TLB.getTypeSourceInfo(SemaRef.Context, NewType);
-
- return clang::buildDeductionGuide(
- SemaRef, AliasTemplate, TemplateParams,
- F->getCorrespondingConstructor(), F->getExplicitSpecifier(), NewTInfo,
- AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(),
- AliasTemplate->getEndLoc(), F->isImplicit(), MaterializedTypedefs);
- }
-
-private:
- // Find all template parameters of the AliasTemplate that appear in the
- // DeducedArgs.
- SmallVector<NamedDecl *>
- FindAppearedTemplateParamsInAlias(ArrayRef<TemplateArgument> DeducedArgs) {
- struct FindAppearedTemplateParams
- : public RecursiveASTVisitor<FindAppearedTemplateParams> {
- llvm::DenseSet<NamedDecl *> TemplateParamsInAlias;
- llvm::DenseSet<const NamedDecl *> AppearedTemplateParams;
-
- FindAppearedTemplateParams(ArrayRef<NamedDecl *> TemplateParamsInAlias)
- : TemplateParamsInAlias(TemplateParamsInAlias.begin(),
- TemplateParamsInAlias.end()) {}
-
- bool VisitTemplateTypeParmType(TemplateTypeParmType *TTP) {
- MarkAppeared(TTP->getDecl());
- return true;
- }
- bool VisitDeclRefExpr(DeclRefExpr *DRE) {
- MarkAppeared(DRE->getFoundDecl());
- return true;
- }
-
- void MarkAppeared(NamedDecl *ND) {
- if (TemplateParamsInAlias.contains(ND))
- AppearedTemplateParams.insert(ND);
- }
- };
- ArrayRef<NamedDecl *> TemplateParamsInAlias =
- AliasTemplate->getTemplateParameters()->asArray();
- FindAppearedTemplateParams MarkAppeared(TemplateParamsInAlias);
- MarkAppeared.TraverseTemplateArguments(DeducedArgs);
-
- SmallVector<NamedDecl *> Results;
- for (auto *TP : TemplateParamsInAlias)
- if (MarkAppeared.AppearedTemplateParams.contains(TP))
- Results.push_back(TP);
- return Results;
- }
-
- // Transform a template parameter of f into a template parameter of f'
- // rebuilding any internal references to earlier parameters and renumbering as
- // we go.
- NamedDecl *transformTemplateParameter(NamedDecl *TemplateParam,
- MultiLevelTemplateArgumentList &Args,
- int NewIndex) {
- if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam))
- return clang::transformTemplateTypeParam(SemaRef, DC, TTP, Args,
- TTP->getDepth(), NewIndex);
-
- if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam))
- return clang::transformTemplateParam(SemaRef, DC, TTP, Args, NewIndex);
- auto *NTTP = cast<NonTypeTemplateParmDecl>(TemplateParam);
- return clang::transformTemplateParam(SemaRef, DC, NTTP, Args, NewIndex);
- }
-
- // Transform the function proto type of f into a function prototype of f'.
- QualType transformFunctionProtoType(
- TypeLocBuilder &TLB, FunctionProtoTypeLoc TL,
- SmallVectorImpl<ParmVarDecl *> &Params,
- MultiLevelTemplateArgumentList &Args, QualType ReturnType,
- SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
- SmallVector<QualType, 4> ParamTypes;
- const FunctionProtoType *T = TL.getTypePtr();
-
- for (auto *OldParam : TL.getParams()) {
- ParmVarDecl *NewParam = clang::transformFunctionTypeParam(
- SemaRef, OldParam, DC, Args, MaterializedTypedefs);
- if (!NewParam)
- return QualType();
- ParamTypes.push_back(NewParam->getType());
- Params.push_back(NewParam);
- }
-
- // The return type of the deduction guide f is InjectedClassNameType,
- // transform it to a TemplateSpecializationType.
- if (const auto *ET = ReturnType->getAs<InjectedClassNameType>())
- ReturnType = ET->getInjectedSpecializationType();
- auto DeductionGuideName =
- SemaRef.Context.DeclarationNames.getCXXDeductionGuideName(
- AliasTemplate);
- ReturnType = SemaRef.SubstType(ReturnType, Args, SourceLocation(),
- DeductionGuideName);
-
- // Resolving a wording defect, we also inherit the variadicness of the
- // constructor.
- FunctionProtoType::ExtProtoInfo EPI;
- EPI.Variadic = T->isVariadic();
- EPI.HasTrailingReturn = true;
-
- QualType FunctionTy = SemaRef.BuildFunctionType(
- ReturnType, ParamTypes, TL.getBeginLoc(), DeductionGuideName, EPI);
- if (FunctionTy.isNull())
- return QualType();
- assert(FunctionTy->getTypeClass() == Type::FunctionProto);
- // Pushes spaces for the new FunctionProtoTypeLoc.
- TLB.pushTrivial(SemaRef.Context,
- TypeLoc(FunctionTy, nullptr).getNextTypeLoc().getType(),
- SourceLocation());
- FunctionProtoTypeLoc TargetTL = TLB.push<FunctionProtoTypeLoc>(FunctionTy);
- TargetTL.setLocalRangeBegin(TL.getLocalRangeBegin());
- TargetTL.setLParenLoc(TL.getLParenLoc());
- TargetTL.setRParenLoc(TL.getRParenLoc());
- TargetTL.setExceptionSpecRange(SourceRange());
- TargetTL.setLocalRangeEnd(TL.getLocalRangeEnd());
- for (unsigned I = 0, E = TargetTL.getNumParams(); I != E; ++I)
- TargetTL.setParam(I, Params[I]);
- return FunctionTy;
- }
-
- Sema &SemaRef;
- TypeAliasTemplateDecl *AliasTemplate = nullptr;
- DeclContext *DC = nullptr;
-};
-
QualType Sema::DeduceTemplateSpecializationFromInitializer(
TypeSourceInfo *TSInfo, const InitializedEntity &Entity,
const InitializationKind &Kind, MultiExprArg Inits) {
@@ -10816,40 +10601,26 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// We can only perform deduction for class templates or alias templates.
auto *Template =
dyn_cast_or_null<ClassTemplateDecl>(TemplateName.getAsTemplateDecl());
-
- TypeAliasTemplateDecl *AliasTemplate = nullptr;
- llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs;
+ TemplateDecl* LookupTemplateDecl = Template;
if (!Template && getLangOpts().CPlusPlus20) { // type alias template
- if (AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>(
+ if (auto *AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>(
TemplateName.getAsTemplateDecl());
AliasTemplate) {
- // Unrap the sugar ElaboratedType.
- auto RhsType = AliasTemplate->getTemplatedDecl()
- ->getUnderlyingType()
- .getSingleStepDesugaredType(Context);
- if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) {
- // TemplateName in TEST can be a TypeAliasTemplateDecl if
- // the right hand side of the alias is also a type alias, e.g.
- //
- // template<typename T>
- // using AliasFoo1 = Foo<T>; // Foo<T> is a class template
- // specialization
- //
- // template<typename T>
- // using AliasFoo2 = AliasFoo1<T>; // AliasFoo1<T> is a type alias
- // FIXME: support this case, we need to recursively perform deductions.
+ LookupTemplateDecl = AliasTemplate;
+ auto UnderlyingType = AliasTemplate->getTemplatedDecl()
+ ->getUnderlyingType()
+ .getDesugaredType(Context);
+ if (const auto *TST =
+ UnderlyingType->getAs<TemplateSpecializationType>()) {
Template = dyn_cast_or_null<ClassTemplateDecl>(
TST->getTemplateName().getAsTemplateDecl());
- AliasRhsTemplateArgs = TST->template_arguments();
- } else if (const auto *RT = RhsType->getAs<RecordType>()) {
+ } else if (const auto *RT = UnderlyingType->getAs<RecordType>()) {
// Cases where template arguments in the RHS of the alias are not
// dependent. e.g.
// using AliasFoo = Foo<bool>;
if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(
- RT->getAsCXXRecordDecl())) {
+ RT->getAsCXXRecordDecl()))
Template = CTSD->getSpecializedTemplate();
- AliasRhsTemplateArgs = CTSD->getTemplateArgs().asArray();
- }
}
}
}
@@ -10884,122 +10655,15 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// template-name, a function template [...]
// - For each deduction-guide, a function or function template [...]
DeclarationNameInfo NameInfo(
- Context.DeclarationNames.getCXXDeductionGuideName(Template),
+ Context.DeclarationNames.getCXXDeductionGuideName(LookupTemplateDecl),
TSInfo->getTypeLoc().getEndLoc());
LookupResult Guides(*this, NameInfo, LookupOrdinaryName);
- LookupQualifiedName(Guides, Template->getDeclContext());
+ LookupQualifiedName(Guides, LookupTemplateDecl->getDeclContext());
// FIXME: Do not diagnose inaccessible deduction guides. The standard isn't
// clear on this, but they're not found by name so access does not apply.
Guides.suppressDiagnostics();
- SmallVector<DeclAccessPair> GuidesCandidates;
- if (AliasTemplate) {
- for (auto *G : Guides) {
- FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(G);
- if (!F)
- // FIXME: handle the non-template deduction guide case.
- continue;
- auto RType = F->getTemplatedDecl()->getReturnType();
- // The (trailing) return type of the deduction guide.
- const TemplateSpecializationType *FReturnType = nullptr;
- if (const auto *InjectedCNT = RType->getAs<InjectedClassNameType>()) {
- // implicitly-generated deduction guide.
- FReturnType = InjectedCNT->getInjectedTST();
- } else if (const auto *ET = RType->getAs<ElaboratedType>()) {
- // explicit deduction guide.
- FReturnType = ET->getNamedType()->getAs<TemplateSpecializationType>();
- }
- assert(FReturnType);
- if (FReturnType) {
- sema::TemplateDeductionInfo TDeduceInfo(Kind.getLocation());
- SmallVector<DeducedTemplateArgument> DeduceResults;
- // DeduceResults.resize(FReturnType->template_arguments().size());
- DeduceResults.resize(F->getTemplateParameters()->size());
-
- // Deduce template arguments of the deduction guide f from the RHS of
- // the alias.
- //
- // C++ [over.match.class.deduct]p3: ...For each function or function
- // template f in the guides of the template named by the
- // simple-template-id of the defining-type-id, the template arguments
- // of the return type of f are deduced from the defining-type-id of A
- // according to the process in [temp.deduct.type] with the exception
- // that deduction does not fail if not all template arguments are
- // deduced.
- //
- //
- // template<typename X, typename Y>
- // f(X, Y) -> f<Y, X>;
- //
- // template<typename U>
- // using alias = f<int, U>;
- //
- // The RHS of alias is f<int, U>, we deduced the template arguments of
- // the return type of the deduction guide from it: Y->int, X->U
- //
- // FIXME: DeduceTemplateArguments stops immediately at the first
- // non-deduced template parameter, extend it to continue performing
- // deduction for rest of parameters.
- DeduceTemplateArguments(
- F->getTemplateParameters(), FReturnType->template_arguments(),
- AliasRhsTemplateArgs, TDeduceInfo, DeduceResults,
- /*NumberOfArgumentsMustMatch=*/false);
-
- SmallVector<TemplateArgument> DeducedArgs;
- SmallVector<NamedDecl *> NonDeducedTemplateParamsInF;
- // !!NOTE: DeduceResults respects the sequence of template parameters.
- for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
- const auto &D = DeduceResults[Index];
- if (!D.isNull()) // Deduced
- DeducedArgs.push_back(D);
- else
- NonDeducedTemplateParamsInF.push_back(
- F->getTemplateParameters()->getParam(Index));
- }
- auto *DeducedArgList =
- TemplateArgumentList::CreateCopy(this->Context, DeducedArgs);
-
- // Let g denote the result of substituting these deductions into f.
- //
- // FIXME: is using the InstantiateFunctionDeclaration API a right
- // implement choice? It has some side effects which creates a
- // specialization for the deduction guide function template, and
- // the specialization is added to the the FunctionTemplateDecl, this
- // is not specified by the standard.
- //
- // FIXME: Cache the result.
- if (auto *G = InstantiateFunctionDeclaration(
- F, DeducedArgList, AliasTemplate->getLocation(),
- Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
- InstantiatingTemplate BuildingDeductionGuides(
- *this, AliasTemplate->getLocation(), AliasTemplate,
- Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{});
- AliasTemplateDeductionGuideTransform Transform(*this, AliasTemplate);
- // If substitution succeeds, form a function or function template
- // f' with the following properties and add it to the set of
- // guides of A:
- if (auto *FPrime = Transform.transform(
- dyn_cast<CXXDeductionGuideDecl>(G), DeducedArgs,
- NonDeducedTemplateParamsInF)) {
- // FIXME: implement the assoicated constraint per C++
- // [over.match.class.deduct]p3.3:
- // The associated constraints ([temp.constr.decl]) are the
- // conjunction of the associated constraints of g and a
- // constraint that is satisfied if and only if the arguments
- // of A are deducible (see below) from the return type.
- // This could be implemented as part of function overload
- // resolution below.
- GuidesCandidates.push_back(
- DeclAccessPair::make(FPrime, AccessSpecifier::AS_public));
- }
- }
- }
- }
- } else {
- for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I)
- GuidesCandidates.push_back(I.getPair());
- }
// Figure out if this is list-initialization.
InitListExpr *ListInit =
(Inits.size() == 1 && Kind.getKind() != InitializationKind::IK_Direct)
@@ -11157,8 +10821,9 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
HasAnyDeductionGuide = true;
}
};
- for (auto I : GuidesCandidates) {
- NamedDecl *D = (I)->getUnderlyingDecl();
+
+ for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) {
+ NamedDecl *D = (*I)->getUnderlyingDecl();
if (D->isInvalidDecl())
continue;
@@ -11171,7 +10836,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
if (!GD->isImplicit())
HasAnyDeductionGuide = true;
- addDeductionCandidate(TD, GD, I, OnlyListConstructors,
+ addDeductionCandidate(TD, GD, I.getPair(), OnlyListConstructors,
/*AllowAggregateDeductionCandidate=*/false);
}
@@ -11211,8 +10876,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// Try list constructors unless the list is empty and the class has one or
// more default constructors, in which case those constructors win.
if (!ListInit->getNumInits()) {
- for (auto D : GuidesCandidates) {
-
+ for (NamedDecl *D : Guides) {
auto *FD = dyn_cast<FunctionDecl>(D->getUnderlyingDecl());
if (FD && FD->getMinRequiredArguments() == 0) {
TryListConstructors = false;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c93563b2047b10..bbecb5e70804e0 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -8,7 +8,6 @@
// This file implements semantic analysis for C++ templates.
//===----------------------------------------------------------------------===//
-#include "CTAD.h"
#include "TreeTransform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
@@ -41,6 +40,7 @@
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
#include <iterator>
#include <optional>
@@ -2260,6 +2260,94 @@ class ExtractTypeForDeductionGuide
}
};
+// Build a deduction guide with the specified parameter types.
+FunctionTemplateDecl *
+buildDeductionGuide(Sema &SemaRef, TemplateDecl *OriginalTemplate,
+ TemplateParameterList *TemplateParams,
+ CXXConstructorDecl *Ctor, ExplicitSpecifier ES,
+ TypeSourceInfo *TInfo, SourceLocation LocStart,
+ SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
+ llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {}) {
+ DeclContext *DC = OriginalTemplate->getDeclContext();
+ auto DeductionGuideName =
+ SemaRef.Context.DeclarationNames.getCXXDeductionGuideName(
+ OriginalTemplate);
+
+ DeclarationNameInfo Name(DeductionGuideName, Loc);
+ ArrayRef<ParmVarDecl *> Params =
+ TInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams();
+
+ // Build the implicit deduction guide template.
+ auto *Guide =
+ CXXDeductionGuideDecl::Create(SemaRef.Context, DC, LocStart, ES, Name,
+ TInfo->getType(), TInfo, LocEnd, Ctor);
+ Guide->setImplicit(IsImplicit);
+ Guide->setParams(Params);
+
+ for (auto *Param : Params)
+ Param->setDeclContext(Guide);
+ for (auto *TD : MaterializedTypedefs)
+ TD->setDeclContext(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);
+ GuideTemplate->setAccess(AS_public);
+ }
+
+ DC->addDecl(GuideTemplate);
+ return GuideTemplate;
+}
+
+// Transform a given template type parameter `TTP`.
+TemplateTypeParmDecl *
+transformTemplateTypeParam(Sema &SemaRef, DeclContext *DC,
+ TemplateTypeParmDecl *TTP,
+ MultiLevelTemplateArgumentList &Args,
+ unsigned NewDepth, unsigned NewIndex) {
+ // TemplateTypeParmDecl's index cannot be changed after creation, so
+ // substitute it directly.
+ auto *NewTTP = TemplateTypeParmDecl::Create(
+ SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(), NewDepth,
+ NewIndex, TTP->getIdentifier(), TTP->wasDeclaredWithTypename(),
+ TTP->isParameterPack(), TTP->hasTypeConstraint(),
+ TTP->isExpandedParameterPack()
+ ? std::optional<unsigned>(TTP->getNumExpansionParameters())
+ : std::nullopt);
+ if (const auto *TC = TTP->getTypeConstraint())
+ SemaRef.SubstTypeConstraint(NewTTP, TC, Args,
+ /*EvaluateConstraint*/ true);
+ if (TTP->hasDefaultArgument()) {
+ TypeSourceInfo *InstantiatedDefaultArg =
+ SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
+ TTP->getDefaultArgumentLoc(), TTP->getDeclName());
+ if (InstantiatedDefaultArg)
+ NewTTP->setDefaultArgument(InstantiatedDefaultArg);
+ }
+ SemaRef.CurrentInstantiationScope->InstantiatedLocal(TTP, NewTTP);
+ return NewTTP;
+}
+// Similar to above, but for non-type template or template template parameters.
+template <typename NonTypeTemplateOrTemplateTemplateParmDecl>
+NonTypeTemplateOrTemplateTemplateParmDecl *
+transformTemplateParam(Sema &SemaRef, DeclContext *DC,
+ NonTypeTemplateOrTemplateTemplateParmDecl *OldParam,
+ MultiLevelTemplateArgumentList &Args,
+ unsigned NewIndex,
+ unsigned NewDepth) {
+ // Ask the template instantiator to do the heavy lifting for us, then adjust
+ // the index of the parameter once it's done.
+ auto *NewParam = cast<NonTypeTemplateOrTemplateTemplateParmDecl>(
+ SemaRef.SubstDecl(OldParam, DC, Args));
+ NewParam->setPosition(NewIndex);
+ NewParam->setDepth(NewDepth);
+ return NewParam;
+}
+
/// Transform to convert portions of a constructor declaration into the
/// corresponding deduction guide, per C++1z [over.match.class.deduct]p1.
struct ConvertConstructorToDeductionGuideTransform {
@@ -2408,7 +2496,7 @@ struct ConvertConstructorToDeductionGuideTransform {
return nullptr;
TypeSourceInfo *NewTInfo = TLB.getTypeSourceInfo(SemaRef.Context, NewType);
- return clang::buildDeductionGuide(
+ return buildDeductionGuide(
SemaRef, Template, TemplateParams, CD, CD->getExplicitSpecifier(),
NewTInfo, CD->getBeginLoc(), CD->getLocation(), CD->getEndLoc(),
/*IsImplicit=*/true, MaterializedTypedefs);
@@ -2446,7 +2534,7 @@ struct ConvertConstructorToDeductionGuideTransform {
Params.push_back(NewParam);
}
- return clang::buildDeductionGuide(
+ return buildDeductionGuide(
SemaRef, Template, GetTemplateParameterList(Template), nullptr,
ExplicitSpecifier(), TSI, Loc, Loc, Loc, /*IsImplicit=*/true);
}
@@ -2458,15 +2546,17 @@ struct ConvertConstructorToDeductionGuideTransform {
NamedDecl *transformTemplateParameter(NamedDecl *TemplateParam,
MultiLevelTemplateArgumentList &Args) {
if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam))
- return clang::transformTemplateTypeParam(
+ return transformTemplateTypeParam(
SemaRef, DC, TTP, Args, TTP->getDepth() - 1,
Depth1IndexAdjustment + TTP->getIndex());
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam))
- return clang::transformTemplateParam(
- SemaRef, DC, TTP, Args, Depth1IndexAdjustment + TTP->getIndex());
+ return transformTemplateParam(
+ SemaRef, DC, TTP, Args, Depth1IndexAdjustment + TTP->getIndex(),
+ TTP->getDepth() - 1);
auto *NTTP = cast<NonTypeTemplateParmDecl>(TemplateParam);
- return clang::transformTemplateParam(
- SemaRef, DC, NTTP, Args, Depth1IndexAdjustment + NTTP->getIndex());
+ return transformTemplateParam(
+ SemaRef, DC, NTTP, Args, Depth1IndexAdjustment + NTTP->getIndex(),
+ NTTP->getDepth() - 1);
}
QualType transformFunctionProtoType(
@@ -2479,12 +2569,11 @@ struct ConvertConstructorToDeductionGuideTransform {
// -- The types of the function parameters are those of the constructor.
for (auto *OldParam : TL.getParams()) {
- ParmVarDecl *NewParam = clang::transformFunctionTypeParam(
- SemaRef, OldParam, DC, Args, MaterializedTypedefs);
+ ParmVarDecl *NewParam =
+ transformFunctionTypeParam(OldParam, Args, MaterializedTypedefs);
if (NestedPattern && NewParam)
- NewParam = clang::transformFunctionTypeParam(SemaRef, NewParam, DC,
- OuterInstantiationArgs,
- MaterializedTypedefs);
+ NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs,
+ MaterializedTypedefs);
if (!NewParam)
return QualType();
ParamTypes.push_back(NewParam->getType());
@@ -2525,9 +2614,366 @@ struct ConvertConstructorToDeductionGuideTransform {
return Result;
}
+
+ ParmVarDecl *transformFunctionTypeParam(
+ ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args,
+ llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
+ TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
+ TypeSourceInfo *NewDI;
+ if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
+ // Expand out the one and only element in each inner pack.
+ Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, 0);
+ NewDI =
+ SemaRef.SubstType(PackTL.getPatternLoc(), Args,
+ OldParam->getLocation(), OldParam->getDeclName());
+ if (!NewDI) return nullptr;
+ NewDI =
+ SemaRef.CheckPackExpansion(NewDI, PackTL.getEllipsisLoc(),
+ PackTL.getTypePtr()->getNumExpansions());
+ } else
+ NewDI = SemaRef.SubstType(OldDI, Args, OldParam->getLocation(),
+ OldParam->getDeclName());
+ if (!NewDI)
+ return nullptr;
+
+ // Extract the type. This (for instance) replaces references to typedef
+ // members of the current instantiations with the definitions of those
+ // typedefs, avoiding triggering instantiation of the deduced type during
+ // deduction.
+ NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs)
+ .transform(NewDI);
+
+ // Resolving a wording defect, we also inherit default arguments from the
+ // constructor.
+ ExprResult NewDefArg;
+ if (OldParam->hasDefaultArg()) {
+ // We don't care what the value is (we won't use it); just create a
+ // placeholder to indicate there is a default argument.
+ QualType ParamTy = NewDI->getType();
+ NewDefArg = new (SemaRef.Context)
+ OpaqueValueExpr(OldParam->getDefaultArg()->getBeginLoc(),
+ ParamTy.getNonLValueExprType(SemaRef.Context),
+ ParamTy->isLValueReferenceType() ? VK_LValue
+ : ParamTy->isRValueReferenceType() ? VK_XValue
+ : VK_PRValue);
+ }
+ // Handle arrays and functions decay.
+ auto NewType = NewDI->getType();
+ if (NewType->isArrayType() || NewType->isFunctionType())
+ NewType = SemaRef.Context.getDecayedType(NewType);
+
+ ParmVarDecl *NewParam = ParmVarDecl::Create(
+ SemaRef.Context, DC, OldParam->getInnerLocStart(),
+ OldParam->getLocation(), OldParam->getIdentifier(), NewType, NewDI,
+ OldParam->getStorageClass(), NewDefArg.get());
+ NewParam->setScopeInfo(OldParam->getFunctionScopeDepth(),
+ OldParam->getFunctionScopeIndex());
+ SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, NewParam);
+ return NewParam;
+ }
};
+
+// Find all template parameters of the AliasTemplate that appear in the
+// given DeducedArgs.
+SmallVector<unsigned>
+FindAppearedTemplateParamsInAlias(ArrayRef<TemplateArgument> DeducedArgs,
+ TypeAliasTemplateDecl *AliasTemplate) {
+ struct FindAppearedTemplateParams
+ : public RecursiveASTVisitor<FindAppearedTemplateParams> {
+ llvm::DenseSet<NamedDecl *> TemplateParamsInAlias;
+ llvm::DenseSet<const NamedDecl *> AppearedTemplateParams;
+
+ FindAppearedTemplateParams(ArrayRef<NamedDecl *> TemplateParamsInAlias)
+ : TemplateParamsInAlias(TemplateParamsInAlias.begin(),
+ TemplateParamsInAlias.end()) {}
+
+ bool VisitTemplateTypeParmType(TemplateTypeParmType *TTP) {
+ TTP->getIndex();
+ MarkAppeared(TTP->getDecl());
+ return true;
+ }
+ bool VisitDeclRefExpr(DeclRefExpr *DRE) {
+ MarkAppeared(DRE->getFoundDecl());
+ return true;
+ }
+
+ void MarkAppeared(NamedDecl *ND) {
+ if (TemplateParamsInAlias.contains(ND))
+ AppearedTemplateParams.insert(ND);
+ }
+ };
+ ArrayRef<NamedDecl *> TemplateParamsInAlias =
+ AliasTemplate->getTemplateParameters()->asArray();
+ FindAppearedTemplateParams MarkAppeared(TemplateParamsInAlias);
+ MarkAppeared.TraverseTemplateArguments(DeducedArgs);
+
+ SmallVector<unsigned> Results;
+ for (unsigned Index = 0; Index < TemplateParamsInAlias.size(); ++Index) {
+ if (MarkAppeared.AppearedTemplateParams.contains(
+ TemplateParamsInAlias[Index]))
+ Results.push_back(Index);
+ }
+ return Results;
+}
+
+bool hasDeclaredDeductionGuides(DeclarationName Name, DeclContext* DC) {
+ // Check whether we've already declared deduction guides for this template.
+ // FIXME: Consider storing a flag on the template to indicate this.
+ auto Existing = DC->lookup(Name);
+ for (auto *D : Existing)
+ if (D->isImplicit())
+ return true;
+ return false;
+}
+
+// Build deduction guides for a type alias template.
+void DeclareImplicitDeductionGuidesForTypeAlias(
+ Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc) {
+ 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(
+ Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate),
+ AliasTemplate->getDeclContext()))
+ return;
+ // Unrap the sugar ElaboratedType.
+ auto RhsType = AliasTemplate->getTemplatedDecl()
+ ->getUnderlyingType()
+ .getSingleStepDesugaredType(Context);
+ TemplateDecl *Template = nullptr;
+ llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs;
+ if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) {
+ // TemplateName in TEST can be a TypeAliasTemplateDecl if
+ // the right hand side of the alias is also a type alias, e.g.
+ //
+ // template<typename T>
+ // using AliasFoo1 = Foo<T>; // Foo<T> is a class template
+ // specialization
+ //
+ // template<typename T>
+ // using AliasFoo2 = AliasFoo1<T>; // AliasFoo1<T> is a type alias
+ Template = TST->getTemplateName().getAsTemplateDecl();
+ AliasRhsTemplateArgs = TST->template_arguments();
+ } else if (const auto *RT = RhsType->getAs<RecordType>()) {
+ // Cases where template arguments in the RHS of the alias are not
+ // dependent. e.g.
+ // using AliasFoo = Foo<bool>;
+ if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(
+ RT->getAsCXXRecordDecl())) {
+ Template = CTSD->getSpecializedTemplate();
+ AliasRhsTemplateArgs = CTSD->getTemplateArgs().asArray();
+ }
+ }
+ if (!Template)
+ return;
+ DeclarationNameInfo NameInfo(
+ Context.DeclarationNames.getCXXDeductionGuideName(Template), Loc);
+ LookupResult Guides(SemaRef, NameInfo, clang::Sema::LookupOrdinaryName);
+ SemaRef.LookupQualifiedName(Guides, Template->getDeclContext());
+ Guides.suppressDiagnostics();
+
+ for (auto *G : Guides) {
+ FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(G);
+ if (!F)
+ continue;
+ auto RType = F->getTemplatedDecl()->getReturnType();
+ // The (trailing) return type of the deduction guide.
+ const TemplateSpecializationType *FReturnType =
+ RType->getAs<TemplateSpecializationType>();
+ if (const auto *InjectedCNT = RType->getAs<InjectedClassNameType>())
+ // implicitly-generated deduction guide.
+ FReturnType = InjectedCNT->getInjectedTST();
+ else if (const auto *ET = RType->getAs<ElaboratedType>())
+ // explicit deduction guide.
+ FReturnType = ET->getNamedType()->getAs<TemplateSpecializationType>();
+ assert(FReturnType);
+ // Deduce template arguments of the deduction guide f from the RHS of
+ // the alias.
+ //
+ // C++ [over.match.class.deduct]p3: ...For each function or function
+ // template f in the guides of the template named by the
+ // simple-template-id of the defining-type-id, the template arguments
+ // of the return type of f are deduced from the defining-type-id of A
+ // according to the process in [temp.deduct.type] with the exception
+ // that deduction does not fail if not all template arguments are
+ // deduced.
+ //
+ //
+ // template<typename X, typename Y>
+ // f(X, Y) -> f<Y, X>;
+ //
+ // template<typename U>
+ // using alias = f<int, U>;
+ //
+ // The RHS of alias is f<int, U>, we deduced the template arguments of
+ // the return type of the deduction guide from it: Y->int, X->U
+ sema::TemplateDeductionInfo TDeduceInfo(Loc);
+ // Must initialize n elements, this is required by DeduceTemplateArguments.
+ SmallVector<DeducedTemplateArgument> DeduceResults(
+ F->getTemplateParameters()->size());
+ // FIXME: DeduceTemplateArguments stops immediately at the first
+ // non-deducible template parameter, extend it to continue performing
+ // deduction for rest of parameters.
+ SemaRef.DeduceTemplateArguments(
+ F->getTemplateParameters(), FReturnType->template_arguments(),
+ AliasRhsTemplateArgs, TDeduceInfo, DeduceResults,
+ /*NumberOfArgumentsMustMatch=*/false);
+
+ SmallVector<TemplateArgument> DeducedArgs;
+ SmallVector<unsigned> NonDeducedTemplateParamsInFIndex;
+ // !!NOTE: DeduceResults respects the sequence of template parameters of
+ // the deduction guide f.
+ for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
+ if (const auto &D = DeduceResults[Index]; !D.isNull()) // Deduced
+ DeducedArgs.push_back(D);
+ else
+ NonDeducedTemplateParamsInFIndex.push_back(Index);
+ }
+ auto DeducedAliasTemplateParams =
+ FindAppearedTemplateParamsInAlias(DeducedArgs, AliasTemplate);
+ // All template arguments null by default.
+ SmallVector<TemplateArgument> TemplateArgsForBuildingFPrime(
+ F->getTemplateParameters()->size());
+
+ Sema::InstantiatingTemplate BuildingDeductionGuides(
+ SemaRef, AliasTemplate->getLocation(), F,
+ Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{});
+ if (BuildingDeductionGuides.isInvalid())
+ return;
+ LocalInstantiationScope Scope(SemaRef);
+
+ // Create a template parameter list for the synthesized deduction guide f'.
+ //
+ // C++ [over.match.class.deduct]p3.2:
+ // If f is a function template, f' is a function template whose template
+ // parameter list consists of all the template parameters of A
+ // (including their default template arguments) that appear in the above
+ // deductions or (recursively) in their default template arguments
+ SmallVector<NamedDecl *> FPrimeTemplateParams;
+ // Store template arguments that refer to the newly-created template
+ // parameters, used for building `TemplateArgsForBuildingFPrime`.
+ SmallVector<TemplateArgument, 16> TransformedDeducedAliasArgs(
+ AliasTemplate->getTemplateParameters()->size());
+ auto TransformTemplateParameter =
+ [&SemaRef](DeclContext *DC, NamedDecl *TemplateParam,
+ MultiLevelTemplateArgumentList &Args,
+ unsigned NewIndex) -> NamedDecl * {
+ if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam))
+ return transformTemplateTypeParam(SemaRef, DC, TTP, Args,
+ TTP->getDepth(), NewIndex);
+ if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam))
+ return transformTemplateParam(SemaRef, DC, TTP, Args, NewIndex,
+ TTP->getDepth());
+ if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(TemplateParam))
+ return transformTemplateParam(SemaRef, DC, NTTP, Args, NewIndex,
+ NTTP->getDepth());
+ return nullptr;
+ };
+
+ for (unsigned AliasTemplateParamIdx : DeducedAliasTemplateParams) {
+ auto *TP = AliasTemplate->getTemplateParameters()->getParam(
+ AliasTemplateParamIdx);
+ // Rebuild any internal references to earlier parameters and reindex as
+ // we go.
+ MultiLevelTemplateArgumentList Args;
+ Args.setKind(TemplateSubstitutionKind::Rewrite);
+ Args.addOuterTemplateArguments(TransformedDeducedAliasArgs);
+ NamedDecl *NewParam =
+ TransformTemplateParameter(AliasTemplate->getDeclContext(), TP, Args,
+ /*NewIndex*/ FPrimeTemplateParams.size());
+ FPrimeTemplateParams.push_back(NewParam);
+
+ auto NewTemplateArgument = Context.getCanonicalTemplateArgument(
+ Context.getInjectedTemplateArg(NewParam));
+ TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument;
+ }
+ // ...followed by the template parameters of f that were not deduced
+ // (including their default template arguments)
+ for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) {
+ auto *TP = F->getTemplateParameters()->getParam(FTemplateParamIdx);
+ MultiLevelTemplateArgumentList Args;
+ Args.setKind(TemplateSubstitutionKind::Rewrite);
+ // We take a shortcut here, it is ok to reuse the
+ // TemplateArgsForBuildingFPrime.
+ Args.addOuterTemplateArguments(TemplateArgsForBuildingFPrime);
+ NamedDecl *NewParam = TransformTemplateParameter(
+ F->getDeclContext(), TP, Args, FPrimeTemplateParams.size());
+ FPrimeTemplateParams.push_back(NewParam);
+
+ assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() &&
+ "InstantiatedArgs must be null before setting");
+ TemplateArgsForBuildingFPrime[FTemplateParamIdx] =
+ Context.getCanonicalTemplateArgument(
+ Context.getInjectedTemplateArg(NewParam));
+ }
+ // FIXME: support require clause.
+ auto *FPrimeTemplateParamList = TemplateParameterList::Create(
+ Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
+ AliasTemplate->getTemplateParameters()->getLAngleLoc(),
+ FPrimeTemplateParams,
+ AliasTemplate->getTemplateParameters()->getRAngleLoc(),
+ /*RequiresClause=*/nullptr);
+
+ // To form a deduction guide f' from f, we leverage clang's instantiation
+ // mechanism, we construct a template argument list where the template
+ // arguments refer to the newly-created template parameters of f', and
+ // then apply instantiation on this template argument list to instantiate
+ // f, this ensures all template parameter occurrences are updated
+ // correctly.
+ //
+ // The template argument list is formed from the `DeducedArgs`, two parts:
+ // 1) appeared template parameters of alias: transfrom the deduced
+ // template argument 2) non-deduced template parameters of f: rebuild a
+ // template argument
+ //
+ // 2) has been built already (when rebuilding the new template
+ // parameters), we now perform 1).
+ MultiLevelTemplateArgumentList Args;
+ Args.setKind(TemplateSubstitutionKind::Rewrite);
+ Args.addOuterTemplateArguments(TransformedDeducedAliasArgs);
+ for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
+ const auto &D = DeduceResults[Index];
+ if (D.isNull()) {
+ // 2): Non-deduced template parameter has been built already.
+ assert(!TemplateArgsForBuildingFPrime[Index].isNull());
+ continue;
+ }
+ TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(
+ D, QualType(), SourceLocation{});
+ TemplateArgumentLoc Output;
+ if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) {
+ assert(TemplateArgsForBuildingFPrime[Index].isNull() &&
+ "InstantiatedArgs must be null before setting");
+ TemplateArgsForBuildingFPrime[Index] = (Output.getArgument());
+ }
+ }
+
+ auto *TemplateArgListForBuildingFPrime = TemplateArgumentList::CreateCopy(
+ Context, TemplateArgsForBuildingFPrime);
+ // Form the f' by substituting the template arguments into f.
+ if (auto *FPrime = SemaRef.InstantiateFunctionDeclaration(
+ F, TemplateArgListForBuildingFPrime, AliasTemplate->getLocation(),
+ Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
+ auto *GG = dyn_cast<CXXDeductionGuideDecl>(FPrime);
+ // FIXME: implement the assoicated constraint per C++
+ // [over.match.class.deduct]p3.3:
+ // The associated constraints ([temp.constr.decl]) are the
+ // conjunction of the associated constraints of g and a
+ // constraint that is satisfied if and only if the arguments
+ // of A are deducible (see below) from the return type.
+ buildDeductionGuide(SemaRef, AliasTemplate, FPrimeTemplateParamList,
+ GG->getCorrespondingConstructor(),
+ GG->getExplicitSpecifier(), GG->getTypeSourceInfo(),
+ AliasTemplate->getBeginLoc(),
+ AliasTemplate->getLocation(),
+ AliasTemplate->getEndLoc(), F->isImplicit());
+ }
+ }
}
+} // namespace
+
FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList(
TemplateDecl *Template, MutableArrayRef<QualType> ParamTypes,
SourceLocation Loc) {
@@ -2571,6 +3017,10 @@ FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList(
void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
SourceLocation Loc) {
+ if (auto* AliasTemplate = llvm::dyn_cast<TypeAliasTemplateDecl>(Template)) {
+ DeclareImplicitDeductionGuidesForTypeAlias(*this, AliasTemplate, Loc);
+ return;
+ }
if (CXXRecordDecl *DefRecord =
cast<CXXRecordDecl>(Template->getTemplatedDecl())->getDefinition()) {
if (TemplateDecl *DescribedTemplate = DefRecord->getDescribedClassTemplate())
@@ -2586,12 +3036,8 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
if (!isCompleteType(Loc, Transform.DeducedType))
return;
- // Check whether we've already declared deduction guides for this template.
- // FIXME: Consider storing a flag on the template to indicate this.
- auto Existing = DC->lookup(Transform.DeductionGuideName);
- for (auto *D : Existing)
- if (D->isImplicit())
- return;
+ if (hasDeclaredDeductionGuides(Transform.DeductionGuideName, DC))
+ return;
// 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.
@@ -5242,6 +5688,15 @@ bool Sema::CheckTemplateTypeArgument(
[[fallthrough]];
}
default: {
+ // We allow instantiateing a template with template argument packs when
+ // building deduction guides.
+ if (Arg.getKind() == TemplateArgument::Pack &&
+ CodeSynthesisContexts.back().Kind ==
+ Sema::CodeSynthesisContext::BuildingDeductionGuides) {
+ SugaredConverted.push_back(Arg);
+ CanonicalConverted.push_back(Arg);
+ return false;
+ }
// We have a template type parameter but the template argument
// is not a type.
SourceRange SR = AL.getSourceRange();
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 4024d3708ab311..d7e3904ddc11c5 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2531,7 +2531,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
return TemplateDeductionResult::Success;
}
-Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
+TemplateDeductionResult Sema::DeduceTemplateArguments(
TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps,
ArrayRef<TemplateArgument> As, sema::TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 32b5f3756307bf..e03b3530119704 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1450,20 +1450,54 @@ namespace {
QualType TransformInjectedClassNameType(TypeLocBuilder &TLB,
InjectedClassNameTypeLoc TL) {
auto Type = inherited::TransformInjectedClassNameType(TLB, TL);
+ // Special case for transforming a deduction guide, we return a
+ // transformed TemplateSpecializationType.
if (Type.isNull() &&
SemaRef.CodeSynthesisContexts.back().Kind ==
Sema::CodeSynthesisContext::BuildingDeductionGuides) {
// Return a TemplateSpecializationType for transforming a deduction
// guide.
if (auto *ICT = TL.getType()->getAs<InjectedClassNameType>()) {
- auto TST = SemaRef.Context.getTemplateSpecializationType(
- ICT->getTemplateName(), TemplateArgs.getOutermost());
- TLB.pushTrivial(SemaRef.Context, TST, TL.getNameLoc());
- return TST;
+ auto Type =
+ inherited::TransformType(ICT->getInjectedSpecializationType());
+ TLB.pushTrivial(SemaRef.Context, Type, TL.getNameLoc());
+ return Type;
}
}
return Type;
}
+ // Override the default version to handle a rewrite-template-arg-pack case
+ // for building a deduction guide.
+ bool TransformTemplateArgument(const TemplateArgumentLoc &Input,
+ TemplateArgumentLoc &Output,
+ bool Uneval = false) {
+ const TemplateArgument &Arg = Input.getArgument();
+ std::vector<TemplateArgument> TArgs;
+ switch (Arg.getKind()) {
+ case TemplateArgument::Pack:
+ // Iterially rewrite the template argument pack, instead of unpacking
+ // it.
+ assert(
+ SemaRef.CodeSynthesisContexts.back().Kind ==
+ Sema::CodeSynthesisContext::BuildingDeductionGuides &&
+ "Transforming a template argument pack is only allowed in building "
+ "deduction guide");
+ for (auto &pack : Arg.getPackAsArray()) {
+ TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(
+ pack, QualType(), SourceLocation{});
+ TemplateArgumentLoc Output;
+ if (!SemaRef.SubstTemplateArgument(Input, TemplateArgs, Output))
+ TArgs.push_back(Output.getArgument());
+ }
+ Output = SemaRef.getTrivialTemplateArgumentLoc(
+ TemplateArgument(llvm::ArrayRef(TArgs).copy(SemaRef.Context)),
+ QualType(), SourceLocation{});
+ return false;
+ default:
+ break;
+ }
+ return inherited::TransformTemplateArgument(Input, Output, Uneval);
+ }
template<typename Fn>
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
@@ -4157,6 +4191,15 @@ Sema::SubstStmt(Stmt *S, const MultiLevelTemplateArgumentList &TemplateArgs) {
return Instantiator.TransformStmt(S);
}
+bool Sema::SubstTemplateArgument(
+ const TemplateArgumentLoc &Input,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ TemplateArgumentLoc &Output) {
+ TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
+ DeclarationName());
+ return Instantiator.TransformTemplateArgument(Input, Output);
+}
+
bool Sema::SubstTemplateArguments(
ArrayRef<TemplateArgumentLoc> Args,
const MultiLevelTemplateArgumentList &TemplateArgs,
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 5e17abb92941e1..1b45327f718ed7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2219,7 +2219,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
FunctionTemplate->setInstantiatedFromMemberTemplate(
D->getDescribedFunctionTemplate());
}
- } else if (FunctionTemplate) {
+ } else if (FunctionTemplate && SemaRef.CodeSynthesisContexts.back().Kind !=
+ Sema::CodeSynthesisContext::BuildingDeductionGuides) {
// Record this function template specialization.
ArrayRef<TemplateArgument> Innermost = TemplateArgs.getInnermost();
Function->setFunctionTemplateSpecialization(FunctionTemplate,
@@ -6286,13 +6287,13 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
CXXRecordDecl *SubstRecord = T->getAsCXXRecordDecl();
if (!SubstRecord) {
- // The template id T is a TemplateSpecializationType when performing
- // a substitution for a deduction guide,
+ // The T can be a dependent TemplateSpecializationType when
+ // performing a substitution for building a deduction guide,
assert(CodeSynthesisContexts.back().Kind ==
CodeSynthesisContext::BuildingDeductionGuides);
// Return a nullptr as a sentinel value, we handle it properly in
// the TemplateInstantiator::TransformInjectedClassNameType
- // override.
+ // override, which we transform it to a TemplateSpecializationType.
return nullptr;
}
// Check that this template-id names the primary template and not a
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 6e5ae123a6ba2c..36687dd64fc807 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4776,6 +4776,14 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
TemplateArgumentLoc In = *First;
if (In.getArgument().getKind() == TemplateArgument::Pack) {
+ // When building the deduction guides, we rewrite the argument packs
+ // instead of unpacking.
+ if (getSema().CodeSynthesisContexts.back().Kind ==
+ Sema::CodeSynthesisContext::BuildingDeductionGuides) {
+ if (getDerived().TransformTemplateArgument(In, Out, Uneval))
+ return true;
+ continue;
+ }
// Unpack argument packs, which we translate them into separate
// arguments.
// FIXME: We could do much better if we could guarantee that the
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 5a5691e48fd17e..169061ff556f4b 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -1,10 +1,8 @@
// RUN: %clang_cc1 -fsyntax-only -Wno-c++11-narrowing -Wno-literal-conversion -std=c++20 -verify %s
-
+// expected-no-diagnostics
namespace test1 {
template <typename T>
-struct Foo {
- T t;
-};
+struct Foo { T t; };
template <typename U>
using Bar = Foo<U>;
@@ -41,6 +39,7 @@ vector v(0, 0);
} // namespace test3
namespace test4 {
+// Explicit deduction guide.
template <class T>
struct X {
T t;
@@ -54,28 +53,28 @@ template <class T>
using AX = X<T>;
AX s = {1};
-static_assert(__is_same(decltype(s.t), double));
+static_assert(__is_same(decltype(s.t), double)); // explicit one is picked.
} // namespace test4
namespace test5 {
template <int B>
struct Foo {};
-
+// Template parameter pack
template <int... C>
using AF = Foo<1>;
auto a = AF{};
} // namespace test5
namespace test6 {
+// non-type template argument.
template <typename T, bool B = false>
struct Foo {
Foo(T);
};
-// non-type template argument.
template <typename T>
using AF = Foo<T, 1>;
-AF b{0}; //
+AF b{0};
} // namespace test6
namespace test7 {
@@ -83,13 +82,12 @@ template <typename T>
struct Foo {
Foo(T);
};
-
+// using alias chain.
template <typename U>
using AF1 = Foo<U>;
template <typename K>
-using AF2 = AF1<K>; // expected-note {{template is declared here}}
-// FIXME: support this case.
-AF2 b = 1; // expected-error {{alias template 'AF2' requires template arguments; argument deduction only allowed for class templates}}
+using AF2 = AF1<K>;
+AF2 b = 1;
} // namespace test7
namespace test8 {
@@ -113,7 +111,7 @@ struct Foo {
template <typename X, int Y>
using Bar = Foo<X, sizeof(X)>;
-// FIXME: should we reject this case? GCC rejects it, MSVC accepts it.
+// FIXME: we should reject this case? GCC rejects it, MSVC accepts it.
Bar s = {{1}};
} // namespace test9
@@ -131,3 +129,58 @@ template <typename K>
using A = Foo<K>;
A a(2); // Foo<int*>
} // namespace test10
+
+namespace test11 {
+struct A {};
+template<class T> struct Foo { T c; };
+// FIXME: we have an out-bound crash on instantating the synthesized deduction guide `auto (B<C2>) -> B<C2>`
+// where C2 should be at the index 0, however, it is still refers the original one where index is 1
+template<class X, class Y=A> using AFoo = Foo<Y>;
+
+AFoo s = {1};
+} // namespace test11
+
+namespace test12 {
+// no crash on null access attribute
+template<typename X>
+struct Foo {
+ template<typename K>
+ struct Bar {
+ Bar(K);
+ };
+
+ template<typename U>
+ using ABar = Bar<U>;
+ void test() { ABar k = 2; }
+};
+
+void func(Foo<int> s) {
+ s.test();
+}
+} // namespace test12
+
+namespace test13 {
+template <typename... Ts>
+struct Foo {
+ Foo(Ts...);
+};
+
+template <typename... Ts>
+using AFoo = Foo<Ts...>;
+
+auto b = AFoo{};
+} // namespace test13
+
+namespace test14 {
+template <class T> struct Foo { Foo(T); };
+
+template<class V> using AFoo = Foo<V *>;
+template<typename> concept False = false;
+template<False W> using BFoo = AFoo<W>;
+int i = 0;
+AFoo a1(&i); // OK, deduce Foo<int *>
+
+// FIXME: we should reject this case as the W is not deduced from the deduced
+// type Foo<int *>.
+BFoo b2(&i);
+} // namespace test14
>From 59e12637bb299b8ae453578b31519a3f18a3b83e Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Fri, 23 Feb 2024 15:02:02 +0100
Subject: [PATCH 3/6] Address review comments, and add more tests.
---
clang/lib/Sema/SemaInit.cpp | 2 +
clang/lib/Sema/SemaTemplate.cpp | 28 ++++++++------
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 40 +++++++++++++++++++-
3 files changed, 57 insertions(+), 13 deletions(-)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 6d288d74761319..e72e40fa019b07 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10621,6 +10621,8 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(
RT->getAsCXXRecordDecl()))
Template = CTSD->getSpecializedTemplate();
+ } else {
+ assert(false && "unexpected underlying type of alias template");
}
}
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index bbecb5e70804e0..0d0f785baf97ba 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -12,7 +12,6 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
@@ -40,7 +39,6 @@
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
#include <iterator>
#include <optional>
@@ -2716,9 +2714,12 @@ FindAppearedTemplateParamsInAlias(ArrayRef<TemplateArgument> DeducedArgs,
return Results;
}
-bool hasDeclaredDeductionGuides(DeclarationName Name, DeclContext* DC) {
+bool hasDeclaredDeductionGuides(DeclarationName Name, DeclContext *DC) {
// Check whether we've already declared deduction guides for this template.
// FIXME: Consider storing a flag on the template to indicate this.
+ assert(Name.getNameKind() ==
+ DeclarationName::NameKind::CXXDeductionGuideName &&
+ "name must be a deduction guide name");
auto Existing = DC->lookup(Name);
for (auto *D : Existing)
if (D->isImplicit())
@@ -2737,14 +2738,14 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate),
AliasTemplate->getDeclContext()))
return;
- // Unrap the sugar ElaboratedType.
+ // Unwrap the sugared ElaboratedType.
auto RhsType = AliasTemplate->getTemplatedDecl()
->getUnderlyingType()
.getSingleStepDesugaredType(Context);
TemplateDecl *Template = nullptr;
llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs;
if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) {
- // TemplateName in TEST can be a TypeAliasTemplateDecl if
+ // TemplateName in TST can be a TypeAliasTemplateDecl if
// the right hand side of the alias is also a type alias, e.g.
//
// template<typename T>
@@ -2787,7 +2788,7 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
else if (const auto *ET = RType->getAs<ElaboratedType>())
// explicit deduction guide.
FReturnType = ET->getNamedType()->getAs<TemplateSpecializationType>();
- assert(FReturnType);
+ assert(FReturnType && "expected to see a return type");
// Deduce template arguments of the deduction guide f from the RHS of
// the alias.
//
@@ -2812,9 +2813,12 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
// Must initialize n elements, this is required by DeduceTemplateArguments.
SmallVector<DeducedTemplateArgument> DeduceResults(
F->getTemplateParameters()->size());
+
// FIXME: DeduceTemplateArguments stops immediately at the first
- // non-deducible template parameter, extend it to continue performing
- // deduction for rest of parameters.
+ // non-deducible template argument. However, this doesn't seem to casue
+ // issues for practice cases, we probably need to extend it to continue
+ // performing deduction for rest of arguments to align with the C++
+ // standard.
SemaRef.DeduceTemplateArguments(
F->getTemplateParameters(), FReturnType->template_arguments(),
AliasRhsTemplateArgs, TDeduceInfo, DeduceResults,
@@ -2902,7 +2906,7 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
FPrimeTemplateParams.push_back(NewParam);
assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() &&
- "InstantiatedArgs must be null before setting");
+ "The argument must be null before setting");
TemplateArgsForBuildingFPrime[FTemplateParamIdx] =
Context.getCanonicalTemplateArgument(
Context.getInjectedTemplateArg(NewParam));
@@ -2936,7 +2940,9 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
const auto &D = DeduceResults[Index];
if (D.isNull()) {
// 2): Non-deduced template parameter has been built already.
- assert(!TemplateArgsForBuildingFPrime[Index].isNull());
+ assert(!TemplateArgsForBuildingFPrime[Index].isNull() &&
+ "template arguments for non-deduced template parameters should "
+ "be been set!");
continue;
}
TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(
@@ -2956,7 +2962,7 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
F, TemplateArgListForBuildingFPrime, AliasTemplate->getLocation(),
Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
auto *GG = dyn_cast<CXXDeductionGuideDecl>(FPrime);
- // FIXME: implement the assoicated constraint per C++
+ // FIXME: implement the associated constraint per C++
// [over.match.class.deduct]p3.3:
// The associated constraints ([temp.constr.decl]) are the
// conjunction of the associated constraints of g and a
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 169061ff556f4b..820ceb83100273 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -Wno-c++11-narrowing -Wno-literal-conversion -std=c++20 -verify %s
-// expected-no-diagnostics
+
namespace test1 {
template <typename T>
struct Foo { T t; };
@@ -172,6 +172,22 @@ auto b = AFoo{};
} // namespace test13
namespace test14 {
+template<typename T>
+concept IsInt = __is_same(decltype(T()), int);
+
+template<IsInt T, int N>
+struct Foo {
+ Foo(T const (&)[N]);
+};
+
+template <int K>
+using Bar = Foo<double, K>; // expected-note {{constraints not satisfied for class template 'Foo'}}
+// expected-note at -1 {{candidate template ignored: could not match}}
+double abc[3];
+Bar s2 = {abc}; // expected-error {{no viable constructor or deduction guide for deduction }}
+} // namespace test14
+
+namespace test15 {
template <class T> struct Foo { Foo(T); };
template<class V> using AFoo = Foo<V *>;
@@ -183,4 +199,24 @@ AFoo a1(&i); // OK, deduce Foo<int *>
// FIXME: we should reject this case as the W is not deduced from the deduced
// type Foo<int *>.
BFoo b2(&i);
-} // namespace test14
+} // namespace test15
+
+namespace test16 {
+struct X { X(int); X(const X&); };
+template<class T>
+struct Foo {
+ T t;
+ Foo(T t) : t(t) {}
+};
+template<class T>
+using AFoo = Foo<T>;
+int i = 0;
+AFoo s{i};
+static_assert(__is_same(decltype(s.t), int));
+
+// explicit deduction guide.
+Foo(int) -> Foo<X>;
+AFoo s2{i};
+// FIXME: the type should be X because of the above explicit deduction guide.
+static_assert(__is_same(decltype(s2.t), int));
+} // namespace test16
>From 4d00c17d3f95624730672f708fadf23eac266afc Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Fri, 23 Feb 2024 15:18:37 +0100
Subject: [PATCH 4/6] Bailout when SubstTemplateArgument fails.
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index e03b3530119704..37306fbf5b1579 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1486,8 +1486,9 @@ namespace {
TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(
pack, QualType(), SourceLocation{});
TemplateArgumentLoc Output;
- if (!SemaRef.SubstTemplateArgument(Input, TemplateArgs, Output))
- TArgs.push_back(Output.getArgument());
+ if (SemaRef.SubstTemplateArgument(Input, TemplateArgs, Output))
+ return true; // fails
+ TArgs.push_back(Output.getArgument());
}
Output = SemaRef.getTrivialTemplateArgumentLoc(
TemplateArgument(llvm::ArrayRef(TArgs).copy(SemaRef.Context)),
>From a53b93f693786d7333b7a605d09d6501932d11da Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Mon, 26 Feb 2024 19:32:52 +0100
Subject: [PATCH 5/6] Add assertion on unhandled RHS type of the type alias.
---
clang/lib/Sema/SemaTemplate.cpp | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 0d0f785baf97ba..23c8ae4605b23f 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2745,15 +2745,9 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
TemplateDecl *Template = nullptr;
llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs;
if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) {
- // TemplateName in TST can be a TypeAliasTemplateDecl if
- // the right hand side of the alias is also a type alias, e.g.
- //
- // template<typename T>
- // using AliasFoo1 = Foo<T>; // Foo<T> is a class template
- // specialization
- //
- // template<typename T>
- // using AliasFoo2 = AliasFoo1<T>; // AliasFoo1<T> is a type alias
+ // Cases where the RHS of the alias is dependent. e.g.
+ // template<typename T>
+ // using AliasFoo1 = Foo<T>; // a class/type alias template specialization
Template = TST->getTemplateName().getAsTemplateDecl();
AliasRhsTemplateArgs = TST->template_arguments();
} else if (const auto *RT = RhsType->getAs<RecordType>()) {
@@ -2765,6 +2759,8 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
Template = CTSD->getSpecializedTemplate();
AliasRhsTemplateArgs = CTSD->getTemplateArgs().asArray();
}
+ } else {
+ assert(false && "unhandled RHS type of the alias");
}
if (!Template)
return;
>From 2a32997d2b7758b9df964870c3ab8b8722df6cbf Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 27 Feb 2024 20:23:16 +0100
Subject: [PATCH 6/6] Address review comments:
- simplify the if
- add a release note
---
clang/docs/ReleaseNotes.rst | 4 ++++
clang/lib/Sema/SemaInit.cpp | 3 +--
clang/lib/Sema/SemaTemplate.cpp | 2 +-
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 16f79a349c20c8..8ee6a309ab585f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -96,6 +96,10 @@ C++20 Feature Support
behavior can use the flag '-Xclang -fno-skip-odr-check-in-gmf'.
(`#79240 <https://github.com/llvm/llvm-project/issues/79240>`_).
+- Initial support for class template argument deduciton (CTAD) for type alias
+ templates.
+ (`#54051 https://github.com/llvm/llvm-project/issues/54051`_).
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index e72e40fa019b07..59cb741b289c21 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10604,8 +10604,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
TemplateDecl* LookupTemplateDecl = Template;
if (!Template && getLangOpts().CPlusPlus20) { // type alias template
if (auto *AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>(
- TemplateName.getAsTemplateDecl());
- AliasTemplate) {
+ TemplateName.getAsTemplateDecl())) {
LookupTemplateDecl = AliasTemplate;
auto UnderlyingType = AliasTemplate->getTemplatedDecl()
->getUnderlyingType()
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 23c8ae4605b23f..42844a97d05e3c 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2318,7 +2318,7 @@ transformTemplateTypeParam(Sema &SemaRef, DeclContext *DC,
: std::nullopt);
if (const auto *TC = TTP->getTypeConstraint())
SemaRef.SubstTypeConstraint(NewTTP, TC, Args,
- /*EvaluateConstraint*/ true);
+ /*EvaluateConstraint=*/true);
if (TTP->hasDefaultArgument()) {
TypeSourceInfo *InstantiatedDefaultArg =
SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
More information about the cfe-commits
mailing list