This PR prevents symbol clashes between (non-`@[export]`) definitions from different Lean packages. Previously, if two modules define a function with the same name and were transitively imported (even privately) by some downstream module, linking would fail due to a symbol clash. Similarly, if a user defined a symbol with the same name as one in the `Lean` library, Lean would use the core symbol even if one did not import `Lean`. This is solved by changing Lean's name mangling algorithm to include an optional package identifier. This identifier is provided by Lake via `--setup` when building a module. This information is weaved through the elaborator, interpreter, and compiler via a persistent environment extension that associates modules with their package identifier. With a package identifier, standard symbols have the form `lp_<pkg-id>_<mangled-def>`. Without one, the old scheme is used (i.e., `l_<mangled-def>`). Module initializers are also prefixed with package identifier (if any). For example, the initializer for a module `Foo` in a package `test` is now `initialize_test_Foo` (instead of `initialize_Foo`). Lake's default for native library names has also been adjusted accordingly, so that libraries can still, by default, be used as plugins. Thus, the default library name of the `lean_lib Foo` in `package test` will now be `libtest_Foo`. When using Lake to build the Lean core (i.e., `bootstrap = true`), no package identifier will be used. Thus, definitions in user packages can never have symbol clashes with core. Closes #222.
69 lines
2.5 KiB
Text
69 lines
2.5 KiB
Text
/-
|
||
Copyright (c) 2025 Mac Malone. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Mac Malone
|
||
-/
|
||
module
|
||
|
||
prelude
|
||
public import Lean.Environment
|
||
import Lean.Compiler.NameMangling
|
||
|
||
set_option doc.verso true
|
||
|
||
namespace Lean
|
||
|
||
/-- Persistent environment extension for storing a single serializable value per module. -/
|
||
@[expose] public def ModuleEnvExtension (σ : Type) := PersistentEnvExtension σ σ σ
|
||
|
||
public def registerModuleEnvExtension
|
||
[Inhabited σ] (mkInitial : IO σ) (name : Name := by exact decl_name%)
|
||
: IO (ModuleEnvExtension σ) :=
|
||
registerPersistentEnvExtension {
|
||
name := name
|
||
mkInitial := mkInitial
|
||
addImportedFn := fun _ _ => mkInitial
|
||
addEntryFn := fun s _ => s
|
||
exportEntriesFn := fun s => #[s]
|
||
}
|
||
|
||
namespace ModuleEnvExtension
|
||
|
||
public instance [Inhabited σ] : Inhabited (ModuleEnvExtension σ) :=
|
||
inferInstanceAs (Inhabited (PersistentEnvExtension ..))
|
||
|
||
public def getStateByIdx? [Inhabited σ] (ext : ModuleEnvExtension σ) (env : Environment) (idx : ModuleIdx) : Option σ :=
|
||
(ext.getModuleEntries env idx)[0]?
|
||
|
||
end ModuleEnvExtension
|
||
|
||
private initialize modPkgExt : ModuleEnvExtension (Option PkgId) ←
|
||
registerModuleEnvExtension (pure none)
|
||
|
||
/-- Returns the package (if any) of an imported module (by its index). -/
|
||
public def Environment.getModulePackageByIdx? (env : Environment) (idx : ModuleIdx) : Option PkgId :=
|
||
modPkgExt.getStateByIdx? env idx |>.join
|
||
|
||
/-- Returns the package (if any) of the current module. -/
|
||
@[inline] public def Environment.getModulePackage? (env : Environment) : Option PkgId :=
|
||
modPkgExt.getState env
|
||
|
||
/-- Sets the package of the current module. -/
|
||
@[inline] public def Environment.setModulePackage (pkg? : Option PkgId) (env : Environment) : Environment :=
|
||
modPkgExt.setState env pkg?
|
||
|
||
/--
|
||
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]`).
|
||
-/
|
||
@[export lean_get_symbol_stem]
|
||
public def getSymbolStem (env : Environment) (declName : Name) : String :=
|
||
let pkg? :=
|
||
match env.getModuleIdxFor? declName with
|
||
| some idx => env.getModulePackageByIdx? idx
|
||
| none => env.getModulePackage?
|
||
declName.mangle (mkPackageSymbolPrefix pkg?)
|