Rename get_or_insert to reuse_existing

Closes #5
This commit is contained in:
Duncan Ogilvie
2026-05-12 16:15:28 +02:00
parent da4bd0f885
commit 231c74cdd2
8 changed files with 91 additions and 72 deletions
+2 -2
View File
@@ -206,11 +206,11 @@ This section captures guard preconditions for wrapper classes other than
- `constant(str, radix)` requires `2 <= radix <= 36`.
- Named struct creation:
- `ctx.types.struct("Name", [field_types...])` defaults to
`get_or_insert=True`.
`reuse_existing=True`.
- Reusing the same name with the same body returns the existing identified
struct; reusing an opaque declaration completes it; conflicting bodies or
packing raise `LLVMAssertionError`.
- Pass `get_or_insert=False` for raw LLVM insertion behavior, which may append
- Pass `reuse_existing=False` for raw LLVM insertion behavior, which may append
a suffix to duplicate names.
- Struct body mutation:
- `set_body` requires identified opaque struct type (not literal, not already
+1 -1
View File
@@ -44,7 +44,7 @@ Implement remaining LLVM-C API bindings to reach ~85% coverage of the C API, wit
- `section.contains_symbol(symbol)` - Symbol membership test
**Comdat.h (all 5 functions):**
- `mod.get_or_insert_comdat(name)` - Get/create COMDAT
- `mod.add_comdat(name)` - Add/create COMDAT, or return the existing one
- `gv.comdat` / `gv.set_comdat(comdat)` - Global COMDAT access
- `comdat.selection_kind` - Selection kind property
+1 -1
View File
@@ -1254,7 +1254,7 @@ def declare_symbols(src: llvm.Module, m: llvm.Module) -> None:
name = cur_md.name
if m.get_named_metadata(name):
raise RuntimeError("Named Metadata Node already cloned")
m.get_or_insert_named_metadata(name)
m.add_named_metadata(name)
next_md = cur_md.next
if next_md is None:
+56 -49
View File
@@ -1042,7 +1042,7 @@ struct LLVMTypeFactoryWrapper {
LLVMTypeWrapper struct_with_name(const std::string &name,
const Iterable<LLVMTypeWrapper> &elem_types,
bool packed = false,
bool get_or_insert = true) const {
bool reuse_existing = true) const {
check_valid();
if (name.empty()) {
throw LLVMAssertionError(
@@ -1050,7 +1050,7 @@ struct LLVMTypeFactoryWrapper {
}
std::vector<LLVMTypeRef> elems = collect_type_refs(elem_types);
if (get_or_insert) {
if (reuse_existing) {
if (LLVMTypeRef existing = LLVMGetTypeByName2(m_ctx_ref, name.c_str())) {
require_same_struct_body(existing, elems, packed, name);
if (LLVMIsOpaqueStruct(existing)) {
@@ -1080,12 +1080,12 @@ struct LLVMTypeFactoryWrapper {
// Opaque named struct (forward declaration)
LLVMTypeWrapper opaque_struct(const std::string &name,
bool get_or_insert = true) const {
bool reuse_existing = true) const {
check_valid();
if (name.empty()) {
throw LLVMAssertionError("Opaque struct type requires a non-empty name");
}
if (get_or_insert) {
if (reuse_existing) {
if (LLVMTypeRef existing = LLVMGetTypeByName2(m_ctx_ref, name.c_str()))
return LLVMTypeWrapper(existing, m_context_token);
}
@@ -6055,10 +6055,10 @@ struct LLVMModuleWrapper : NoMoveCopy {
// Functions
LLVMFunctionWrapper add_function(const std::string &name,
const LLVMTypeWrapper &func_ty,
bool get_or_insert = true) {
bool reuse_existing = true) {
check_valid();
func_ty.check_valid();
if (get_or_insert) {
if (reuse_existing) {
if (LLVMValueRef func = LLVMGetNamedFunction(m_ref, name.c_str())) {
require_same_type(LLVMGlobalGetValueType(func), func_ty.m_ref, name,
"function");
@@ -6081,17 +6081,17 @@ struct LLVMModuleWrapper : NoMoveCopy {
// Global variables
LLVMValueWrapper add_global(const LLVMTypeWrapper &ty,
const std::string &name,
bool get_or_insert = true) {
return add_global_in_address_space(ty, name, 0, get_or_insert);
bool reuse_existing = true) {
return add_global_in_address_space(ty, name, 0, reuse_existing);
}
LLVMValueWrapper add_global_in_address_space(const LLVMTypeWrapper &ty,
const std::string &name,
unsigned address_space,
bool get_or_insert = true) {
bool reuse_existing = true) {
check_valid();
ty.check_valid();
if (get_or_insert) {
if (reuse_existing) {
if (LLVMValueRef global = LLVMGetNamedGlobal(m_ref, name.c_str())) {
require_same_type(LLVMGlobalGetValueType(global), ty.m_ref, name,
"global variable");
@@ -6263,8 +6263,8 @@ struct LLVMModuleWrapper : NoMoveCopy {
// COMDAT support (for Windows/COFF linking)
// ==========================================================================
/// Get or insert a COMDAT section with the given name.
LLVMComdatWrapper get_or_insert_comdat(const std::string &name) {
/// Add a COMDAT section, or return the existing one with this name.
LLVMComdatWrapper add_comdat(const std::string &name) {
check_valid();
LLVMComdatRef comdat = LLVMGetOrInsertComdat(m_ref, name.c_str());
return LLVMComdatWrapper(comdat, m_token);
@@ -6319,11 +6319,11 @@ struct LLVMModuleWrapper : NoMoveCopy {
unsigned addr_space,
const LLVMValueWrapper &aliasee,
const std::string &name,
bool get_or_insert = true) {
bool reuse_existing = true) {
check_valid();
value_ty.check_valid();
aliasee.check_valid();
if (get_or_insert) {
if (reuse_existing) {
if (LLVMValueRef alias =
LLVMGetNamedGlobalAlias(m_ref, name.c_str(), name.size())) {
require_same_type(LLVMGlobalGetValueType(alias), value_ty.m_ref, name,
@@ -6374,11 +6374,11 @@ struct LLVMModuleWrapper : NoMoveCopy {
const LLVMTypeWrapper &ty,
unsigned addr_space,
const LLVMValueWrapper &resolver,
bool get_or_insert = true) {
bool reuse_existing = true) {
check_valid();
ty.check_valid();
resolver.check_valid();
if (get_or_insert) {
if (reuse_existing) {
if (LLVMValueRef ifunc =
LLVMGetNamedGlobalIFunc(m_ref, name.c_str(), name.size())) {
require_same_type(LLVMGlobalGetValueType(ifunc), ty.m_ref, name,
@@ -6426,7 +6426,7 @@ struct LLVMModuleWrapper : NoMoveCopy {
return LLVMNamedMDNodeWrapper(md, m_context_token);
}
LLVMNamedMDNodeWrapper get_or_insert_named_metadata(const std::string &name) {
LLVMNamedMDNodeWrapper add_named_metadata(const std::string &name) {
check_valid();
return LLVMNamedMDNodeWrapper(
LLVMGetOrInsertNamedMetadata(m_ref, name.c_str(), name.size()),
@@ -13959,12 +13959,12 @@ Valid when:
<sub>C API: LLVMGetTarget, LLVMSetTarget</sub>)")
.def("add_function", &LLVMModuleWrapper::add_function, "name"_a,
"func_ty"_a, nb::kw_only(), "get_or_insert"_a = true,
"func_ty"_a, nb::kw_only(), "reuse_existing"_a = true,
R"(Add or get a function.
By default, this returns an existing function with the same name when the
function type matches, and raises if the name exists with an incompatible type
or as another global value kind. Pass get_or_insert=False for LLVM's raw
or as another global value kind. Pass reuse_existing=False for LLVM's raw
inserting behavior, which may rename collisions with a suffix like `.0`.
<sub>C API: LLVMGetNamedFunction, LLVMAddFunction</sub>)")
@@ -13973,23 +13973,23 @@ inserting behavior, which may rename collisions with a suffix like `.0`.
<sub>C API: LLVMGetNamedFunction</sub>)")
.def("add_global", &LLVMModuleWrapper::add_global, "ty"_a, "name"_a,
nb::kw_only(), "get_or_insert"_a = true,
nb::kw_only(), "reuse_existing"_a = true,
R"(Add or get a global variable in address space 0.
By default, this returns an existing global variable with the same name when
the element type and address space match, and raises on incompatible existing
symbols. Pass get_or_insert=False for LLVM's raw inserting behavior, which may
symbols. Pass reuse_existing=False for LLVM's raw inserting behavior, which may
rename collisions with a suffix like `.0`.
<sub>C API: LLVMGetNamedGlobal, LLVMAddGlobalInAddressSpace</sub>)")
.def("add_global_in_address_space",
&LLVMModuleWrapper::add_global_in_address_space, "ty"_a, "name"_a,
"address_space"_a, nb::kw_only(), "get_or_insert"_a = true,
"address_space"_a, nb::kw_only(), "reuse_existing"_a = true,
R"(Add or get a global variable in an address space.
By default, this returns an existing global variable with the same name when
the element type and address space match, and raises on incompatible existing
symbols. Pass get_or_insert=False for LLVM's raw inserting behavior, which may
symbols. Pass reuse_existing=False for LLVM's raw inserting behavior, which may
rename collisions with a suffix like `.0`.
<sub>C API: LLVMGetNamedGlobal, LLVMAddGlobalInAddressSpace</sub>)")
@@ -14065,11 +14065,11 @@ rename collisions with a suffix like `.0`.
<sub>C API: LLVMGetNamedGlobalAlias</sub>)")
.def("add_alias", &LLVMModuleWrapper::add_alias, "value_ty"_a,
"addr_space"_a, "aliasee"_a, "name"_a, nb::kw_only(),
"get_or_insert"_a = true,
"reuse_existing"_a = true,
R"(Add or get a global alias.
By default, this returns an existing alias with the same name when its value
type, address space, and aliasee match. Pass get_or_insert=False for LLVM's raw
type, address space, and aliasee match. Pass reuse_existing=False for LLVM's raw
inserting behavior.
<sub>C API: LLVMGetNamedGlobalAlias, LLVMAddAlias2</sub>)")
@@ -14088,11 +14088,11 @@ inserting behavior.
<sub>C API: LLVMGetNamedGlobalIFunc</sub>)")
.def("add_global_ifunc", &LLVMModuleWrapper::add_global_ifunc, "name"_a,
"ty"_a, "addr_space"_a, "resolver"_a, nb::kw_only(),
"get_or_insert"_a = true,
"reuse_existing"_a = true,
R"(Add or get an indirect function (IFunc) in the module.
By default, this returns an existing IFunc with the same name when its type,
address space, and resolver match. Pass get_or_insert=False for LLVM's raw
address space, and resolver match. Pass reuse_existing=False for LLVM's raw
inserting behavior.
<sub>C API: LLVMGetNamedGlobalIFunc, LLVMAddGlobalIFunc</sub>)")
@@ -14111,9 +14111,13 @@ inserting behavior.
"name"_a, R"(Get named metadata.
<sub>C API: LLVMGetNamedMetadata</sub>)")
.def("get_or_insert_named_metadata",
&LLVMModuleWrapper::get_or_insert_named_metadata, "name"_a,
R"(Get or insert named metadata.
.def("add_named_metadata",
&LLVMModuleWrapper::add_named_metadata, "name"_a,
R"(Add named metadata, or return the existing node with this name.
This method has get-or-insert behavior: if named metadata with `name` already
exists in the module, it is returned unchanged; otherwise a new empty named
metadata node is created and returned.
<sub>C API: LLVMGetOrInsertNamedMetadata</sub>)")
.def("get_named_metadata_num_operands",
@@ -14164,18 +14168,21 @@ inserting behavior.
<sub>C API: LLVMLinkModules2</sub>)")
// COMDAT support
.def("get_or_insert_comdat", &LLVMModuleWrapper::get_or_insert_comdat,
"name"_a,
R"(Get or insert a COMDAT section with the given name.
COMDAT sections are used on Windows/COFF targets for symbol
deduplication and merging.
Args:
name: The COMDAT name
Returns:
A Comdat object for the named section.
.def("add_comdat", &LLVMModuleWrapper::add_comdat, "name"_a,
R"(Add a COMDAT section, or return the existing one with this name.
This method has get-or-insert behavior: if a COMDAT with `name` already exists
in the module, it is returned unchanged; otherwise a new COMDAT is created and
returned.
COMDAT sections are used on Windows/COFF targets for symbol deduplication and
merging.
Args:
name: The COMDAT name
Returns:
A Comdat object for the named section.
<sub>C API: LLVMGetOrInsertComdat</sub>)")
// Module printing to file
@@ -14379,21 +14386,21 @@ Use struct(name, elem_types, ...) for identified named structs.
<sub>C API: LLVMStructTypeInContext</sub>)")
.def("struct", &LLVMTypeFactoryWrapper::struct_with_name, "name"_a,
"elem_types"_a, "packed"_a = false, nb::kw_only(),
"get_or_insert"_a = true,
"reuse_existing"_a = true,
R"(Named struct type.
Named structs default to lookup-first get-or-insert semantics: an existing
struct with the same name and body is returned, an opaque declaration is
completed, and a different existing body raises. Pass get_or_insert=False for
LLVM's raw inserting behavior, which may append a suffix to duplicate names.
Named structs default to lookup-first reuse semantics: an existing struct with
the same name and body is returned, an opaque declaration is completed, and a
different existing body raises. Pass reuse_existing=False for LLVM's raw
inserting behavior, which may append a suffix to duplicate names.
<sub>C API: LLVMStructCreateNamed</sub>)")
.def("opaque_struct", &LLVMTypeFactoryWrapper::opaque_struct, "name"_a,
nb::kw_only(), "get_or_insert"_a = true,
nb::kw_only(), "reuse_existing"_a = true,
R"(Opaque struct type.
Defaults to returning an existing named struct with this name. Pass
get_or_insert=False for LLVM's raw inserting behavior, which may append a
reuse_existing=False for LLVM's raw inserting behavior, which may append a
suffix to duplicate names.
<sub>C API: LLVMStructCreateNamed</sub>)")
+25 -13
View File
@@ -2,8 +2,8 @@
Regression coverage for API surface cleanup helpers.
Covers:
- Module add_* get_or_insert=True defaults and explicit raw insertion escape hatch
- Named struct get_or_insert=True defaults and explicit raw insertion escape hatch
- Module add_* reuse_existing=True defaults and explicit raw insertion escape hatch
- Named struct reuse_existing=True defaults and explicit raw insertion escape hatch
- New member/factory APIs that replace module-level helpers
- Builder parent navigation helpers and Module.create_builder()
"""
@@ -20,7 +20,7 @@ def expect_raises(fn, message_part: str) -> None:
raise AssertionError("expected exception")
def test_module_add_get_or_insert_semantics() -> None:
def test_module_add_reuse_existing_semantics() -> None:
with llvm.create_context() as ctx:
i32 = ctx.types.i32
i64 = ctx.types.i64
@@ -29,12 +29,12 @@ def test_module_add_get_or_insert_semantics() -> None:
other_fn_ty = ctx.types.function(i32, [])
resolver_ty = ctx.types.function(i32, [])
with ctx.create_module("get_or_insert") as mod:
with ctx.create_module("reuse_existing") as mod:
fn = mod.add_function("f", fn_ty)
assert mod.add_function("f", fn_ty) == fn
assert len([f for f in mod.functions if f.name.startswith("f")]) == 1
expect_raises(lambda: mod.add_function("f", other_fn_ty), "different type")
raw_fn = mod.add_function("f", fn_ty, get_or_insert=False)
raw_fn = mod.add_function("f", fn_ty, reuse_existing=False)
assert raw_fn.name != "f"
g = mod.add_global(i32, "g")
@@ -44,7 +44,7 @@ def test_module_add_get_or_insert_semantics() -> None:
lambda: mod.add_global_in_address_space(i32, "g", 1),
"address space",
)
raw_g = mod.add_global(i32, "g", get_or_insert=False)
raw_g = mod.add_global(i32, "g", reuse_existing=False)
assert raw_g.name != "g"
expect_raises(lambda: mod.add_function("g", fn_ty), "global variable")
@@ -53,7 +53,7 @@ def test_module_add_get_or_insert_semantics() -> None:
alias = mod.add_alias(i32, 0, g, "alias")
assert mod.add_alias(i32, 0, g, "alias") == alias
expect_raises(lambda: mod.add_alias(i64, 0, g, "alias"), "different type")
raw_alias = mod.add_alias(i32, 0, g, "alias", get_or_insert=False)
raw_alias = mod.add_alias(i32, 0, g, "alias", reuse_existing=False)
assert raw_alias.name != "alias"
resolver = mod.add_function("resolver", resolver_ty)
@@ -69,12 +69,12 @@ def test_module_add_get_or_insert_semantics() -> None:
"different resolver",
)
raw_ifunc = mod.add_global_ifunc(
"ifunc", resolver_ty, 0, resolver, get_or_insert=False
"ifunc", resolver_ty, 0, resolver, reuse_existing=False
)
assert raw_ifunc.name != "ifunc"
def test_named_struct_get_or_insert_semantics() -> None:
def test_named_struct_reuse_existing_semantics() -> None:
with llvm.create_context() as ctx:
i32 = ctx.types.i32
i64 = ctx.types.i64
@@ -93,7 +93,7 @@ def test_named_struct_get_or_insert_semantics() -> None:
)
raw_pair = ctx.types.struct(
"Pair", [i32, i64], packed=False, get_or_insert=False
"Pair", [i32, i64], packed=False, reuse_existing=False
)
assert raw_pair != pair
assert raw_pair.struct_name != "Pair"
@@ -105,7 +105,7 @@ def test_named_struct_get_or_insert_semantics() -> None:
assert completed == forward
assert not completed.is_opaque_struct
raw_forward = ctx.types.opaque_struct("Forward", get_or_insert=False)
raw_forward = ctx.types.opaque_struct("Forward", reuse_existing=False)
assert raw_forward != forward
assert raw_forward.struct_name != "Forward"
@@ -205,6 +205,18 @@ def test_member_factories_and_builder_navigation() -> None:
with mod.create_builder(ret) as before_ret:
assert before_ret.insert_block == bb
assert not hasattr(mod, "get_or_insert_named_metadata")
named_md = mod.add_named_metadata("llvm.nanobind.surface")
assert mod.add_named_metadata("llvm.nanobind.surface") == named_md
assert not hasattr(mod, "get_or_insert_comdat")
comdat = mod.add_comdat("surface_comdat")
comdat.selection_kind = llvm.ComdatSelectionKind.ExactMatch
assert (
mod.add_comdat("surface_comdat").selection_kind
== llvm.ComdatSelectionKind.ExactMatch
)
opts = llvm.PassBuilderOptions()
mod.run_passes("default<O0>", options=opts)
@@ -223,7 +235,7 @@ def test_member_factories_and_builder_navigation() -> None:
if __name__ == "__main__":
test_module_add_get_or_insert_semantics()
test_named_struct_get_or_insert_semantics()
test_module_add_reuse_existing_semantics()
test_named_struct_reuse_existing_semantics()
test_member_factories_and_builder_navigation()
print("test_api_surface_cleanup: PASSED")
@@ -282,8 +282,8 @@ def test_lifetime_guards_for_remaining_wrappers():
i32 = ctx.types.i32
escaped["types"] = ctx.types
escaped["attr"] = ctx.create_string_attribute("k", "v")
escaped["comdat"] = mod.get_or_insert_comdat("C")
escaped["named_md"] = mod.get_or_insert_named_metadata("llvm.nanobind.named")
escaped["comdat"] = mod.add_comdat("C")
escaped["named_md"] = mod.add_named_metadata("llvm.nanobind.named")
fn_ty = ctx.types.function(i32, [i32], False)
fn = mod.add_function("f", fn_ty)
+2 -2
View File
@@ -827,7 +827,7 @@ def test_value_accessor_guard_matrix_negative():
"global value",
),
("comdat", lambda: bad.comdat, "global object"),
("set_comdat", lambda: bad.set_comdat(mod.get_or_insert_comdat("x")), "global object"),
("set_comdat", lambda: bad.set_comdat(mod.add_comdat("x")), "global object"),
("section", lambda: bad.section, "global object"),
("section", lambda: setattr(bad, "section", ".foo"), "global object"),
("set_thread_local", lambda: bad.set_thread_local(True), "global variable"),
@@ -1155,7 +1155,7 @@ def test_value_accessor_guard_matrix_positive():
assert g0.is_thread_local
g0.set_externally_initialized(True)
assert g0.is_externally_initialized
c = mod.get_or_insert_comdat("value_guard_positive_comdat")
c = mod.add_comdat("value_guard_positive_comdat")
g0.set_comdat(c)
assert g0.comdat is not None
g_del = mod.add_global(ctx.types.i32, "g_del")
+2 -2
View File
@@ -382,8 +382,8 @@ def test_comdat():
"""Test all Comdat APIs"""
with llvm.create_context() as ctx:
with ctx.create_module("test") as mod:
# Test get_or_insert_comdat
comdat = mod.get_or_insert_comdat("my_comdat")
# Test add_comdat get-or-insert behavior
comdat = mod.add_comdat("my_comdat")
assert comdat is not None
# Test selection_kind property (getter and setter)