Skip to content

Commit 875ab4c

Browse files
committed
[clang] CTAD: build aggregate deduction guides for alias templates.
Fixes #85767.
1 parent 846394e commit 875ab4c

File tree

3 files changed

+160
-51
lines changed

3 files changed

+160
-51
lines changed

clang/lib/Sema/SemaInit.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10932,7 +10932,8 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
1093210932

1093310933
if (FunctionTemplateDecl *TD =
1093410934
DeclareImplicitDeductionGuideFromInitList(
10935-
Template, ElementTypes, TSInfo->getTypeLoc().getEndLoc())) {
10935+
LookupTemplateDecl, ElementTypes,
10936+
TSInfo->getTypeLoc().getEndLoc())) {
1093610937
auto *GD = cast<CXXDeductionGuideDecl>(TD->getTemplatedDecl());
1093710938
addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public),
1093810939
OnlyListConstructors,

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 146 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,23 +2754,42 @@ bool hasDeclaredDeductionGuides(DeclarationName Name, DeclContext *DC) {
27542754
return false;
27552755
}
27562756

2757-
// Build deduction guides for a type alias template.
2758-
void DeclareImplicitDeductionGuidesForTypeAlias(
2759-
Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc) {
2760-
if (AliasTemplate->isInvalidDecl())
2761-
return;
2762-
auto &Context = SemaRef.Context;
2763-
// FIXME: if there is an explicit deduction guide after the first use of the
2764-
// type alias usage, we will not cover this explicit deduction guide. fix this
2765-
// case.
2766-
if (hasDeclaredDeductionGuides(
2767-
Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate),
2768-
AliasTemplate->getDeclContext()))
2769-
return;
2757+
NamedDecl *transformTemplateParameter(Sema &SemaRef, DeclContext *DC,
2758+
NamedDecl *TemplateParam,
2759+
MultiLevelTemplateArgumentList &Args,
2760+
unsigned NewIndex) {
2761+
if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam))
2762+
return transformTemplateTypeParam(SemaRef, DC, TTP, Args, TTP->getDepth(),
2763+
NewIndex);
2764+
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam))
2765+
return transformTemplateParam(SemaRef, DC, TTP, Args, NewIndex,
2766+
TTP->getDepth());
2767+
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(TemplateParam))
2768+
return transformTemplateParam(SemaRef, DC, NTTP, Args, NewIndex,
2769+
NTTP->getDepth());
2770+
return nullptr;
2771+
}
2772+
2773+
Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *FTD,
2774+
llvm::ArrayRef<TemplateArgument> TransformedArgs) {
2775+
Expr *RC = FTD->getTemplateParameters()->getRequiresClause();
2776+
if (!RC)
2777+
return nullptr;
2778+
MultiLevelTemplateArgumentList Args;
2779+
Args.setKind(TemplateSubstitutionKind::Rewrite);
2780+
Args.addOuterTemplateArguments(TransformedArgs);
2781+
ExprResult E = SemaRef.SubstExpr(RC, Args);
2782+
if (E.isInvalid())
2783+
return nullptr;
2784+
return E.getAs<Expr>();
2785+
}
2786+
2787+
std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>>
2788+
getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) {
27702789
// Unwrap the sugared ElaboratedType.
27712790
auto RhsType = AliasTemplate->getTemplatedDecl()
27722791
->getUnderlyingType()
2773-
.getSingleStepDesugaredType(Context);
2792+
.getSingleStepDesugaredType(SemaRef.Context);
27742793
TemplateDecl *Template = nullptr;
27752794
llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs;
27762795
if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) {
@@ -2791,6 +2810,24 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
27912810
} else {
27922811
assert(false && "unhandled RHS type of the alias");
27932812
}
2813+
return {Template, AliasRhsTemplateArgs};
2814+
}
2815+
2816+
// Build deduction guides for a type alias template.
2817+
void DeclareImplicitDeductionGuidesForTypeAlias(
2818+
Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc) {
2819+
if (AliasTemplate->isInvalidDecl())
2820+
return;
2821+
auto &Context = SemaRef.Context;
2822+
// FIXME: if there is an explicit deduction guide after the first use of the
2823+
// type alias usage, we will not cover this explicit deduction guide. fix this
2824+
// case.
2825+
if (hasDeclaredDeductionGuides(
2826+
Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate),
2827+
AliasTemplate->getDeclContext()))
2828+
return;
2829+
auto [Template, AliasRhsTemplateArgs] =
2830+
getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate);
27942831
if (!Template)
27952832
return;
27962833
DeclarationNameInfo NameInfo(
@@ -2803,6 +2840,13 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
28032840
FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(G);
28042841
if (!F)
28052842
continue;
2843+
// The **aggregate** deduction guides are handled in a different code path
2844+
// (DeclareImplicitDeductionGuideFromInitList), which involves the tricky
2845+
// cache.
2846+
if (cast<CXXDeductionGuideDecl>(F->getTemplatedDecl())
2847+
->getDeductionCandidateKind() == DeductionCandidate::Aggregate)
2848+
continue;
2849+
28062850
auto RType = F->getTemplatedDecl()->getReturnType();
28072851
// The (trailing) return type of the deduction guide.
28082852
const TemplateSpecializationType *FReturnType =
@@ -2885,21 +2929,6 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
28852929
// parameters, used for building `TemplateArgsForBuildingFPrime`.
28862930
SmallVector<TemplateArgument, 16> TransformedDeducedAliasArgs(
28872931
AliasTemplate->getTemplateParameters()->size());
2888-
auto TransformTemplateParameter =
2889-
[&SemaRef](DeclContext *DC, NamedDecl *TemplateParam,
2890-
MultiLevelTemplateArgumentList &Args,
2891-
unsigned NewIndex) -> NamedDecl * {
2892-
if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam))
2893-
return transformTemplateTypeParam(SemaRef, DC, TTP, Args,
2894-
TTP->getDepth(), NewIndex);
2895-
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam))
2896-
return transformTemplateParam(SemaRef, DC, TTP, Args, NewIndex,
2897-
TTP->getDepth());
2898-
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(TemplateParam))
2899-
return transformTemplateParam(SemaRef, DC, NTTP, Args, NewIndex,
2900-
NTTP->getDepth());
2901-
return nullptr;
2902-
};
29032932

