From 9ba9d044ee8bfdcf426b3286c675af88487b6a77 Mon Sep 17 00:00:00 2001 From: Devajith Valaparambil Sreeramaswamy Date: Thu, 17 Jul 2025 11:41:23 +0200 Subject: [PATCH 1/2] [cling] Unload all redecls of ClassTemplateDecl When unloading a ClassTemplateDecl, no redeclarations were handled Additional redecls may still be part of the chain leading to duplicates and broken redecl chains that cause assertion failures like: "Passed first decl twice, invalid redecl chain!" Fixes assertion failures post llvm commit: https://github.com/llvm/llvm-project/commit/17d8ed7 for the test `roottest-root-meta-cmsUnload-make` --- interpreter/cling/lib/Interpreter/DeclUnloader.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/interpreter/cling/lib/Interpreter/DeclUnloader.cpp b/interpreter/cling/lib/Interpreter/DeclUnloader.cpp index 2ae3bfff66c1f..37a60a0aef66d 100644 --- a/interpreter/cling/lib/Interpreter/DeclUnloader.cpp +++ b/interpreter/cling/lib/Interpreter/DeclUnloader.cpp @@ -1042,7 +1042,15 @@ namespace cling { Successful &= VisitClassTemplateSpecializationDecl(*I, /*RemoveSpec=*/false); - Successful &= VisitRedeclarableTemplateDecl(CTD); + // Visit all redeclarations of this template to ensure the full + // redecl chain is unloaded properly. + // Not doing this will lead to a corrupted decl chain and can cause + // assertions later down the line. + // We also need to make a copy of `CTD->redecls()` to not mess up the chain + llvm::SmallVector redecls(CTD->redecls()); + for (auto* Redecl : redecls) + Successful &= VisitRedeclarableTemplateDecl(Redecl); + Successful &= Visit(CTD->getTemplatedDecl()); return Successful; } From 0bb874bc3d9f8755411e72189cee0b2bc5b83592 Mon Sep 17 00:00:00 2001 From: Devajith Valaparambil Sreeramaswamy Date: Thu, 25 Sep 2025 14:38:52 +0200 Subject: [PATCH 2/2] [cling] Add test for template unloading --- roottest/root/meta/redeclUnload/CMakeLists.txt | 12 ++++++++++++ .../root/meta/redeclUnload/TemplateRedeclUnload.ref | 1 + roottest/root/meta/redeclUnload/inc/RedeclTrigger.h | 8 ++++++++ .../root/meta/redeclUnload/inc/redecl_selection.xml | 4 ++++ .../meta/redeclUnload/testTemplateRedeclUnload.C | 12 ++++++++++++ 5 files changed, 37 insertions(+) create mode 100644 roottest/root/meta/redeclUnload/CMakeLists.txt create mode 100644 roottest/root/meta/redeclUnload/TemplateRedeclUnload.ref create mode 100644 roottest/root/meta/redeclUnload/inc/RedeclTrigger.h create mode 100644 roottest/root/meta/redeclUnload/inc/redecl_selection.xml create mode 100644 roottest/root/meta/redeclUnload/testTemplateRedeclUnload.C diff --git a/roottest/root/meta/redeclUnload/CMakeLists.txt b/roottest/root/meta/redeclUnload/CMakeLists.txt new file mode 100644 index 0000000000000..e68ae4539ade6 --- /dev/null +++ b/roottest/root/meta/redeclUnload/CMakeLists.txt @@ -0,0 +1,12 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc) + +ROOTTEST_GENERATE_REFLEX_DICTIONARY(TemplateRedecl + inc/RedeclTrigger.h + SELECTION inc/redecl_selection.xml + FIXTURES_SETUP root-meta-redeclUnload-TemplateRedecl-fixture) + +ROOTTEST_ADD_TEST(templateRedeclUnload + MACRO testTemplateRedeclUnload.C + ROOTEXE_OPTS -e "gInterpreter->AddIncludePath(\"-I${CMAKE_CURRENT_SOURCE_DIR}\")" + OUTREF TemplateRedeclUnload.ref + FIXTURES_REQUIRED root-meta-redeclUnload-TemplateRedecl-fixture) diff --git a/roottest/root/meta/redeclUnload/TemplateRedeclUnload.ref b/roottest/root/meta/redeclUnload/TemplateRedeclUnload.ref new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/roottest/root/meta/redeclUnload/TemplateRedeclUnload.ref @@ -0,0 +1 @@ + diff --git a/roottest/root/meta/redeclUnload/inc/RedeclTrigger.h b/roottest/root/meta/redeclUnload/inc/RedeclTrigger.h new file mode 100644 index 0000000000000..0ab35620bb7f8 --- /dev/null +++ b/roottest/root/meta/redeclUnload/inc/RedeclTrigger.h @@ -0,0 +1,8 @@ +// Two wrappers are needed to reproduce the assertion with broken redecl chain. +template +class Wrapper1 {}; +template +class Wrapper2 {}; + +class RandomClass {}; +Wrapper1> var; diff --git a/roottest/root/meta/redeclUnload/inc/redecl_selection.xml b/roottest/root/meta/redeclUnload/inc/redecl_selection.xml new file mode 100644 index 0000000000000..3fa587844e76e --- /dev/null +++ b/roottest/root/meta/redeclUnload/inc/redecl_selection.xml @@ -0,0 +1,4 @@ + + + + diff --git a/roottest/root/meta/redeclUnload/testTemplateRedeclUnload.C b/roottest/root/meta/redeclUnload/testTemplateRedeclUnload.C new file mode 100644 index 0000000000000..88b14344ee185 --- /dev/null +++ b/roottest/root/meta/redeclUnload/testTemplateRedeclUnload.C @@ -0,0 +1,12 @@ +void testTemplateRedeclUnload() +{ + gROOT->ProcessLine("gSystem->Load(\"libTemplateRedecl_dictrflx\");"); + gROOT->ProcessLine("gInterpreter->AutoParse(\"RandomClass\");"); + + // Undo the last 2 commands, which unloads the templates. + gROOT->ProcessLine(".undo 2"); + + // Before the fix: cling crashes here with: + // "Passed first decl twice, invalid redecl chain!" + gROOT->ProcessLine("RandomClass obj;"); +}