From 6f28fbd0148ae034c97a0cc7da0264e87153a432 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:26:00 +0200 Subject: [PATCH 1/3] Fix crash in nested autoparsing --- core/metacling/src/ClingRAII.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/metacling/src/ClingRAII.h b/core/metacling/src/ClingRAII.h index cce753ced1266..ac146c998d2cf 100644 --- a/core/metacling/src/ClingRAII.h +++ b/core/metacling/src/ClingRAII.h @@ -26,15 +26,24 @@ namespace ROOT { decltype(clang::Sema::MaybeODRUseExprs) fMaybeODRUseExprs; decltype(clang::Sema::FunctionScopes) fFunctionScopes; decltype(clang::Sema::UndefinedButUsed) fUndefinedButUsed; + // The start indices into FunctionScopes and InventedParameterInfos must + // follow the swap: when we are called during a template instantiation, + // Sema::ContextRAII has set them to the depth of the outer stack, which + // is past the end of our fresh FunctionScopes. + unsigned fFunctionScopesStart = 0; + unsigned fInventedParameterInfosStart; clang::Sema& fSema; void Swapem() { std::swap(fCleanup, fSema.Cleanup); std::swap(fExprCleanupObjects, fSema.ExprCleanupObjects); std::swap(fMaybeODRUseExprs, fSema.MaybeODRUseExprs); std::swap(fFunctionScopes, fSema.FunctionScopes); + std::swap(fFunctionScopesStart, fSema.FunctionScopesStart); + std::swap(fInventedParameterInfosStart, fSema.InventedParameterInfosStart); std::swap(fUndefinedButUsed, fSema.UndefinedButUsed); } - SemaExprCleanupsRAII(clang::Sema& S): fSema(S) { + SemaExprCleanupsRAII(clang::Sema& S): + fInventedParameterInfosStart(S.InventedParameterInfos.size()), fSema(S) { fFunctionScopes.push_back(new clang::sema::FunctionScopeInfo(S.Diags)); Swapem(); }; From 2590e692332c920b1d41fc0917ab93d994633470 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 14 Oct 2026 17:10:19 +0200 Subject: [PATCH 2/3] Update core/metacling/src/ClingRAII.h Co-authored-by: Philippe Canal --- core/metacling/src/ClingRAII.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/metacling/src/ClingRAII.h b/core/metacling/src/ClingRAII.h index ac146c998d2cf..9a9a38955bdb5 100644 --- a/core/metacling/src/ClingRAII.h +++ b/core/metacling/src/ClingRAII.h @@ -30,8 +30,8 @@ namespace ROOT { // follow the swap: when we are called during a template instantiation, // Sema::ContextRAII has set them to the depth of the outer stack, which // is past the end of our fresh FunctionScopes. - unsigned fFunctionScopesStart = 0; - unsigned fInventedParameterInfosStart; + decltype(clang::Sema::FunctionScopesStart) fFunctionScopesStart = 0; + decltype(clang::Sema::InventedParameterInfosStart) fInventedParameterInfosStart; // Set by the constructor to the size of Sema::InventedParameterInfos clang::Sema& fSema; void Swapem() { std::swap(fCleanup, fSema.Cleanup); From 68f6b83de39cbe444264143da6e9dab911a8602f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:54:50 +0200 Subject: [PATCH 3/3] [roottest] Add test for autoparsing from within a template instantiation --- .../nestedAutoParse/CMakeLists.txt | 11 ++++++++++ .../nestedAutoParse/execNestedAutoParse.C | 15 +++++++++++++ .../nestedAutoParse/nestedAutoParse.h | 22 +++++++++++++++++++ .../autoloading/nestedAutoParse/selection.xml | 3 +++ .../nestedAutoParse/useNestedAutoParse.C | 10 +++++++++ 5 files changed, 61 insertions(+) create mode 100644 roottest/root/meta/autoloading/nestedAutoParse/CMakeLists.txt create mode 100644 roottest/root/meta/autoloading/nestedAutoParse/execNestedAutoParse.C create mode 100644 roottest/root/meta/autoloading/nestedAutoParse/nestedAutoParse.h create mode 100644 roottest/root/meta/autoloading/nestedAutoParse/selection.xml create mode 100644 roottest/root/meta/autoloading/nestedAutoParse/useNestedAutoParse.C diff --git a/roottest/root/meta/autoloading/nestedAutoParse/CMakeLists.txt b/roottest/root/meta/autoloading/nestedAutoParse/CMakeLists.txt new file mode 100644 index 0000000000000..48b39123762ac --- /dev/null +++ b/roottest/root/meta/autoloading/nestedAutoParse/CMakeLists.txt @@ -0,0 +1,11 @@ +ROOTTEST_GENERATE_REFLEX_DICTIONARY(nestedAutoParse + nestedAutoParse.h + SELECTION selection.xml + LIBNAME libnestedAutoParse_dictrflx + NO_ROOTMAP + FIXTURES_SETUP root-meta-autoloading-nestedAutoParse-fixture) + +ROOTTEST_ADD_TEST(nestedAutoParse + MACRO execNestedAutoParse.C + COPY_TO_BUILDDIR useNestedAutoParse.C + FIXTURES_REQUIRED root-meta-autoloading-nestedAutoParse-fixture) diff --git a/roottest/root/meta/autoloading/nestedAutoParse/execNestedAutoParse.C b/roottest/root/meta/autoloading/nestedAutoParse/execNestedAutoParse.C new file mode 100644 index 0000000000000..3a9ae471b7fbd --- /dev/null +++ b/roottest/root/meta/autoloading/nestedAutoParse/execNestedAutoParse.C @@ -0,0 +1,15 @@ +// Autoparsing a payload from within a template instantiation used to crash in +// Sema::EnterTemplatedContext, because the nested parse saw the function scopes +// of the outer instantiation (Sema::FunctionScopesStart was not reset). +int execNestedAutoParse() +{ + // Registers only the forward declaration of Payload; its definition is + // parsed on demand. + if (gSystem->Load("libnestedAutoParse_dictrflx") < 0) + return 1; + // Use ProcessLine rather than TInterpreter::Declare: Declare suspends + // autoparsing. + if (gROOT->ProcessLine(".L useNestedAutoParse.C") != 0) + return 2; + return gROOT->ProcessLine("useNestedAutoParse();") == 42 ? 0 : 3; +} diff --git a/roottest/root/meta/autoloading/nestedAutoParse/nestedAutoParse.h b/roottest/root/meta/autoloading/nestedAutoParse/nestedAutoParse.h new file mode 100644 index 0000000000000..646a1648ffbb2 --- /dev/null +++ b/roottest/root/meta/autoloading/nestedAutoParse/nestedAutoParse.h @@ -0,0 +1,22 @@ +#ifndef ROOTTEST_NESTEDAUTOPARSE_H +#define ROOTTEST_NESTEDAUTOPARSE_H + +namespace NestedAutoParse { +namespace Inner { + +// Declaring a class template while parsing the payload calls +// Sema::EnterTemplatedContext, which walks Sema's function scopes. +template +struct Tmpl { + static constexpr int value = N; +}; + +struct Payload { + int fValue = 42; + int get() const { return fValue + Tmpl<0>::value; } +}; + +} // namespace Inner +} // namespace NestedAutoParse + +#endif diff --git a/roottest/root/meta/autoloading/nestedAutoParse/selection.xml b/roottest/root/meta/autoloading/nestedAutoParse/selection.xml new file mode 100644 index 0000000000000..2a09e895e640b --- /dev/null +++ b/roottest/root/meta/autoloading/nestedAutoParse/selection.xml @@ -0,0 +1,3 @@ + + + diff --git a/roottest/root/meta/autoloading/nestedAutoParse/useNestedAutoParse.C b/roottest/root/meta/autoloading/nestedAutoParse/useNestedAutoParse.C new file mode 100644 index 0000000000000..6a34b0350be3a --- /dev/null +++ b/roottest/root/meta/autoloading/nestedAutoParse/useNestedAutoParse.C @@ -0,0 +1,10 @@ +#include + +int useNestedAutoParse() +{ + // Payload is only forward declared here. Instantiating the members of + // std::vector needs the complete type, so its payload is + // autoparsed from within a template instantiation. + std::vector v(1); + return v[0].get(); +}