From 6bec8adf160fb40ee1101fbbfe8d852bb61f72c7 Mon Sep 17 00:00:00 2001 From: Rob23oba <152706811+Rob23oba@users.noreply.github.com> Date: Wed, 21 Jan 2026 21:38:29 +0100 Subject: [PATCH] fix: symbol name for native boxed declarations in the interpreter (#12095) This PR fixes the procedure for finding the mangled symbol name of boxed variants of native functions. Previously, the wrong symbol name has been used for names ending in `_`: For example `test_` mangles to `l_test__` but `test_._boxed` mangles to `l_test___00__boxed`, not `l_test_____boxed` which the compiler would previously wrongly use. This probably didn't affect anybody though since the failure condition is pretty rare: the name of a native function that the interpreter tries to execute would've had to end in `_`. --- src/Lean/Compiler/ModPkgExt.lean | 6 +++--- src/Lean/Compiler/NameMangling.lean | 14 +++++++++++++- src/library/ir_interpreter.cpp | 14 ++++++++------ tests/lean/run/mangling.lean | 7 ++++++- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/Lean/Compiler/ModPkgExt.lean b/src/Lean/Compiler/ModPkgExt.lean index f96d5bbb74..29ede4dfc6 100644 --- a/src/Lean/Compiler/ModPkgExt.lean +++ b/src/Lean/Compiler/ModPkgExt.lean @@ -56,9 +56,9 @@ public def Environment.getModulePackageByIdx? (env : Environment) (idx : ModuleI Returns the standard base of the native symbol for the compiled constant {lean}`declName`. For many constants, this is the full symbol. However, initializers have an additional prefix -(i.e., {lit}`_init_`) and boxed functions have an additional suffix (i.e., {lit}`___boxed`). -Furthermore, some constants do not use this stem at all (e.g., {lit}`main` and definitions -with {lit}`@[export]`). +(i.e., {lit}`_init_`) and boxed functions have an additional suffix +(see {name}`mkMangledBoxedName`). Furthermore, some constants do not use this stem at all +(e.g., {lit}`main` and definitions with {lit}`@[export]`). -/ @[export lean_get_symbol_stem] public def getSymbolStem (env : Environment) (declName : Name) : String := diff --git a/src/Lean/Compiler/NameMangling.lean b/src/Lean/Compiler/NameMangling.lean index cac708aac9..d5ae148fe9 100644 --- a/src/Lean/Compiler/NameMangling.lean +++ b/src/Lean/Compiler/NameMangling.lean @@ -7,7 +7,7 @@ module prelude public import Lean.Setup -import Init.Data.String.Termination +import Init.Data.String.TakeDrop namespace String @@ -133,6 +133,18 @@ def Name.mangleAux : Name → String public def Name.mangle (n : Name) (pre : String := "l_") : String := pre ++ Name.mangleAux n +/-- +Given `s = nm.mangle pre` for some `nm : Name` and `pre : String` with `nm != Name.anonymous`, +returns `(mkBoxedName nm).mangle pre`. This is used in the interpreter to find names of boxed +IR declarations. +-/ +@[export lean_mk_mangled_boxed_name] +public def mkMangledBoxedName (s : String) : String := + if s.endsWith "__" then + s ++ "_00__boxed" + else + s ++ "___boxed" + /-- The mangled name of the name used to create the module initialization function. diff --git a/src/library/ir_interpreter.cpp b/src/library/ir_interpreter.cpp index 9766c2dd79..8a79a50e03 100644 --- a/src/library/ir_interpreter.cpp +++ b/src/library/ir_interpreter.cpp @@ -200,7 +200,6 @@ option_ref find_ir_decl_boxed(elab_environment const & env, name const & n extern "C" double lean_float_of_nat(lean_obj_arg a); extern "C" float lean_float32_of_nat(lean_obj_arg a); -static string_ref * g_boxed_mangled_suffix = nullptr; static name * g_interpreter_prefer_native = nullptr; DEBUG_CODE(static name * g_interpreter_step = nullptr;) DEBUG_CODE(static name * g_interpreter_call = nullptr;) @@ -216,6 +215,12 @@ string_ref get_symbol_stem(elab_environment const & env, name const & fn) { return string_ref(lean_get_symbol_stem(env.to_obj_arg(), fn.to_obj_arg())); } +extern "C" obj_res lean_mk_mangled_boxed_name(obj_arg str); + +string_ref mk_mangled_boxed_name(string_ref const & str) { + return string_ref(lean_mk_mangled_boxed_name(str.to_obj_arg())); +} + extern "C" object * lean_ir_format_fn_body_head(object * b); std::string format_fn_body_head(fn_body const & b) { object_ref s(lean_ir_format_fn_body_head(b.to_obj_arg())); @@ -843,7 +848,7 @@ private: symbol_cache_entry e_new { get_decl(fn), {nullptr, false} }; if (m_prefer_native || decl_tag(e_new.m_decl) == decl_kind::Extern || has_init_attribute(m_env, fn)) { string_ref mangled = get_symbol_stem(m_env, fn); - string_ref boxed_mangled(string_append(mangled.to_obj_arg(), g_boxed_mangled_suffix->raw())); + string_ref boxed_mangled = mk_mangled_boxed_name(mangled); // check for boxed version first if (void *p_boxed = lookup_symbol_in_cur_exe(boxed_mangled.data())) { e_new.m_native.m_addr = p_boxed; @@ -965,7 +970,7 @@ private: } else { if (decl_tag(e.m_decl) == decl_kind::Extern) { string_ref mangled = get_symbol_stem(m_env, fn); - string_ref boxed_mangled(string_append(mangled.to_obj_arg(), g_boxed_mangled_suffix->raw())); + string_ref boxed_mangled = mk_mangled_boxed_name(mangled); throw exception(sstream() << "Could not find native implementation of external declaration '" << fn << "' (symbols '" << boxed_mangled.data() << "' or '" << mangled.data() << "').\n" << "For declarations from `Init`, `Std`, or `Lean`, you need to set `supportInterpreter := true` " @@ -1212,8 +1217,6 @@ extern "C" LEAN_EXPORT object * lean_run_init(object * env, object * opts, objec } void initialize_ir_interpreter() { - ir::g_boxed_mangled_suffix = new string_ref("___boxed"); - mark_persistent(ir::g_boxed_mangled_suffix->raw()); ir::g_interpreter_prefer_native = new name({"interpreter", "prefer_native"}); ir::g_init_globals = new name_hash_map(); register_bool_option(*ir::g_interpreter_prefer_native, LEAN_DEFAULT_INTERPRETER_PREFER_NATIVE, "(interpreter) whether to use precompiled code where available"); @@ -1236,6 +1239,5 @@ void finalize_ir_interpreter() { }); delete ir::g_init_globals; delete ir::g_interpreter_prefer_native; - delete ir::g_boxed_mangled_suffix; } } diff --git a/tests/lean/run/mangling.lean b/tests/lean/run/mangling.lean index b1286eb8d0..3ca056dd49 100644 --- a/tests/lean/run/mangling.lean +++ b/tests/lean/run/mangling.lean @@ -1,15 +1,20 @@ module +import Lean.Compiler.IR.CompilerM import Lean.Compiler.NameMangling /-! # Test behavior of name mangling -/ -def checkMangle (n : Lean.Name) (s : String) : IO Unit := do +open Lean IR ExplicitBoxing +def checkMangle (n : Name) (s : String) : IO Unit := do if n.mangle "" ≠ s then throw <| .userError s!"failed: {n} mangles to {n.mangle ""} but expected {s}" if .demangle s ≠ n then throw <| .userError s!"failed: {s} demangles to {Lean.Name.demangle s} but expected {n}" + if n ≠ .anonymous ∧ mkMangledBoxedName s ≠ (mkBoxedName n).mangle "" then + throw <| .userError s!"failed: {mkBoxedName n} mangles to {(mkBoxedName n).mangle ""} but \ + mkMangledBoxedName produced {mkMangledBoxedName s}" /-! Mangling simple identifiers with optional number components and preceding underscores.