[clang] [clang-tools-extra] [libcxx] [Clang] Keep the deduced TSI in sync with its DeducedTemplateSpecializationType (PR #181105)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 12 02:21:11 PST 2026
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/181105
>From 0f1d4e384e6e326a6bc16e0352c32e7937a0cd62 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 12 Feb 2026 16:28:30 +0800
Subject: [PATCH 1/3] [Clang] Keep the deduced TSI in sync with its
DeducedTemplateSpecializationType
The deduced TSI of CTAD was not tracked in VarDecl, creating an
inconsistency with the deduced type. This inconsistency confused the
template instantiator.
We now propagate the TSI up from DeduceTemplateSpecializationFromInitializer,
allowing its caller to update the VarDecl accordingly. Ideally deduceVarTypeFromInitializer
should propagate that too, but that involves refactoring of DeduceAutoType
and it isn't currently necessary to DeducedTemplateSpecializationType.
Also this happened to resolve a FIXME from module's test.
---
.../include-cleaner/unittests/WalkASTTest.cpp | 6 ++---
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/Sema/SemaDecl.cpp | 12 ++++++++--
clang/lib/Sema/SemaExprCXX.cpp | 8 ++++---
clang/lib/Sema/SemaInit.cpp | 24 +++++++++----------
clang/lib/Sema/SemaTemplate.cpp | 3 ++-
clang/test/Modules/odr_hash.cpp | 3 ++-
clang/test/SemaCXX/ctad.cpp | 14 +++++++++++
9 files changed, 49 insertions(+), 24 deletions(-)
diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
index 3487f24f2af8f..68a3a20045f6c 100644
--- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
@@ -171,11 +171,9 @@ TEST(WalkAST, ClassTemplates) {
template struct Foo<int>;)cpp",
"^Foo<int> x;"),
ElementsAre(Decl::CXXRecord));
- // FIXME: This is broken due to
- // https://github.com/llvm/llvm-project/issues/42259.
EXPECT_THAT(testWalk(R"cpp(
- template<typename T> struct $explicit^Foo { Foo(T); };
- template<> struct Foo<int> { Foo(int); };)cpp",
+ template<typename T> struct Foo { Foo(T); };
+ template<> struct $explicit^Foo<int> { Foo(int); };)cpp",
"^Foo x(3);"),
ElementsAre(Decl::ClassTemplate));
}
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7dc6881ed43e6..de55659645b5c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -235,6 +235,7 @@ Bug Fixes to C++ Support
^^^^^^^^^^^^^^^^^^^^^^^^
- Fixed a crash when instantiating ``requires`` expressions involving substitution failures in C++ concepts. (#GH176402)
- Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639)
+- Fixed a type deduction bug where CTAD didn't apply to designated initializers. (#GH178879)
- Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
Bug Fixes to AST Handling
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 0ba3daab764b7..efcdc994ecbc7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9077,7 +9077,7 @@ class Sema final : public SemaBase {
bool TopLevelOfInitList = false,
bool AllowExplicit = false);
- QualType DeduceTemplateSpecializationFromInitializer(
+ TypeSourceInfo *DeduceTemplateSpecializationFromInitializer(
TypeSourceInfo *TInfo, const InitializedEntity &Entity,
const InitializationKind &Kind, MultiExprArg Init);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 3b2c93b9fe7b5..e317c104d83e4 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13279,8 +13279,16 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl,
VDecl->getLocation(), DirectInit, Init);
// FIXME: Initialization should not be taking a mutable list of inits.
SmallVector<Expr *, 8> InitsCopy(DeduceInits);
- return DeduceTemplateSpecializationFromInitializer(TSI, Entity, Kind,
- InitsCopy);
+ TypeSourceInfo *DeducedTSI = DeduceTemplateSpecializationFromInitializer(
+ TSI, Entity, Kind, InitsCopy);
+ if (DeducedTSI) {
+ // DeduceVariableDeclarationType will update the type from DeducedTSI.
+ // FIXME: Propagate TSI up, but it's difficult for DeduceAutoType.
+ // See https://github.com/llvm/llvm-project/issues/42259
+ VDecl->setTypeSourceInfo(DeducedTSI);
+ return DeducedTSI->getType();
+ }
+ return QualType();
}
if (DirectInit) {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 91967a7a9ff97..cb231ab4ecf86 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1549,8 +1549,9 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
DeducedType *Deduced = Ty->getContainedDeducedType();
if (Deduced && !Deduced->isDeduced() &&
isa<DeducedTemplateSpecializationType>(Deduced)) {
- Ty = DeduceTemplateSpecializationFromInitializer(TInfo, Entity,
- Kind, Exprs);
+ TypeSourceInfo *TSI =
+ DeduceTemplateSpecializationFromInitializer(TInfo, Entity, Kind, Exprs);
+ Ty = TSI ? TSI->getType() : QualType();
if (Ty.isNull())
return ExprError();
Entity = InitializedEntity::InitializeTemporary(TInfo, Ty);
@@ -2208,8 +2209,9 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
InitializedEntity Entity
= InitializedEntity::InitializeNew(StartLoc, AllocType);
- AllocType = DeduceTemplateSpecializationFromInitializer(
+ TypeSourceInfo *TSI = DeduceTemplateSpecializationFromInitializer(
AllocTypeInfo, Entity, Kind, Exprs);
+ AllocType = TSI ? TSI->getType() : QualType();
if (AllocType.isNull())
return ExprError();
} else if (Deduced && !Deduced->isDeduced()) {
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index ff278bc7471bd..fa5fbfccf98f1 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10062,7 +10062,7 @@ static bool isOrIsDerivedFromSpecializationOf(CXXRecordDecl *RD,
return !(NotSpecialization(RD) && RD->forallBases(NotSpecialization));
}
-QualType Sema::DeduceTemplateSpecializationFromInitializer(
+TypeSourceInfo *Sema::DeduceTemplateSpecializationFromInitializer(
TypeSourceInfo *TSInfo, const InitializedEntity &Entity,
const InitializationKind &Kind, MultiExprArg Inits) {
auto *DeducedTST = dyn_cast<DeducedTemplateSpecializationType>(
@@ -10071,7 +10071,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
auto TemplateName = DeducedTST->getTemplateName();
if (TemplateName.isDependent())
- return SubstAutoTypeSourceInfoDependent(TSInfo)->getType();
+ return SubstAutoTypeSourceInfoDependent(TSInfo);
// We can only perform deduction for class templates or alias templates.
auto *Template =
@@ -10108,7 +10108,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
<< (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName;
if (auto *TD = TemplateName.getAsTemplateDecl())
NoteTemplateLocation(*TD);
- return QualType();
+ return nullptr;
}
// Can't deduce from dependent arguments.
@@ -10116,7 +10116,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
Diag(TSInfo->getTypeLoc().getBeginLoc(),
diag::warn_cxx14_compat_class_template_argument_deduction)
<< TSInfo->getTypeLoc().getSourceRange() << 0;
- return SubstAutoTypeSourceInfoDependent(TSInfo)->getType();
+ return SubstAutoTypeSourceInfoDependent(TSInfo);
}
// FIXME: Perform "exact type" matching first, per CWG discussion?
@@ -10381,7 +10381,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
PDiag(diag::err_deduced_class_template_ctor_ambiguous)
<< TemplateName),
*this, OCD_AmbiguousCandidates, Inits);
- return QualType();
+ return nullptr;
case OR_No_Viable_Function: {
CXXRecordDecl *Primary =
@@ -10395,7 +10395,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
: diag::err_deduced_class_template_incomplete)
<< TemplateName << !Guides.empty()),
*this, OCD_AllCandidates, Inits);
- return QualType();
+ return nullptr;
}
case OR_Deleted: {
@@ -10405,7 +10405,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
Diag(Kind.getLocation(), diag::err_deduced_class_template_deleted)
<< TemplateName;
NoteDeletedFunction(Best->Function);
- return QualType();
+ return nullptr;
}
case OR_Success:
@@ -10420,7 +10420,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
Diag(Best->Function->getLocation(),
diag::note_explicit_ctor_deduction_guide_here)
<< IsDeductionGuide;
- return QualType();
+ return nullptr;
}
// Make sure we didn't select an unusable deduction guide, and mark it
@@ -10433,9 +10433,9 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// C++ [dcl.type.class.deduct]p1:
// The placeholder is replaced by the return type of the function selected
// by overload resolution for class template deduction.
- QualType DeducedType =
- SubstAutoTypeSourceInfo(TSInfo, Best->Function->getReturnType())
- ->getType();
+ TypeSourceInfo *DeducedTSI =
+ SubstAutoTypeSourceInfo(TSInfo, Best->Function->getReturnType());
+ QualType DeducedType = DeducedTSI ? DeducedTSI->getType() : QualType();
Diag(TSInfo->getTypeLoc().getBeginLoc(),
diag::warn_cxx14_compat_class_template_argument_deduction)
<< TSInfo->getTypeLoc().getSourceRange() << 1 << DeducedType;
@@ -10449,5 +10449,5 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
Diag(Template->getLocation(), diag::note_suppress_ctad_maybe_unsupported);
}
- return DeducedType;
+ return DeducedTSI;
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 3497ff7856eed..4475e6d490218 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -7145,8 +7145,9 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
InitializationKind Kind = InitializationKind::CreateForInit(
DeductionArg->getBeginLoc(), /*DirectInit*/false, DeductionArg);
Expr *Inits[1] = {DeductionArg};
- ParamType =
+ auto *DeducedTSI =
DeduceTemplateSpecializationFromInitializer(TSI, Entity, Kind, Inits);
+ ParamType = DeducedTSI ? DeducedTSI->getType() : QualType();
if (ParamType.isNull())
return ExprError();
} else {
diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp
index f22f3c71f44d2..daf229a2176fe 100644
--- a/clang/test/Modules/odr_hash.cpp
+++ b/clang/test/Modules/odr_hash.cpp
@@ -3883,7 +3883,8 @@ void valid() {
}
#else
auto function1 = invalid1;
-// FIXME: We should reject the merge of `invalid1` due to the inconsistent definition.
+// expected-error at second.h:* {{'Types::DeducedTemplateSpecialization::invalid1' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note at first.h:* {{but in 'FirstModule' found a different body}}
auto function2 = invalid2;
// expected-error at second.h:* {{'Types::DeducedTemplateSpecialization::invalid2' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
// expected-note at first.h:* {{but in 'FirstModule' found a different body}}
diff --git a/clang/test/SemaCXX/ctad.cpp b/clang/test/SemaCXX/ctad.cpp
index 7de7f50337e8c..91173f7331255 100644
--- a/clang/test/SemaCXX/ctad.cpp
+++ b/clang/test/SemaCXX/ctad.cpp
@@ -197,3 +197,17 @@ namespace GH131342 {
template <class T> using AA = A<T, val<T>>;
AA a{0};
} // namespace GH131342
+
+namespace GH178879 {
+
+template <typename T> struct A {
+ T i, j;
+};
+
+template <typename T> void foo() {
+ A a = {.i = 1, .j = 3};
+}
+
+template void foo<int>();
+
+}
>From d1723f0a1eb8f7c227f6cd535fc6fc9b83c9e7bc Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 12 Feb 2026 17:31:16 +0800
Subject: [PATCH 2/3] Fix tests
---
clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
index 68a3a20045f6c..d5ee81b59e597 100644
--- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
@@ -175,7 +175,7 @@ TEST(WalkAST, ClassTemplates) {
template<typename T> struct Foo { Foo(T); };
template<> struct $explicit^Foo<int> { Foo(int); };)cpp",
"^Foo x(3);"),
- ElementsAre(Decl::ClassTemplate));
+ ElementsAre(Decl::ClassTemplateSpecialization));
}
TEST(WalkAST, VarTemplates) {
// Explicit instantiation and (partial) specialization references primary
>From 8ce4040fd2ffb8d8dc7a01642f9fd26327f33b58 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 12 Feb 2026 18:20:47 +0800
Subject: [PATCH 3/3] Fix libc++ test
---
.../set.intersection/set_intersection_complexity.pass.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/set_intersection_complexity.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/set_intersection_complexity.pass.cpp
index ddf4087ddd6cd..ce91a4e20eff8 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/set_intersection_complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/set_intersection_complexity.pass.cpp
@@ -88,9 +88,6 @@ struct counted_set_intersection_result {
}
};
-template <std::size_t ResultSize>
-counted_set_intersection_result(std::array<int, ResultSize>) -> counted_set_intersection_result<ResultSize>;
-
template <template <class...> class InIterType1,
template <class...>
class InIterType2,
More information about the cfe-commits
mailing list