https://github.com/Qixi-1 updated https://github.com/llvm/llvm-project/pull/207644
>From e4a2f6ae2c4ff53763a71c74fe8f733a3f92a536 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..9a8b47cdea2a7 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