29042933
for (unsigned AliasTemplateParamIdx : DeducedAliasTemplateParams) {
29052934
auto *TP = AliasTemplate->getTemplateParameters()->getParam(
@@ -2909,9 +2938,9 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
29092938
MultiLevelTemplateArgumentList Args;
29102939
Args.setKind(TemplateSubstitutionKind::Rewrite);
29112940
Args.addOuterTemplateArguments(TransformedDeducedAliasArgs);
2912-
NamedDecl *NewParam =
2913-
TransformTemplateParameter(AliasTemplate->getDeclContext(), TP, Args,
2914-
/*NewIndex*/ FPrimeTemplateParams.size());
2941+
NamedDecl *NewParam = transformTemplateParameter(
2942+
SemaRef, AliasTemplate->getDeclContext(), TP, Args,
2943+
/*NewIndex*/ FPrimeTemplateParams.size());
29152944
FPrimeTemplateParams.push_back(NewParam);
29162945

29172946
auto NewTemplateArgument = Context.getCanonicalTemplateArgument(
@@ -2927,8 +2956,8 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
29272956
// We take a shortcut here, it is ok to reuse the
29282957
// TemplateArgsForBuildingFPrime.
29292958
Args.addOuterTemplateArguments(TemplateArgsForBuildingFPrime);
2930-
NamedDecl *NewParam = TransformTemplateParameter(
2931-
F->getDeclContext(), TP, Args, FPrimeTemplateParams.size());
2959+
NamedDecl *NewParam = transformTemplateParameter(
2960+
SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size());
29322961
FPrimeTemplateParams.push_back(NewParam);
29332962

29342963
assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() &&
@@ -2938,16 +2967,8 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
29382967
Context.getInjectedTemplateArg(NewParam));
29392968
}
29402969
// Substitute new template parameters into requires-clause if present.
2941-
Expr *RequiresClause = nullptr;
2942-
if (Expr *InnerRC = F->getTemplateParameters()->getRequiresClause()) {
2943-
MultiLevelTemplateArgumentList Args;
2944-
Args.setKind(TemplateSubstitutionKind::Rewrite);
2945-
Args.addOuterTemplateArguments(TemplateArgsForBuildingFPrime);
2946-
ExprResult E = SemaRef.SubstExpr(InnerRC, Args);
2947-
if (E.isInvalid())
2948-
return;
2949-
RequiresClause = E.getAs<Expr>();
2950-
}
2970+
Expr *RequiresClause =
2971+
transformRequireClause(SemaRef, F, TemplateArgsForBuildingFPrime);
29512972
// FIXME: implement the is_deducible constraint per C++
29522973
// [over.match.class.deduct]p3.3:
29532974
// ... and a constraint that is satisfied if and only if the arguments
@@ -3013,8 +3034,78 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
30133034
}
30143035
}
30153036

