[clang] [clang] Fix crash/assert for NTTP defaults in CTAD (PR #227214)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 29 02:16:46 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Daniel Petrovic (daniel-petrovic)
<details>
<summary>Changes</summary>
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
---
Full diff: https://github.com/llvm/llvm-project/pull/227214.diff
3 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+3)
- (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+13-5)
- (modified) clang/test/SemaCXX/cxx20-ctad-type-alias.cpp (+26)
``````````diff
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
``````````
</details>
https://github.com/llvm/llvm-project/pull/227214
More information about the cfe-commits
mailing list