https://github.com/daniel-petrovic created 
https://github.com/llvm/llvm-project/pull/227214

TransformTemplateParmRefExpr was asserting on argument being expression, but a 
non-type template parameter that defaults to another non-type template 
parameter can be canonicalized into a non-expression argument, which caused the 
crash. Rebuild the expression from the canonical argument instead.

Fixes #227007.

>From d88b100d7acce3cba266e5529c7e10cc76f336da Mon Sep 17 00:00:00 2001
From: Daniel Petrovic <[email protected]>
Date: Tue, 29 Sep 2026 10:23:50 +0200
Subject: [PATCH] [clang] Fix crash/assert for NTTP defaults in CTAD

TransformTemplateParmRefExpr was asserting on argument being expression,
but a non-type template parameter that defaults to another non-type template
parameter can be canonicalized into a non-expression argument, which
caused the crash. Rebuild the expression from the canonical argument instead.

Fixes #227007.
---
 clang/docs/ReleaseNotes.md                   |  3 +++
 clang/lib/Sema/SemaTemplateInstantiate.cpp   | 18 ++++++++++----
 clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 26 ++++++++++++++++++++
 3 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3c6acf353f93f..5074dd7a21959 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -802,6 +802,9 @@ features cannot lower the translation-unit ABI level;
 - Fixed an assertion failure when a method or function definition follows an
   Objective-C `@implementation` that was ended by a nested `@interface`,
   `@protocol` or `@implementation` before its `@end`. (#GH209503)
+- Fixed a crash and an assertion failure when a non-type template parameter 
+  is canonicalized into a non-expression form (e.g. in a deduction guide),
+  which the template rewrite now rebuilds instead of asserting. (#GH227007)
 
 ### OpenACC Specific Changes
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 06ea12ca7bd73..6544986cea8f7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2227,11 +2227,19 @@ 
TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
     // We're rewriting the template parameter as a reference to another
     // template parameter.
     Arg = getTemplateArgumentPackPatternForRewrite(Arg);
-    assert(Arg.getKind() == TemplateArgument::Expression &&
-           "unexpected nontype template argument kind in template rewrite");
-    // FIXME: This can lead to the same subexpression appearing multiple times
-    // in a complete expression.
-    return Arg.getAsExpr();
+    if (Arg.getKind() == TemplateArgument::Expression) {
+      // FIXME: This can lead to the same subexpression appearing multiple 
times
+      // in a complete expression.
+      return Arg.getAsExpr();
+    }
+    // Otherwise try to rebuild expression if argument has been canonicalized
+    // into a non-expression form (e.g. integral value or template parameter
+    // object)
+    ExprResult Rewritten = SemaRef.BuildExpressionFromNonTypeTemplateArgument(
+        Arg, E->getLocation());
+    if (Rewritten.isInvalid())
+      return ExprError();
+    return Rewritten;
   }
 
   QualType ParamType = NTTP->isExpandedParameterPack()
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp 
b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 78911cbaed67b..7b68ed394393d 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -629,3 +629,29 @@ template <typename T> using S3 = S2<T>; // expected-note 
{{candidate function no
                                         // expected-note {{cannot deduce 
template arguments for 'GH190517::S3' from 'GH190517::S1<char>'}}
 S3 foo(42); // expected-error {{no viable constructor or deduction guide for 
deduction of template arguments of 'S3'}}
 }
+
+namespace GH227007 {
+
+// A non-type template parameter that defaults to another non-type template
+// parameter is canonicalized to a non-expression argument, so the rewrite has
+// to rebuild the expression from the canonical argument.
+struct Id {};
+
+template <auto Kind, class Value, auto Token = Kind> struct Tagged { Value 
value; };
+template <auto Kind = Id{}, class Value, auto Token = Kind>
+Tagged(Value) -> Tagged<Kind, Value, Token>;
+template <class Value> using Default = Tagged<Id{}, Value>;
+
+Default value{false};
+static_assert(__is_same(decltype(value), Tagged<Id{}, bool, Id{}>));
+
+// Same, but with both defaulted parameters being of integral type.
+template <auto Kind, class Value, auto Token = Kind> struct IntTagged { Value 
value; };
+template <auto Kind = 0, class Value, auto Token = Kind>
+IntTagged(Value) -> IntTagged<Kind, Value, Token>;
+template <class Value> using IntDefault = IntTagged<0, Value>;
+
+IntDefault int_value{false};
+static_assert(__is_same(decltype(int_value), IntTagged<0, bool, 0>));
+
+} // namespace GH227007

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

Reply via email to