3037+
// Build an aggregate deduction guide for a type alias template.
3038+
FunctionTemplateDecl *DeclareAggrecateDeductionGuideForTypeAlias(
3039+
Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate,
3040+
MutableArrayRef<QualType> ParamTypes, SourceLocation Loc) {
3041+
TemplateDecl *RHSTemplate =
3042+
getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate).first;
3043+
if (!RHSTemplate)
3044+
return nullptr;
3045+
auto *RHSDeductionGuide = SemaRef.DeclareImplicitDeductionGuideFromInitList(
3046+
RHSTemplate, ParamTypes, Loc);
3047+
if (!RHSDeductionGuide)
3048+
return nullptr;
3049+
3050+
LocalInstantiationScope Scope(SemaRef);
3051+
3052+
// Build a new template parameter list for the synthesized aggregate deduction
3053+
// guide by transforming the one from RHSDeductionGuide.
3054+
SmallVector<NamedDecl *> TransformedTemplateParams;
3055+
// Template args that refer to the rebuilt template parameters.
3056+
// All template arguments must be initialized in advance.
3057+
SmallVector<TemplateArgument> TransformedTemplateArgs(
3058+
RHSDeductionGuide->getTemplateParameters()->size());
3059+
for (auto *TP : *RHSDeductionGuide->getTemplateParameters()) {
3060+
// Rebuild any internal references to earlier parameters and reindex as
3061+
// we go.
3062+
MultiLevelTemplateArgumentList Args;
3063+
Args.setKind(TemplateSubstitutionKind::Rewrite);
3064+
Args.addOuterTemplateArguments(TransformedTemplateArgs);
3065+
NamedDecl *NewParam = transformTemplateParameter(
3066+
SemaRef, AliasTemplate->getDeclContext(), TP, Args,
3067+
/*NewIndex=*/TransformedTemplateParams.size());
3068+
3069+
TransformedTemplateArgs[TransformedTemplateParams.size()] =
3070+
SemaRef.Context.getCanonicalTemplateArgument(
3071+
SemaRef.Context.getInjectedTemplateArg(NewParam));
3072+
TransformedTemplateParams.push_back(NewParam);
3073+
}
3074+
// FIXME: implement the is_deducible constraint per C++
3075+
// [over.match.class.deduct]p3.3.
3076+
Expr *TransformedRequiresClause = transformRequireClause(
3077+
SemaRef, RHSDeductionGuide, TransformedTemplateArgs);
3078+
auto *TransformedTemplateParameterList = TemplateParameterList::Create(
3079+
SemaRef.Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
3080+
AliasTemplate->getTemplateParameters()->getLAngleLoc(),
3081+
TransformedTemplateParams,
3082+
AliasTemplate->getTemplateParameters()->getRAngleLoc(),
3083+
TransformedRequiresClause);
3084+
auto *TransformedTemplateArgList = TemplateArgumentList::CreateCopy(
3085+
SemaRef.Context, TransformedTemplateArgs);
3086+
3087+
if (auto *TransformedDeductionGuide = SemaRef.InstantiateFunctionDeclaration(
3088+
RHSDeductionGuide, TransformedTemplateArgList,
3089+
AliasTemplate->getLocation(),
3090+
Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
3091+
auto *GD =
3092+
llvm::dyn_cast<clang::CXXDeductionGuideDecl>(TransformedDeductionGuide);
3093+
FunctionTemplateDecl *Result = buildDeductionGuide(
3094+
SemaRef, AliasTemplate, TransformedTemplateParameterList,
3095+
GD->getCorrespondingConstructor(), GD->getExplicitSpecifier(),
3096+
GD->getTypeSourceInfo(), AliasTemplate->getBeginLoc(),
3097+
AliasTemplate->getLocation(), AliasTemplate->getEndLoc(),
3098+
GD->isImplicit());
3099+
cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl())
3100+
->setDeductionCandidateKind(DeductionCandidate::Aggregate);
3101+
return Result;
3102+
}
3103+
return nullptr;
3104+
}
3105+
30163106
} // namespace
30173107

