https://github.com/Qixi-1 updated 
https://github.com/llvm/llvm-project/pull/207644

>From 23b582c5075ffd5fc76b9498b5c425c3f172ad33 Mon Sep 17 00:00:00 2001
From: Qixi-1 <[email protected]>
Date: Mon, 6 Jul 2026 11:30:39 +0800
Subject: [PATCH] [clang][Sema] Fix crash on nested CTAD with
 InjectedClassNameType (#136813)

When generating implicit deduction guides for nested template classes,
transformFunctionTypeParam calls SubstType to substitute outer template
parameters. However, SubstType fails to substitute through
InjectedClassNameType because TreeTransform does not reach
TransformTagType for it in this context, leaving outer template
parameters unsubstituted in the deduction guide parameter type.

Later, MarkUsedTemplateParameters traverses the unsubstituted parameter
type and encounters template parameter indices that exceed the
SmallBitVector size (allocated for the inner template), causing an
out-of-bounds access.

Fix by expanding InjectedClassNameType to its canonical
TemplateSpecializationType before calling SubstType when transforming
outer patterns, so that the outer template parameters can be properly
substituted.

Fixes #136813.
---
 clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 23 +++++++
 ...nested-implicit-deduction-guides-crash.cpp | 64 +++++++++++++++++++
 2 files changed, 87 insertions(+)
 create mode 100644 
clang/test/SemaTemplate/nested-implicit-deduction-guides-crash.cpp

diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp 
b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 15b6aea749584..2a413439f1e65 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -152,6 +152,28 @@ class ExtractTypeForDeductionGuide
         Keyword, TemplateName(NewTATD), TemplateNameLoc, TemplateArgs);
   }
 
+  QualType TransformInjectedClassNameType(TypeLocBuilder &TLB,
+                                          InjectedClassNameTypeLoc TL) {
+    if (!OuterInstantiationArgs)
+      return Base::TransformInjectedClassNameType(TLB, TL);
+
+    auto *ICN = TL.getTypePtr();
+    ASTContext &Ctx = SemaRef.getASTContext();
+
+    // Expand InjectedClassNameType to TemplateSpecializationType (e.g. C<T,U>)
+    QualType TST = ICN->getDecl()->getCanonicalTemplateSpecializationType(Ctx);
+
+    // Substitute outer template parameters (e.g. T->int, U->int)
+    TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(TST, TL.getNameLoc());
+    TypeSourceInfo *SubstTSI = SemaRef.SubstType(
+        TSI, *OuterInstantiationArgs, TL.getNameLoc(), DeclarationName());
+    if (!SubstTSI)
+      return QualType();
+
+    TLB.pushFullCopy(SubstTSI->getTypeLoc());
+    return SubstTSI->getType();
+  }
+
   QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) {
     ASTContext &Context = SemaRef.getASTContext();
     TypedefNameDecl *OrigDecl = TL.getDecl();
@@ -839,6 +861,7 @@ struct ConvertConstructorToDeductionGuideTransform {
     } else
       NewTSI = SemaRef.SubstType(OldTSI, Args, OldParam->getLocation(),
                                  OldParam->getDeclName());
+
     if (!NewTSI)
       return nullptr;
 
diff --git a/clang/test/SemaTemplate/nested-implicit-deduction-guides-crash.cpp 
b/clang/test/SemaTemplate/nested-implicit-deduction-guides-crash.cpp
new file mode 100644
index 0000000000000..687858ab396d4
--- /dev/null
+++ b/clang/test/SemaTemplate/nested-implicit-deduction-guides-crash.cpp
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+// Original crash case: CTAD fails gracefully (no crash), error expected.
+template <typename, typename>
+class C {
+public:
+    template <typename>
+    class C2 { // expected-note {{candidate template ignored}} \
+               // expected-note {{implicit deduction guide declared as}}
+    public:
+        C2(C &) {} // expected-note {{candidate template ignored}} \
+                    // expected-note {{implicit deduction guide declared as}}
+    };
+    void f() {
+        C2(*this); // expected-error {{no viable constructor or deduction 
guide}}
+    }
+};
+void test_original() {
+    C<int, int>().f(); // expected-note {{in instantiation of member function}}
+}
+
+// Success cases: U is deducible from constructor args.
+namespace DeducibleCases {
+
+// Direct
+template <class T> struct Outer1 {
+  template <class U> struct Inner {
+    Inner(Outer1, U) {}
+  };
+  void f() { Inner(*this, 42); }
+};
+
+// Pointer
+template <class T> struct Outer2 {
+  template <class U> struct Inner {
+    Inner(Outer2*, U) {}
+  };
+  void f() { Inner(this, 42); }
+};
+
+// Const lvalue reference
+template <class T> struct Outer3 {
+  template <class U> struct Inner {
+    Inner(const Outer3&, U) {}
+  };
+  void f() { Inner(*this, 42); }
+};
+
+// Rvalue reference
+template <class T> struct Outer4 {
+  template <class U> struct Inner {
+    Inner(Outer4&&, U) {}
+  };
+  void f() { Inner(Outer4{}, 42); }
+};
+
+void test() {
+  Outer1<int>().f();
+  Outer2<int>().f();
+  Outer3<int>().f();
+  Outer4<int>().f();
+}
+
+} // namespace DeducibleCases

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

Reply via email to