https://github.com/saschwartz updated https://github.com/llvm/llvm-project/pull/230513
>From 96972c2d0985f112a13107575f6f5624f2263da3 Mon Sep 17 00:00:00 2001 From: Sebastian Schwartz <[email protected]> Date: Wed, 7 Oct 2026 21:29:09 -0500 Subject: [PATCH] [clang][Parse] Delay template-id destruction in NTTP default arguments A non-type template parameter with a constrained placeholder type, as in template <typename T, C auto V = [] { return T(); }> keeps the TemplateIdAnnotation of its type-constraint `C` in the DeclSpec until ActOnNonTypeTemplateParameter builds the parameter's type. The default argument is parsed before that. When it contains a lambda, each statement of the lambda body ends with MaybeDestroyTemplateIds, which frees all pending template-ids, including the one for `C`. GetTypeForDeclarator then reads the freed annotation. Usually the freed memory still holds the old values; when it was reused, clang builds the constraint from garbage or corrupts the heap. ParseTypeParameter already guards its default argument with DelayTemplateIdDestructionRAII (#89494, for #67235). Do the same in ParseNonTypeTemplateParameter. Fixes #230539. Co-authored-by: Emery Conrad <[email protected]> Assisted-by: Claude Code (claude-opus-5-5) --- clang/docs/ReleaseNotes.md | 4 +++ clang/lib/Parse/ParseTemplate.cpp | 5 ++++ .../cxx2a-constrained-template-param.cpp | 26 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index b5a6679e443d08..01f0b5ea2642ae 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -918,6 +918,10 @@ features cannot lower the translation-unit ABI level; parameters use pack-indexed template template parameters (`TT...[N]<int>`) with different template parameter lists. (#GH228870) +- Fixed a use-after-free when parsing a non-type template parameter with a + constrained placeholder type (such as `C auto`) whose default argument + contains a lambda. (#GH230539) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 1e5aa55338309e..ef6920fdc485c1 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -841,7 +841,12 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { // we introduce the template parameter into the local scope. SourceLocation EqualLoc; ExprResult DefaultArg; + std::optional<DelayTemplateIdDestructionRAII> DontDestructTemplateIds; if (TryConsumeToken(tok::equal, EqualLoc)) { + // The default argument might contain a lambda declaration; avoid destroying + // parsed template ids at the end of that declaration because they can be + // used in the type constraint of the parameter's placeholder type later. + DontDestructTemplateIds.emplace(*this, /*DelayTemplateIdDestruction=*/true); if (Tok.is(tok::l_paren) && NextToken().is(tok::l_brace)) { Diag(Tok.getLocation(), diag::err_stmt_expr_in_default_arg) << 1; SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch); diff --git a/clang/test/Parser/cxx2a-constrained-template-param.cpp b/clang/test/Parser/cxx2a-constrained-template-param.cpp index d27f0f8db9b885..dd15370df27de1 100644 --- a/clang/test/Parser/cxx2a-constrained-template-param.cpp +++ b/clang/test/Parser/cxx2a-constrained-template-param.cpp @@ -68,3 +68,29 @@ template <typename Ret, C Default = S<[] { return Ret(); }>> void func2() {} } + +namespace NTTPLambdaDefaultArg { + +template <class T> +concept C = true; + +template <class T, class U> +concept C2 = true; + +// Same as PR67235, for a non-type template parameter: don't destroy the +// annotation 'C' at the end of the lambda body in the default argument; else +// we'd read freed memory while building the placeholder type 'C auto'. +template <typename T, C auto Default = [](int) { return T{}; }> +constexpr auto V = Default; + +template <typename T, C2<int> auto Default = [] { return T(); }> +void func() {} + +// The source locations of these diagnostics come from the annotation 'C'. +template <typename T, const C auto &Default = [] { return T(); }> // expected-error{{constrained placeholder types other than simple 'auto' on non-type template parameters not supported yet}} +void func2() {} + +template <typename T, C decltype(auto) Default = [] { return T(); }> // expected-error{{constrained placeholder types other than simple 'auto' on non-type template parameters not supported yet}} +void func3() {} + +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
