Skip to content

Commit 8f7d83a

Browse files
committed
[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.
1 parent fb9a82b commit 8f7d83a

File tree

10 files changed

+858
-160
lines changed

10 files changed

+858
-160
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9280,6 +9280,14 @@ class Sema final {
92809280
const TemplateArgumentList &TemplateArgs,
92819281
sema::TemplateDeductionInfo &Info);
92829282

9283+
TemplateDeductionResult
9284+
DeduceTemplateArguments(TemplateParameterList *TemplateParams,
9285+
ArrayRef<TemplateArgument> Ps,
9286+
ArrayRef<TemplateArgument> As,
9287+
sema::TemplateDeductionInfo &Info,
9288+
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
9289+
bool NumberOfArgumentsMustMatch);
9290+
92839291
TemplateDeductionResult SubstituteExplicitTemplateArguments(
92849292
FunctionTemplateDecl *FunctionTemplate,
92859293
TemplateArgumentListInfo &ExplicitTemplateArgs,
@@ -10432,9 +10440,11 @@ class Sema final {
1043210440
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
1043310441
ArrayRef<TemplateArgument> TemplateArgs,
1043410442
ConstraintSatisfaction &Satisfaction);
10435-
FunctionDecl *InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD,
10436-
const TemplateArgumentList *Args,
10437-
SourceLocation Loc);
10443+
FunctionDecl *InstantiateFunctionDeclaration(
10444+
FunctionTemplateDecl *FTD, const TemplateArgumentList *Args,
10445+
SourceLocation Loc,
10446+
CodeSynthesisContext::SynthesisKind CSC =
10447+
CodeSynthesisContext::ExplicitTemplateArgumentSubstitution);
1043810448
void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
1043910449
FunctionDecl *Function,
1044010450
bool Recursive = false,

clang/lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins
1414

1515
add_clang_library(clangSema
1616
AnalysisBasedWarnings.cpp
17+
CTAD.cpp
1718
CodeCompleteConsumer.cpp
1819
DeclSpec.cpp
1920
DelayedDiagnostic.cpp

clang/lib/Sema/CTAD.cpp

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
//===--- CTAD.cpp - -------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "CTAD.h"
10+
#include "TreeTransform.h"
11+
#include "TypeLocBuilder.h"
12+
#include "clang/AST/ASTConsumer.h"
13+
#include "clang/AST/ASTContext.h"
14+
#include "clang/AST/ASTMutationListener.h"
15+
#include "clang/AST/ASTStructuralEquivalence.h"
16+
#include "clang/AST/CXXInheritance.h"
17+
#include "clang/AST/Decl.h"
18+
#include "clang/AST/DeclObjC.h"
19+
#include "clang/AST/DeclTemplate.h"
20+
#include "clang/AST/Expr.h"
21+
#include "clang/AST/Type.h"
22+
#include "clang/AST/TypeLoc.h"
23+
#include "clang/Basic/SourceLocation.h"
24+
#include "clang/Basic/Specifiers.h"
25+
#include "clang/Sema/DeclSpec.h"
26+
#include "clang/Sema/ScopeInfo.h"
27+
#include "clang/Sema/Template.h"
28+
#include "llvm/ADT/ArrayRef.h"
29+
#include <optional>
30+
31+
namespace clang {
32+
33+
namespace {
34+
/// Tree transform to "extract" a transformed type from a class template's
35+
/// constructor to a deduction guide.
36+
class ExtractTypeForDeductionGuide
37+
: public TreeTransform<ExtractTypeForDeductionGuide> {
38+
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs;
39+
40+
public:
41+
typedef TreeTransform<ExtractTypeForDeductionGuide> Base;
42+
ExtractTypeForDeductionGuide(
43+
Sema &SemaRef,
44+
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs)
45+
: Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs) {}
46+
47+
TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); }
48+
49+
QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) {
50+
ASTContext &Context = SemaRef.getASTContext();
51+
TypedefNameDecl *OrigDecl = TL.getTypedefNameDecl();
52+
TypedefNameDecl *Decl = OrigDecl;
53+
// Transform the underlying type of the typedef and clone the Decl only if
54+
// the typedef has a dependent context.
55+
if (OrigDecl->getDeclContext()->isDependentContext()) {
56+
TypeLocBuilder InnerTLB;
57+
QualType Transformed =
58+
TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc());
59+
TypeSourceInfo *TSI = InnerTLB.getTypeSourceInfo(Context, Transformed);
60+
if (isa<TypeAliasDecl>(OrigDecl))
61+
Decl = TypeAliasDecl::Create(
62+
Context, Context.getTranslationUnitDecl(), OrigDecl->getBeginLoc(),
63+
OrigDecl->getLocation(), OrigDecl->getIdentifier(), TSI);
64+
else {
65+
assert(isa<TypedefDecl>(OrigDecl) && "Not a Type alias or typedef");
66+
Decl = TypedefDecl::Create(
67+
Context, Context.getTranslationUnitDecl(), OrigDecl->getBeginLoc(),
68+
OrigDecl->getLocation(), OrigDecl->getIdentifier(), TSI);
69+
}
70+
MaterializedTypedefs.push_back(Decl);
71+
}
72+
73+
QualType TDTy = Context.getTypedefType(Decl);
74+
TypedefTypeLoc TypedefTL = TLB.push<TypedefTypeLoc>(TDTy);
75+
TypedefTL.setNameLoc(TL.getNameLoc());
76+
77+
return TDTy;
78+
}
79+
};
80+
} // namespace
81+
82+
ParmVarDecl *transformFunctionTypeParam(
83+
Sema &SemaRef, ParmVarDecl *OldParam, DeclContext *DC,
84+
MultiLevelTemplateArgumentList &Args,
85+
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
86+
TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
87+
TypeSourceInfo *NewDI;
88+
if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
89+
// Expand out the one and only element in each inner pack.
90+
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, 0);
91+
NewDI = SemaRef.SubstType(PackTL.getPatternLoc(), Args,
92+
OldParam->getLocation(), OldParam->getDeclName());
93+
if (!NewDI)
94+
return nullptr;
95+
NewDI = SemaRef.CheckPackExpansion(NewDI, PackTL.getEllipsisLoc(),
96+
PackTL.getTypePtr()->getNumExpansions());
97+
} else
98+
NewDI = SemaRef.SubstType(OldDI, Args, OldParam->getLocation(),
99+
OldParam->getDeclName());
100+
if (!NewDI)
101+
return nullptr;
102+
103+
// Extract the type. This (for instance) replaces references to typedef
104+
// members of the current instantiations with the definitions of those
105+
// typedefs, avoiding triggering instantiation of the deduced type during
106+
// deduction.
107+
NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs)
108+
.transform(NewDI);
109+
110+
// Resolving a wording defect, we also inherit default arguments from the
111+
// constructor.
112+
ExprResult NewDefArg;
113+
if (OldParam->hasDefaultArg()) {
114+
// We don't care what the value is (we won't use it); just create a
115+
// placeholder to indicate there is a default argument.
116+
QualType ParamTy = NewDI->getType();
117+
NewDefArg = new (SemaRef.Context)
118+
OpaqueValueExpr(OldParam->getDefaultArgRange().getBegin(),
119+
ParamTy.getNonLValueExprType(SemaRef.Context),
120+
ParamTy->isLValueReferenceType() ? VK_LValue
121+
: ParamTy->isRValueReferenceType() ? VK_XValue
122+
: VK_PRValue);
123+
}
124+
// Handle arrays and functions decay.
125+
auto NewType = NewDI->getType();
126+
if (NewType->isArrayType() || NewType->isFunctionType())
127+
NewType = SemaRef.Context.getDecayedType(NewType);
128+
129+
ParmVarDecl *NewParam = ParmVarDecl::Create(
130+
SemaRef.Context, DC, OldParam->getInnerLocStart(),
131+
OldParam->getLocation(), OldParam->getIdentifier(), NewType, NewDI,
132+
OldParam->getStorageClass(), NewDefArg.get());
133+
NewParam->setScopeInfo(OldParam->getFunctionScopeDepth(),
134+
OldParam->getFunctionScopeIndex());
135+
SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, NewParam);
136+
return NewParam;
137+
}
138+
139+
TemplateTypeParmDecl *
140+
transformTemplateTypeParam(Sema &SemaRef, DeclContext *DC,
141+
TemplateTypeParmDecl *TTP,
142+
MultiLevelTemplateArgumentList &Args,
143+
unsigned NewDepth, unsigned NewIndex) {
144+
// TemplateTypeParmDecl's index cannot be changed after creation, so
145+
// substitute it directly.
146+
auto *NewTTP = TemplateTypeParmDecl::Create(
147+
SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(), NewDepth,
148+
NewIndex, TTP->getIdentifier(), TTP->wasDeclaredWithTypename(),
149+
TTP->isParameterPack(), TTP->hasTypeConstraint(),
150+
TTP->isExpandedParameterPack()
151+
? std::optional<unsigned>(TTP->getNumExpansionParameters())
152+
: std::nullopt);
153+
if (const auto *TC = TTP->getTypeConstraint())
154+
SemaRef.SubstTypeConstraint(NewTTP, TC, Args,
155+
/*EvaluateConstraint*/ true);
156+
if (TTP->hasDefaultArgument()) {
157+
TypeSourceInfo *InstantiatedDefaultArg =
158+
SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
159+
TTP->getDefaultArgumentLoc(), TTP->getDeclName());
160+
if (InstantiatedDefaultArg)
161+
NewTTP->setDefaultArgument(InstantiatedDefaultArg);
162+
}
163+
SemaRef.CurrentInstantiationScope->InstantiatedLocal(TTP, NewTTP);
164+
return NewTTP;
165+
}
166+
167+
FunctionTemplateDecl *
168+
buildDeductionGuide(Sema &SemaRef, TemplateDecl *OriginalTemplate,
169+
TemplateParameterList *TemplateParams,
170+
CXXConstructorDecl *Ctor, ExplicitSpecifier ES,
171+
TypeSourceInfo *TInfo, SourceLocation LocStart,
172+
SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
173+
llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs) {
174+
DeclContext *DC = OriginalTemplate->getDeclContext();
175+
auto DeductionGuideName =
176+
SemaRef.Context.DeclarationNames.getCXXDeductionGuideName(
177+
OriginalTemplate);
178+
179+
DeclarationNameInfo Name(DeductionGuideName, Loc);
180+
ArrayRef<ParmVarDecl *> Params =
181+
TInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams();
182+
183+
// Build the implicit deduction guide template.
184+
auto *Guide =
185+
CXXDeductionGuideDecl::Create(SemaRef.Context, DC, LocStart, ES, Name,
186+
TInfo->getType(), TInfo, LocEnd, Ctor);
187+
Guide->setImplicit(IsImplicit);
188+
Guide->setParams(Params);
189+
190+
for (auto *Param : Params)
191+
Param->setDeclContext(Guide);
192+
for (auto *TD : MaterializedTypedefs)
193+
TD->setDeclContext(Guide);
194+
195+
auto *GuideTemplate = FunctionTemplateDecl::Create(
196+
SemaRef.Context, DC, Loc, DeductionGuideName, TemplateParams, Guide);
197+
GuideTemplate->setImplicit(IsImplicit);
198+
Guide->setDescribedFunctionTemplate(GuideTemplate);
199+
200+
if (isa<CXXRecordDecl>(DC)) {
201+
Guide->setAccess(AS_public);
202+
GuideTemplate->setAccess(AS_public);
203+
}
204+
205+
DC->addDecl(GuideTemplate);
206+
return GuideTemplate;
207+
}
208+
209+
} // namespace clang

