[clang] [clang-tools-extra] [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 01:12:01 PST 2026


https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/181105

>From e9ba82815b5e9cdcb3e20573e8359cf92c839ed3 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] [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                   | 11 +++++++--
 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, 48 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..5d75e05bdce66 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13279,8 +13279,15 @@ 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.
+      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>();
+
+}



More information about the cfe-commits mailing list