[clang] [Clang] Fix synthesis of alias CTAD constraints (PR #223340)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 15 02:13:06 PDT 2026
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/223340
>From f7ae9a62e7d82a71be4f2c1c8f153a85b0d2f2a7 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 14 Sep 2026 17:31:25 +0800
Subject: [PATCH 1/3] [Clang] Fix synthesizing of alias CTAD constraints
buildAssociatedConstraints reconstructs a template parameter list relative
to its primary template. However it didn't match the construction of
the CTAD guide very precisely.
---
clang/docs/ReleaseNotes.md | 2 ++
clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 29 ++++++++++++-------
clang/test/SemaCXX/ctad.cpp | 29 +++++++++++++++++++
3 files changed, 50 insertions(+), 10 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 043a0ddae2a6c..be67a1b3779d8 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -579,6 +579,8 @@ features cannot lower the translation-unit ABI level;
- Fixed a crash when a using-declaration naming an unresolvable member of a
dependent base was shadowed by an invalid using-declaration. (#GH209427)
+- Fixed a CTAD bug when combining with concepts. (#GH124715)
+
- Fixed a regression where an internal-linkage function (e.g. a `static` or
anonymous-namespace helper) declared in the global module fragment of the
current translation unit was removed from the overload set when the calling
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 1a488ece55d25..e1000372985fc 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -998,11 +998,19 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
AliasTemplate->getInstantiatedFromMemberTemplate())
AdjustDepth = PrimaryTemplate->getTemplateDepth();
+ // FIXME: We're rebuilding the synthesized template parameter list again
+ // Consider reuse the template parameter from its caller.
+
// We rebuild all template parameters with the uninstantiated depth, and
// build template arguments refer to them.
- SmallVector<TemplateArgument> AdjustedAliasTemplateArgs;
+ SmallVector<TemplateArgument> AdjustedAliasTemplateArgs(
+ AliasTemplate->getTemplateParameters()->size());
- for (auto *TP : *AliasTemplate->getTemplateParameters()) {
+ unsigned N = 0;
+ for (unsigned Index = 0; Index != AdjustedAliasTemplateArgs.size(); ++Index) {
+ if (DeduceResults[Index].isNull())
+ continue;
+ auto *TP = AliasTemplate->getTemplateParameters()->getParam(Index);
// Rebuild any internal references to earlier parameters and reindex
// as we go.
MultiLevelTemplateArgumentList Args;
@@ -1010,21 +1018,22 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
Args.addOuterTemplateArguments(AdjustedAliasTemplateArgs);
NamedDecl *NewParam = transformTemplateParameter(
SemaRef, AliasTemplate->getDeclContext(), TP, Args,
- /*NewIndex=*/AdjustedAliasTemplateArgs.size(),
- getDepthAndIndex(TP).first + AdjustDepth);
+ /*NewIndex=*/N++, getDepthAndIndex(TP).first + AdjustDepth);
TemplateArgument NewTemplateArgument =
Context.getInjectedTemplateArg(NewParam);
- AdjustedAliasTemplateArgs.push_back(NewTemplateArgument);
+ AdjustedAliasTemplateArgs[Index] = NewTemplateArgument;
}
+ assert(FirstUndeducedParamIdx == N);
+
// Template arguments used to transform the template arguments in
// DeducedResults.
SmallVector<TemplateArgument> TemplateArgsForBuildingRC(
F->getTemplateParameters()->size());
// Transform the transformed template args
- MultiLevelTemplateArgumentList Args;
- Args.setKind(TemplateSubstitutionKind::Rewrite);
- Args.addOuterTemplateArguments(AdjustedAliasTemplateArgs);
+ MultiLevelTemplateArgumentList ArgsForDeducedParams;
+ ArgsForDeducedParams.setKind(TemplateSubstitutionKind::Rewrite);
+ ArgsForDeducedParams.addOuterTemplateArguments(AdjustedAliasTemplateArgs);
for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
const auto &D = DeduceResults[Index];
@@ -1047,7 +1056,7 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
TemplateArgumentLoc Input =
SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{});
TemplateArgumentLoc Output;
- if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) {
+ if (!SemaRef.SubstTemplateArgument(Input, ArgsForDeducedParams, Output)) {
assert(TemplateArgsForBuildingRC[Index].isNull() &&
"InstantiatedArgs must be null before setting");
TemplateArgsForBuildingRC[Index] = Output.getArgument();
@@ -1119,7 +1128,7 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
Expr *buildIsDeducibleConstraint(Sema &SemaRef,
TypeAliasTemplateDecl *AliasTemplate,
QualType ReturnType,
- SmallVector<NamedDecl *> TemplateParams) {
+ ArrayRef<NamedDecl *> TemplateParams) {
ASTContext &Context = SemaRef.Context;
// Constraint AST nodes must use uninstantiated depth.
if (auto *PrimaryTemplate =
diff --git a/clang/test/SemaCXX/ctad.cpp b/clang/test/SemaCXX/ctad.cpp
index 2a4e1c571ac5c..6517e88a8a831 100644
--- a/clang/test/SemaCXX/ctad.cpp
+++ b/clang/test/SemaCXX/ctad.cpp
@@ -197,3 +197,32 @@ namespace GH131342 {
template <class T> using AA = A<T, val<T>>;
AA a{0};
} // namespace GH131342
+
+namespace GH124715_2 {
+
+template <class F, class... Args>
+using invoke_result_t = decltype(F()(Args()...));
+
+template <class F, class... Args>
+invoke_result_t<F, Args...> invoke(F f, Args... args);
+
+template <class F, class... Args>
+concept invocable = requires(F f, Args... args) {
+ invoke(f, args...);
+};
+
+template <class Ret, class... Args>
+struct A {
+ A(auto&&...) {}
+};
+
+template <class Lambda, class... Args>
+ requires invocable<Lambda, Args...>
+A(Lambda, Args...) -> A<invoke_result_t<Lambda, Args...>, Args...>;
+
+template <class T, class... Ts>
+using AliasName = A<T, Ts...>;
+
+AliasName aa([](int){}, 0);
+
+}
>From dec9c601da34631af5d8989bcfaed628d7cd2030 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 15 Sep 2026 16:39:52 +0800
Subject: [PATCH 2/3] Remove FIXME
---
clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index e1000372985fc..5f656c01083d2 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -998,9 +998,6 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
AliasTemplate->getInstantiatedFromMemberTemplate())
AdjustDepth = PrimaryTemplate->getTemplateDepth();
- // FIXME: We're rebuilding the synthesized template parameter list again
- // Consider reuse the template parameter from its caller.
-
// We rebuild all template parameters with the uninstantiated depth, and
// build template arguments refer to them.
SmallVector<TemplateArgument> AdjustedAliasTemplateArgs(
>From 8d6208ad2b3d9ff161161b476d8b5b8e8faf1812 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 15 Sep 2026 16:58:27 +0800
Subject: [PATCH 3/3] Really match how BuildDeductionGuideForTypeAlias works
---
clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 51 +++++++++----------
1 file changed, 23 insertions(+), 28 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 5f656c01083d2..fa0918ae30282 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -956,6 +956,19 @@ llvm::DenseSet<const NamedDecl *> getSourceDeductionGuides(DeclarationName Name,
return Result;
}
+bool IsNonDeducedArgument(const TemplateArgument &TA) {
+ // The following cases indicate the template argument is non-deducible:
+ // 1. The result is null. E.g. When it comes from a default template
+ // argument that doesn't appear in the alias declaration.
+ // 2. The template parameter is a pack and that cannot be deduced from
+ // the arguments within the alias declaration.
+ // Non-deducible template parameters will persist in the transformed
+ // deduction guide.
+ return TA.isNull() ||
+ (TA.getKind() == TemplateArgument::Pack &&
+ llvm::any_of(TA.pack_elements(), IsNonDeducedArgument));
+}
+
// Build the associated constraints for the alias deduction guides.
// C++ [over.match.class.deduct]p3.3:
// The associated constraints ([temp.constr.decl]) are the conjunction of the
@@ -968,7 +981,8 @@ Expr *
buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
TypeAliasTemplateDecl *AliasTemplate,
ArrayRef<DeducedTemplateArgument> DeduceResults,
- unsigned FirstUndeducedParamIdx, Expr *IsDeducible) {
+ ArrayRef<unsigned> DeducedAliasTemplateParams,
+ Expr *IsDeducible) {
Expr *RC = F->getTemplateParameters()->getRequiresClause();
if (!RC)
return IsDeducible;
@@ -1004,9 +1018,7 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
AliasTemplate->getTemplateParameters()->size());
unsigned N = 0;
- for (unsigned Index = 0; Index != AdjustedAliasTemplateArgs.size(); ++Index) {
- if (DeduceResults[Index].isNull())
- continue;
+ for (unsigned Index : DeducedAliasTemplateParams) {
auto *TP = AliasTemplate->getTemplateParameters()->getParam(Index);
// Rebuild any internal references to earlier parameters and reindex
// as we go.
@@ -1021,7 +1033,6 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
Context.getInjectedTemplateArg(NewParam);
AdjustedAliasTemplateArgs[Index] = NewTemplateArgument;
}
- assert(FirstUndeducedParamIdx == N);
// Template arguments used to transform the template arguments in
// DeducedResults.
@@ -1034,17 +1045,15 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
const auto &D = DeduceResults[Index];
- if (D.isNull()) { // non-deduced template parameters of f
+ if (IsNonDeducedArgument(D)) { // non-deduced template parameters of f
NamedDecl *TP = F->getTemplateParameters()->getParam(Index);
MultiLevelTemplateArgumentList Args;
Args.setKind(TemplateSubstitutionKind::Rewrite);
Args.addOuterTemplateArguments(TemplateArgsForBuildingRC);
// Rebuild the template parameter with updated depth and index.
- NamedDecl *NewParam =
- transformTemplateParameter(SemaRef, F->getDeclContext(), TP, Args,
- /*NewIndex=*/FirstUndeducedParamIdx,
- getDepthAndIndex(TP).first + AdjustDepth);
- FirstUndeducedParamIdx += 1;
+ NamedDecl *NewParam = transformTemplateParameter(
+ SemaRef, F->getDeclContext(), TP, Args,
+ /*NewIndex=*/N++, getDepthAndIndex(TP).first + AdjustDepth);
assert(TemplateArgsForBuildingRC[Index].isNull());
TemplateArgsForBuildingRC[Index] =
Context.getInjectedTemplateArg(NewParam);
@@ -1203,19 +1212,6 @@ getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) {
return {Template, AliasRhsTemplateArgs};
}
-bool IsNonDeducedArgument(const TemplateArgument &TA) {
- // The following cases indicate the template argument is non-deducible:
- // 1. The result is null. E.g. When it comes from a default template
- // argument that doesn't appear in the alias declaration.
- // 2. The template parameter is a pack and that cannot be deduced from
- // the arguments within the alias declaration.
- // Non-deducible template parameters will persist in the transformed
- // deduction guide.
- return TA.isNull() ||
- (TA.getKind() == TemplateArgument::Pack &&
- llvm::any_of(TA.pack_elements(), IsNonDeducedArgument));
-}
-
// Build deduction guides for a type alias template from the given underlying
// source deduction guide.
CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias(
@@ -1362,7 +1358,6 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias(
Context.getInjectedTemplateArg(NewParam);
TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument;
}
- unsigned FirstUndeducedParamIdx = FPrimeTemplateParams.size();
// To form a deduction guide f' from f, we leverage clang's instantiation
// mechanism, we construct a template argument list where the template
@@ -1453,7 +1448,7 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias(
SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams);
Expr *RequiresClause =
buildAssociatedConstraints(SemaRef, F, AliasTemplate, DeduceResults,
- FirstUndeducedParamIdx, IsDeducible);
+ DeducedAliasTemplateParams, IsDeducible);
TemplateParameterList *FPrimeTemplateParamList = nullptr;
if (!FPrimeTemplateParams.empty())
@@ -1519,11 +1514,11 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
NewParam->setScopeInfo(0, I);
FPTL.setParam(I, NewParam);
}
- auto *Transformed = cast<CXXDeductionGuideDecl>(buildDeductionGuide(
+ auto *Transformed = buildDeductionGuide(
SemaRef, AliasTemplate, /*TemplateParams=*/nullptr,
/*Constructor=*/nullptr, DG->getExplicitSpecifier(), FunctionType,
AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(),
- AliasTemplate->getEndLoc(), DG->isImplicit()));
+ AliasTemplate->getEndLoc(), DG->isImplicit());
Transformed->setSourceDeductionGuide(DG);
Transformed->setSourceDeductionGuideKind(
CXXDeductionGuideDecl::SourceDeductionGuideKind::Alias);
More information about the cfe-commits
mailing list