Skip to content

Commit f11b878

Browse files
committed
[clang] CTAD: build aggregate deduction guides for alias templates.
Fixes #85767.
1 parent 99f4e99 commit f11b878

File tree

3 files changed

+161
-51
lines changed

3 files changed

+161
-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: 147 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2755,23 +2755,42 @@ bool hasDeclaredDeductionGuides(DeclarationName Name, DeclContext *DC) {
27552755
return false;
27562756
}
27572757

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

29052934
for (unsigned AliasTemplateParamIdx : DeducedAliasTemplateParams) {
29062935
auto *TP = AliasTemplate->getTemplateParameters()->getParam(
@@ -2910,9 +2939,9 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
29102939
MultiLevelTemplateArgumentList Args;
29112940
Args.setKind(TemplateSubstitutionKind::Rewrite);
29122941
Args.addOuterTemplateArguments(TransformedDeducedAliasArgs);
2913-
NamedDecl *NewParam =
2914-
TransformTemplateParameter(AliasTemplate->getDeclContext(), TP, Args,
2915-
/*NewIndex*/ FPrimeTemplateParams.size());
2942+
NamedDecl *NewParam = transformTemplateParameter(
2943+
SemaRef, AliasTemplate->getDeclContext(), TP, Args,
2944+
/*NewIndex*/ FPrimeTemplateParams.size());
29162945
FPrimeTemplateParams.push_back(NewParam);
29172946

29182947
auto NewTemplateArgument = Context.getCanonicalTemplateArgument(
@@ -2928,8 +2957,8 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
29282957
// We take a shortcut here, it is ok to reuse the
29292958
// TemplateArgsForBuildingFPrime.
29302959
Args.addOuterTemplateArguments(TemplateArgsForBuildingFPrime);
2931-
NamedDecl *NewParam = TransformTemplateParameter(
2932-
F->getDeclContext(), TP, Args, FPrimeTemplateParams.size());
2960+
NamedDecl *NewParam = transformTemplateParameter(
2961+
SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size());
29332962
FPrimeTemplateParams.push_back(NewParam);
29342963

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

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

3110+
// FIXME: rename to DeclareAggrecateDeductionGuide.
30193111
FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList(
30203112
TemplateDecl *Template, MutableArrayRef<QualType> ParamTypes,
30213113
SourceLocation Loc) {
@@ -3024,17 +3116,22 @@ FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList(
30243116
for (auto &T : ParamTypes)
30253117
T.getCanonicalType().Profile(ID);
30263118
unsigned Hash = ID.ComputeHash();
3027-
3119+
30283120
auto Found = AggregateDeductionCandidates.find(Hash);
30293121
if (Found != AggregateDeductionCandidates.end()) {
30303122
CXXDeductionGuideDecl *GD = Found->getSecond();
30313123
return GD->getDescribedFunctionTemplate();
30323124
}
3033-
3034-
// if (auto *AliasTemplate = llvm::dyn_cast<TypeAliasTemplateDecl>(Template)) {
3035-
// DeclareImplicitDeductionGuidesForTypeAlias(*this, AliasTemplate, Loc);
3036-
// return;
3037-
// }
3125+
3126+
if (auto *AliasTemplate = llvm::dyn_cast<TypeAliasTemplateDecl>(Template)) {
3127+
if (auto *FTD = DeclareAggrecateDeductionGuideForTypeAlias(
3128+
*this, AliasTemplate, ParamTypes, Loc)) {
3129+
auto *GD = cast<CXXDeductionGuideDecl>(FTD->getTemplatedDecl());
3130+
GD->setDeductionCandidateKind(DeductionCandidate::Aggregate);
3131+
AggregateDeductionCandidates[Hash] = GD;
3132+
return FTD;
3133+
}
3134+
}
30383135

30393136
if (CXXRecordDecl *DefRecord =
30403137
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)