Skip to content

Commit 5e77dfe

Browse files
authored
[clang] CTAD: build aggregate deduction guides for alias templates. (#85904)
Fixes #85767. The aggregate deduction guides are handled in a separate code path. We don't generate dedicated aggregate deduction guides for alias templates (we just reuse the ones from the underlying template decl by accident). The patch fixes this incorrect issue. Note: there is a small refactoring change in this PR, where we move the cache logic from `Sema::DeduceTemplateSpecializationFromInitializer` to `Sema::DeclareImplicitDeductionGuideFromInitList`
1 parent 43ba568 commit 5e77dfe

File tree

4 files changed

+182
-70
lines changed

4 files changed

+182
-70
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9713,7 +9713,8 @@ class Sema final {
97139713
/// not already done so.
97149714
void DeclareImplicitDeductionGuides(TemplateDecl *Template,
97159715
SourceLocation Loc);
9716-
FunctionTemplateDecl *DeclareImplicitDeductionGuideFromInitList(
9716+
9717+
FunctionTemplateDecl *DeclareAggregateDeductionGuideFromInitList(
97179718
TemplateDecl *Template, MutableArrayRef<QualType> ParamTypes,
97189719
SourceLocation Loc);
97199720

clang/lib/Sema/SemaInit.cpp

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10930,32 +10930,16 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
1093010930
Context.getLValueReferenceType(ElementTypes[I].withConst());
1093110931
}
1093210932

10933-
llvm::FoldingSetNodeID ID;
10934-
ID.AddPointer(Template);
10935-
for (auto &T : ElementTypes)
10936-
T.getCanonicalType().Profile(ID);
10937-
unsigned Hash = ID.ComputeHash();
10938-
if (AggregateDeductionCandidates.count(Hash) == 0) {
10939-
if (FunctionTemplateDecl *TD =
10940-
DeclareImplicitDeductionGuideFromInitList(
10941-
Template, ElementTypes,
10942-
TSInfo->getTypeLoc().getEndLoc())) {
10943-
auto *GD = cast<CXXDeductionGuideDecl>(TD->getTemplatedDecl());
10944-
GD->setDeductionCandidateKind(DeductionCandidate::Aggregate);
10945-
AggregateDeductionCandidates[Hash] = GD;
10946-
addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public),
10947-
OnlyListConstructors,
10948-
/*AllowAggregateDeductionCandidate=*/true);
10949-
}
10950-
} else {
10951-
CXXDeductionGuideDecl *GD = AggregateDeductionCandidates[Hash];
10952-
FunctionTemplateDecl *TD = GD->getDescribedFunctionTemplate();
10953-
assert(TD && "aggregate deduction candidate is function template");
10933+
if (FunctionTemplateDecl *TD =
10934+
DeclareAggregateDeductionGuideFromInitList(
10935+
LookupTemplateDecl, ElementTypes,
10936+
TSInfo->getTypeLoc().getEndLoc())) {
10937+
auto *GD = cast<CXXDeductionGuideDecl>(TD->getTemplatedDecl());
1095410938
addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public),
1095510939
OnlyListConstructors,
1095610940
/*AllowAggregateDeductionCandidate=*/true);
10941+
HasAnyDeductionGuide = true;
1095710942
}
10958-
HasAnyDeductionGuide = true;
1095910943
}
1096010944
};
1096110945

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 162 additions & 47 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+
llvm_unreachable("Unhandled template parameter types");
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,11 +3034,102 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
30133034
}
30143035
}
30153036

3037+
// Build an aggregate deduction guide for a type alias template.
3038+
FunctionTemplateDecl *DeclareAggregateDeductionGuideForTypeAlias(
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.DeclareAggregateDeductionGuideFromInitList(
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

3018-
FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList(
3108+
FunctionTemplateDecl *Sema::DeclareAggregateDeductionGuideFromInitList(
30193109
TemplateDecl *Template, MutableArrayRef<QualType> ParamTypes,
30203110
SourceLocation Loc) {
3111+
llvm::FoldingSetNodeID ID;
3112+
ID.AddPointer(Template);
3113+
for (auto &T : ParamTypes)
3114+
T.getCanonicalType().Profile(ID);
3115+
unsigned Hash = ID.ComputeHash();
3116+
3117+
auto Found = AggregateDeductionCandidates.find(Hash);
3118+
if (Found != AggregateDeductionCandidates.end()) {
3119+
CXXDeductionGuideDecl *GD = Found->getSecond();
3120+
return GD->getDescribedFunctionTemplate();
3121+
}
3122+
3123+
if (auto *AliasTemplate = llvm::dyn_cast<TypeAliasTemplateDecl>(Template)) {
3124+
if (auto *FTD = DeclareAggregateDeductionGuideForTypeAlias(
3125+
*this, AliasTemplate, ParamTypes, Loc)) {
3126+
auto *GD = cast<CXXDeductionGuideDecl>(FTD->getTemplatedDecl());
3127+
GD->setDeductionCandidateKind(DeductionCandidate::Aggregate);
3128+
AggregateDeductionCandidates[Hash] = GD;
3129+
return FTD;
3130+
}
3131+
}
3132+
30213133
if (CXXRecordDecl *DefRecord =
30223134
cast<CXXRecordDecl>(Template->getTemplatedDecl())->getDefinition()) {
30233135
if (TemplateDecl *DescribedTemplate =
@@ -3050,10 +3162,13 @@ FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList(
30503162
Transform.NestedPattern ? Transform.NestedPattern : Transform.Template;
30513163
ContextRAII SavedContext(*this, Pattern->getTemplatedDecl());
30523164

3053-
auto *DG = cast<FunctionTemplateDecl>(
3165+
auto *FTD = cast<FunctionTemplateDecl>(
30543166
Transform.buildSimpleDeductionGuide(ParamTypes));
30553167
SavedContext.pop();
3056-
return DG;
3168+
auto *GD = cast<CXXDeductionGuideDecl>(FTD->getTemplatedDecl());
3169+
GD->setDeductionCandidateKind(DeductionCandidate::Aggregate);
3170+
AggregateDeductionCandidates[Hash] = GD;
3171+
return FTD;
30573172
}
30583173

30593174
void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,

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)