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 <[email protected]>
Date: Thu, 12 Feb 2026 16:28:30 +0800
Subject: [PATCH 1/2] [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.
+// [email protected]:* 
{{'Types::DeducedTemplateSpecialization::invalid1' has different definitions in 
different modules; definition in module 'SecondModule' first difference is 
function body}}
+// [email protected]:* {{but in 'FirstModule' found a different body}}
 auto function2 = invalid2;
 // [email protected]:* 
{{'Types::DeducedTemplateSpecialization::invalid2' has different definitions in 
different modules; definition in module 'SecondModule' first difference is 
function body}}
 // [email protected]:* {{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 <[email protected]>
Date: Thu, 12 Feb 2026 17:31:16 +0800
Subject: [PATCH 2/2] 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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to