3108+
// FIXME: rename to DeclareAggrecateDeductionGuide.
30183109
FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList(
30193110
TemplateDecl *Template, MutableArrayRef<QualType> ParamTypes,
30203111
SourceLocation Loc) {
@@ -3023,17 +3114,22 @@ FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList(
30233114
for (auto &T : ParamTypes)
30243115
T.getCanonicalType().Profile(ID);
30253116
unsigned Hash = ID.ComputeHash();
3026-
3117+
30273118
auto Found = AggregateDeductionCandidates.find(Hash);
30283119
if (Found != AggregateDeductionCandidates.end()) {
30293120
CXXDeductionGuideDecl *GD = Found->getSecond();
30303121
return GD->getDescribedFunctionTemplate();
30313122
}
3032-
3033-
// if (auto *AliasTemplate = llvm::dyn_cast<TypeAliasTemplateDecl>(Template)) {
3034-
// DeclareImplicitDeductionGuidesForTypeAlias(*this, AliasTemplate, Loc);
3035-
// return;
3036-
// }
3123+
3124+
if (auto *AliasTemplate = llvm::dyn_cast<TypeAliasTemplateDecl>(Template)) {
3125+
if (auto *FTD = DeclareAggrecateDeductionGuideForTypeAlias(
3126+
*this, AliasTemplate, ParamTypes, Loc)) {
3127+
auto *GD = cast<CXXDeductionGuideDecl>(FTD->getTemplatedDecl());
3128+
GD->setDeductionCandidateKind(DeductionCandidate::Aggregate);
3129+
AggregateDeductionCandidates[Hash] = GD;
3130+
return FTD;
3131+
}
3132+
}
30373133

30383134
if (CXXRecordDecl *DefRecord =
30393135
cast<CXXRecordDecl>(Template->getTemplatedDecl())->getDefinition()) {

clang/test/SemaTemplate/deduction-guide.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,15 @@ G g = {1};
248248
// CHECK: FunctionTemplateDecl
249249
// CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for G> 'auto (T) -> G<T>' aggregate
250250
// CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for G> 'auto (int) -> G<int>' implicit_instantiation aggregate
251+
252+
template<typename X>
253+
using AG = G<X>;
254+
AG ag = {1};
255+
// Verify that the aggregate deduction guide for alias templates is built.
256+
// CHECK-LABEL: Dumping <deduction guide for AG>
257+
// CHECK: FunctionTemplateDecl
258+
// CHECK: |-CXXDeductionGuideDecl {{.*}} 'auto (type-parameter-0-0) -> G<type-parameter-0-0>'
259+
// CHECK: `-CXXDeductionGuideDecl {{.*}} 'auto (int) -> G<int>' implicit_instantiation
260+
// CHECK: |-TemplateArgument type 'int'
261+
// CHECK: | `-BuiltinType {{.*}} 'int'
262+
// CHECK: `-ParmVarDecl {{.*}} 'int'

0 commit comments

Comments
 (0)