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

>From f5e826cebd22588460984cdec0fcd9dd0c8028f1 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

When generating implicit deduction guides for a nested class template,
an InjectedClassNameType in a constructor parameter can retain template
parameters from the enclosing class.

Expand the injected-class-name to its canonical
TemplateSpecializationType and substitute the outer instantiation
arguments before rebuilding the deduction guide type.

This prevents dangling outer template parameters from reaching
MarkUsedTemplateParameters and causing an out-of-bounds SmallBitVector
access.

Fixes #136813.

Assisted-by: ChatGPT (OpenAI)
---
 clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 23 +++++++
 clang/test/SemaTemplate/GH136813.cpp          | 64 +++++++++++++++++++
 2 files changed, 87 insertions(+)
 create mode 100644 clang/test/SemaTemplate/GH136813.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/GH136813.cpp 
b/clang/test/SemaTemplate/GH136813.cpp
new file mode 100644
index 0000000000000..687858ab396d4
--- /dev/null
+++ b/clang/test/SemaTemplate/GH136813.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