[clang] [clang] CTAD: fix the aggregate deduction guide for alias templates. (PR #90894)
via cfe-commits
cfe-commits at lists.llvm.org
Thu May 2 12:16:52 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Haojian Wu (hokein)
<details>
<summary>Changes</summary>
For alias templates, our current way of constructing their aggregate deduction guides deviates from the standard approach. We should align it with how we handle implicit deduction guides.
This patch has a refactoring change which pulls the construction logic out from `DeclareImplicitDeductionGuidesForTypeAlia` and reusing it for building aggregate deduction guides.
---
Patch is 24.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/90894.diff
3 Files Affected:
- (modified) clang/lib/Sema/SemaTemplate.cpp (+205-246)
- (modified) clang/test/SemaCXX/cxx20-ctad-type-alias.cpp (+14)
- (modified) clang/test/SemaTemplate/deduction-guide.cpp (+8-1)
``````````diff
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 989f3995ca5991..c3dbb0d58e5bb5 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2803,7 +2803,207 @@ getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) {
return {Template, AliasRhsTemplateArgs};
}
-// Build deduction guides for a type alias template.
+// Build deduction guides for a type alias template from the given underlying
+// deduction guide F.
+FunctionTemplateDecl *
+BuildDeductionGuideForTypeAlias(Sema &SemaRef,
+ TypeAliasTemplateDecl *AliasTemplate,
+ FunctionTemplateDecl *F, SourceLocation Loc) {
+ LocalInstantiationScope Scope(SemaRef);
+ Sema::InstantiatingTemplate BuildingDeductionGuides(
+ SemaRef, AliasTemplate->getLocation(), F,
+ Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{});
+ if (BuildingDeductionGuides.isInvalid())
+ return nullptr;
+
+ auto &Context = SemaRef.Context;
+ auto [Template, AliasRhsTemplateArgs] =
+ getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate);
+
+ 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 && "expected to see a return type");
+ // 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 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,
+ /*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 =
+ TemplateParamsReferencedInTemplateArgumentList(
+ AliasTemplate->getTemplateParameters()->asArray(), DeducedArgs);
+ // All template arguments null by default.
+ SmallVector<TemplateArgument> TemplateArgsForBuildingFPrime(
+ F->getTemplateParameters()->size());
+
+ // 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());
+
+ 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(
+ SemaRef, 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(
+ SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size());
+ FPrimeTemplateParams.push_back(NewParam);
+
+ assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() &&
+ "The argument must be null before setting");
+ TemplateArgsForBuildingFPrime[FTemplateParamIdx] =
+ Context.getCanonicalTemplateArgument(
+ Context.getInjectedTemplateArg(NewParam));
+ }
+
+ // 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() &&
+ "template arguments for non-deduced template parameters should "
+ "be been set!");
+ 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 = cast<CXXDeductionGuideDecl>(FPrime);
+
+ Expr *RequiresClause =
+ transformRequireClause(SemaRef, F, TemplateArgsForBuildingFPrime);
+
+ // FIXME: implement the is_deducible constraint per C++
+ // [over.match.class.deduct]p3.3:
+ // ... and a constraint that is satisfied if and only if the arguments
+ // of A are deducible (see below) from the return type.
+ auto *FPrimeTemplateParamList = TemplateParameterList::Create(
+ Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
+ AliasTemplate->getTemplateParameters()->getLAngleLoc(),
+ FPrimeTemplateParams,
+ AliasTemplate->getTemplateParameters()->getRAngleLoc(),
+ /*RequiresClause=*/RequiresClause);
+ FunctionTemplateDecl *Result = buildDeductionGuide(
+ SemaRef, AliasTemplate, FPrimeTemplateParamList,
+ GG->getCorrespondingConstructor(), GG->getExplicitSpecifier(),
+ GG->getTypeSourceInfo(), AliasTemplate->getBeginLoc(),
+ AliasTemplate->getLocation(), AliasTemplate->getEndLoc(),
+ F->isImplicit());
+ cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl())
+ ->setDeductionCandidateKind(GG->getDeductionCandidateKind());
+ return Result;
+ }
+ return nullptr;
+}
+
void DeclareImplicitDeductionGuidesForTypeAlias(
Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc) {
if (AliasTemplate->isInvalidDecl())
@@ -2831,197 +3031,13 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
if (!F)
continue;
// The **aggregate** deduction guides are handled in a different code path
- // (DeclareImplicitDeductionGuideFromInitList), which involves the tricky
+ // (DeclareAggregateDeductionGuideFromInitList), which involves the tricky
// cache.
if (cast<CXXDeductionGuideDecl>(F->getTemplatedDecl())
->getDeductionCandidateKind() == DeductionCandidate::Aggregate)
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 && "expected to see a return type");
- // 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 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,
- /*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 =
- TemplateParamsReferencedInTemplateArgumentList(
- AliasTemplate->getTemplateParameters()->asArray(), DeducedArgs);
- // 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());
-
- 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(
- SemaRef, 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(
- SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size());
- FPrimeTemplateParams.push_back(NewParam);
-
- assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() &&
- "The argument must be null before setting");
- TemplateArgsForBuildingFPrime[FTemplateParamIdx] =
- Context.getCanonicalTemplateArgument(
- Context.getInjectedTemplateArg(NewParam));
- }
-
- // 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() &&
- "template arguments for non-deduced template parameters should "
- "be been set!");
- 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 = cast<CXXDeductionGuideDecl>(FPrime);
- // Substitute new template parameters into requires-clause if present.
- Expr *RequiresClause =
- transformRequireClause(SemaRef, F, TemplateArgsForBuildingFPrime);
- // FIXME: implement the is_deducible constraint per C++
- // [over.match.class.deduct]p3.3:
- // ... and a constraint that is satisfied if and only if the arguments
- // of A are deducible (see below) from the return type.
- auto *FPrimeTemplateParamList = TemplateParameterList::Create(
- Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
- AliasTemplate->getTemplateParameters()->getLAngleLoc(),
- FPrimeTemplateParams,
- AliasTemplate->getTemplateParameters()->getRAngleLoc(),
- /*RequiresClause=*/RequiresClause);
-
- buildDeductionGuide(SemaRef, AliasTemplate, FPrimeTemplateParamList,
- GG->getCorrespondingConstructor(),
- GG->getExplicitSpecifier(), GG->getTypeSourceInfo(),
- AliasTemplate->getBeginLoc(),
- AliasTemplate->getLocation(),
- AliasTemplate->getEndLoc(), F->isImplicit());
- }
+ BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, F, Loc);
}
}
@@ -3038,65 +3054,8 @@ FunctionTemplateDecl *...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/90894
More information about the cfe-commits
mailing list