[clang] [clang] Fix crash/assert for NTTP defaults in CTAD (PR #227214)
Daniel Petrovic via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 29 05:29:47 PDT 2026
https://github.com/daniel-petrovic updated https://github.com/llvm/llvm-project/pull/227214
>From d88b100d7acce3cba266e5529c7e10cc76f336da Mon Sep 17 00:00:00 2001
From: Daniel Petrovic <daniel-dev at hotmail.de>
Date: Tue, 29 Sep 2026 10:23:50 +0200
Subject: [PATCH 1/2] [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
>From d2452ba05909f1b05b9e3abb4c60e3b09d437da0 Mon Sep 17 00:00:00 2001
From: Daniel Petrovic <daniel-dev at hotmail.de>
Date: Tue, 29 Sep 2026 14:06:08 +0200
Subject: [PATCH 2/2] Get assertion back because of MLTAL
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 6544986cea8f7..ca7c4dbc3f7fd 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2232,9 +2232,23 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
// 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)
+
+ // Otherwise try rebuilding the expression if argument has been
+ // canonicalized into a non-expression form
+ // (e.g. integral value, template parameter object, ...).
+ // Any other kind indicates a mismatch between the template argument list
+ // and the parameter being rewritten.
+ switch (Arg.getKind()) {
+ case TemplateArgument::Integral:
+ case TemplateArgument::Declaration:
+ case TemplateArgument::NullPtr:
+ case TemplateArgument::StructuralValue:
+ break;
+ default:
+ assert(false &&
+ "unexpected nontype template argument kind in template rewrite");
+ return ExprError();
+ }
ExprResult Rewritten = SemaRef.BuildExpressionFromNonTypeTemplateArgument(
Arg, E->getLocation());
if (Rewritten.isInvalid())
More information about the cfe-commits
mailing list