clang/lib/Sema/CTAD.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===--- CTAD.h - Helper functions for CTAD -------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines helper functions for the class template argument deduction
10+
// (CTAD) implementation.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "clang/AST/ASTContext.h"
15+
#include "clang/AST/ASTMutationListener.h"
16+
#include "clang/AST/ASTStructuralEquivalence.h"
17+
#include "clang/AST/Decl.h"
18+
#include "clang/AST/DeclCXX.h"
19+
#include "clang/AST/DeclTemplate.h"
20+
#include "clang/AST/Type.h"
21+
#include "clang/Basic/SourceLocation.h"
22+
#include "clang/Sema/DeclSpec.h"
23+
#include "clang/Sema/ScopeInfo.h"
24+
#include "clang/Sema/Template.h"
25+
#include "llvm/ADT/ArrayRef.h"
26+
27+
namespace clang {
28+
29+
// Transform a given function parameter decl into a deduction guide parameter
30+
// decl.
31+
ParmVarDecl *transformFunctionTypeParam(
32+
Sema &SemaRef, ParmVarDecl *OldParam, DeclContext *DC,
33+
MultiLevelTemplateArgumentList &Args,
34+
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs);
35+
36+
// Transform a given template type parameter into a deduction guide template
37+
// parameter, rebuilding any internal references to earlier parameters and
38+
// re-indexing as we go.
39+
TemplateTypeParmDecl *transformTemplateTypeParam(
40+
Sema &SemaRef, DeclContext *DC, TemplateTypeParmDecl *TPT,
41+
MultiLevelTemplateArgumentList &Args, unsigned NewDepth, unsigned NewIndex);
42+
// Similar to above, but for non-type template or template template parameters.
43+
template <typename NonTypeTemplateOrTemplateTemplateParmDecl>
44+
NonTypeTemplateOrTemplateTemplateParmDecl *transformTemplateParam(
45+
Sema &SemaRef, DeclContext *DC,
46+
NonTypeTemplateOrTemplateTemplateParmDecl *OldParam,
47+
MultiLevelTemplateArgumentList &Args, unsigned NewIndex) {
48+
// Ask the template instantiator to do the heavy lifting for us, then adjust
49+
// the index of the parameter once it's done.
50+
auto *NewParam = cast<NonTypeTemplateOrTemplateTemplateParmDecl>(
51+
SemaRef.SubstDecl(OldParam, DC, Args));
52+
NewParam->setPosition(NewIndex);
53+
return NewParam;
54+
}
55+
56+
// Build a deduction guide with the specified parameter types.
57+
FunctionTemplateDecl *buildDeductionGuide(
58+
Sema &SemaRef, TemplateDecl *OriginalTemplate,
59+
TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor,
60+
ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart,
61+
SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
62+
llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {});
63+
64+
} // namespace clang

0 commit comments

Comments
 (0)