Skip to content

Destroy the interpreters when they are owned. #610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 2, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 54 additions & 25 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_os_ostream.h"

#include <algorithm>
#include <cassert>
#include <deque>
#include <iterator>
#include <map>
#include <memory>
Expand Down Expand Up @@ -84,20 +86,47 @@
struct InterpreterInfo {
compat::Interpreter* Interpreter = nullptr;
bool isOwned = true;
InterpreterInfo(compat::Interpreter* I, bool Owned)
: Interpreter(I), isOwned(Owned) {}

// Enable move constructors.
InterpreterInfo(InterpreterInfo&& other) noexcept
: Interpreter(other.Interpreter), isOwned(other.isOwned) {
other.Interpreter = nullptr;
other.isOwned = false;
}
InterpreterInfo& operator=(InterpreterInfo&& other) noexcept {
if (this != &other) {
// Delete current resource if owned
if (isOwned)
delete Interpreter;

Interpreter = other.Interpreter;
isOwned = other.isOwned;

other.Interpreter = nullptr;
other.isOwned = false;
}
return *this;
}

// Valgrind complains about __cxa_pure_virtual called when deleting
// llvm::SectionMemoryManager::~SectionMemoryManager as part of the dtor
// chain of the Interpreter.
// This might fix the issue https://reviews.llvm.org/D107087
// FIXME: For now we just leak the Interpreter.
~InterpreterInfo() = default;
~InterpreterInfo() {
if (isOwned)
delete Interpreter;
}

// Disable copy semantics (to avoid accidental double deletes)
InterpreterInfo(const InterpreterInfo&) = delete;
InterpreterInfo& operator=(const InterpreterInfo&) = delete;
};
static llvm::SmallVector<InterpreterInfo, 8> sInterpreters;

// std::deque avoids relocations and calling the dtor of InterpreterInfo.
static llvm::ManagedStatic<std::deque<InterpreterInfo>> sInterpreters;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'sInterpreters' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables]

static llvm::ManagedStatic<std::deque<InterpreterInfo>> sInterpreters;
                                                        ^


static compat::Interpreter& getInterp() {
assert(!sInterpreters.empty() &&
assert(!sInterpreters->empty() &&
"Interpreter instance must be set before calling this!");
return *sInterpreters.back().Interpreter;
return *sInterpreters->back().Interpreter;
}
static clang::Sema& getSema() { return getInterp().getCI()->getSema(); }
static clang::ASTContext& getASTContext() { return getSema().getASTContext(); }
Expand Down Expand Up @@ -2934,53 +2963,53 @@
llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get());
}

sInterpreters.push_back({I, /*isOwned=*/true});
sInterpreters->emplace_back(I, /*Owned=*/true);

return I;
}

bool DeleteInterpreter(TInterp_t I /*=nullptr*/) {
if (!I) {
sInterpreters.pop_back();
sInterpreters->pop_back();
return true;
}

auto* found =
std::find_if(sInterpreters.begin(), sInterpreters.end(),
auto found =
std::find_if(sInterpreters->begin(), sInterpreters->end(),
[&I](const auto& Info) { return Info.Interpreter == I; });
if (found == sInterpreters.end())
if (found == sInterpreters->end())
return false; // failure

sInterpreters.erase(found);
sInterpreters->erase(found);
return true;
}

bool ActivateInterpreter(TInterp_t I) {
if (!I)
return false;

auto* found =
std::find_if(sInterpreters.begin(), sInterpreters.end(),
auto found =
std::find_if(sInterpreters->begin(), sInterpreters->end(),
[&I](const auto& Info) { return Info.Interpreter == I; });
if (found == sInterpreters.end())
if (found == sInterpreters->end())
return false;

if (std::next(found) != sInterpreters.end()) // if not already last element.
std::rotate(found, found + 1, sInterpreters.end());
if (std::next(found) != sInterpreters->end()) // if not already last element.
std::rotate(found, found + 1, sInterpreters->end());

return true; // success
}

TInterp_t GetInterpreter() {
if (sInterpreters.empty())
if (sInterpreters->empty())
return nullptr;
return sInterpreters.back().Interpreter;
return sInterpreters->back().Interpreter;
}

void UseExternalInterpreter(TInterp_t I) {
assert(sInterpreters.empty() && "sInterpreter already in use!");
sInterpreters.push_back(
{static_cast<compat::Interpreter*>(I), /*isOwned=*/false});
assert(sInterpreters->empty() && "sInterpreter already in use!");
sInterpreters->emplace_back(static_cast<compat::Interpreter*>(I),
/*isOwned=*/false);

Check warning on line 3012 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L3010-L3012

Added lines #L3010 - L3012 were not covered by tests
}

void AddSearchPath(const char* dir, bool isUser, bool prepend) {
Expand Down
Loading