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.
152 lines
5.1 KiB
Text
152 lines
5.1 KiB
Text
/-
|
|
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Leonardo de Moura, Mac Malone
|
|
-/
|
|
module
|
|
|
|
prelude
|
|
public import Lean.Data.Json.Parser
|
|
public import Lean.Util.LeanOptions
|
|
|
|
set_option doc.verso true
|
|
|
|
public section
|
|
|
|
/-!
|
|
# Module Setup Information
|
|
|
|
Data types used by Lean module headers and the `--setup` CLI.
|
|
-/
|
|
|
|
namespace Lean
|
|
|
|
/- Abstract structure of an `import` statement. -/
|
|
structure Import where
|
|
module : Name
|
|
/-- `import all`; whether to import and expose all data saved by the module. -/
|
|
importAll : Bool := false
|
|
/-- Whether to activate this import when the current module itself is imported. -/
|
|
isExported : Bool := true
|
|
/-- Whether to import IR for all definitions (transitively) reachable. -/
|
|
isMeta : Bool := false
|
|
deriving Repr, Inhabited, ToJson, FromJson,
|
|
BEq, Hashable -- needed by Lake (in `Lake.Load.Elab.Lean`)
|
|
|
|
instance : Coe Name Import := ⟨({module := ·})⟩
|
|
|
|
instance : ToString Import := ⟨fun imp =>
|
|
s!"{if imp.isExported then "public " else ""}{if imp.isMeta then "meta " else ""}import \
|
|
{if imp.importAll then "all " else ""}{imp.module}"⟩
|
|
|
|
/-- Abstract structure of a module's header. -/
|
|
structure ModuleHeader where
|
|
/-- The module's direct imports (i.e., those listed in the header). -/
|
|
imports : Array Import
|
|
/-- Whether the module is participating in the module system. -/
|
|
isModule : Bool
|
|
deriving Repr, Inhabited, ToJson, FromJson
|
|
|
|
/--
|
|
Module data files used for an {lit}`import` statement.
|
|
This structure is designed for efficient JSON serialization.
|
|
-/
|
|
structure ImportArtifacts where
|
|
ofArray ::
|
|
toArray : Array System.FilePath
|
|
deriving Repr, Inhabited
|
|
|
|
instance : ToJson ImportArtifacts := ⟨(toJson ·.toArray)⟩
|
|
instance : FromJson ImportArtifacts := ⟨(.ofArray <$> fromJson? ·)⟩
|
|
|
|
def ImportArtifacts.size (arts : ImportArtifacts) :=
|
|
arts.toArray.size
|
|
|
|
def ImportArtifacts.olean? (arts : ImportArtifacts) :=
|
|
arts.toArray[0]?
|
|
|
|
def ImportArtifacts.ir? (arts : ImportArtifacts) :=
|
|
arts.toArray[1]?
|
|
|
|
def ImportArtifacts.oleanServer? (arts : ImportArtifacts) :=
|
|
arts.toArray[2]?
|
|
|
|
def ImportArtifacts.oleanPrivate? (arts : ImportArtifacts) :=
|
|
arts.toArray[3]?
|
|
|
|
def ImportArtifacts.oleanParts (inServer : Bool) (arts : ImportArtifacts) : Array System.FilePath := Id.run do
|
|
let mut fnames := #[]
|
|
if let some mFile := arts.olean? then
|
|
fnames := fnames.push mFile
|
|
if let some sFile := arts.oleanServer? then
|
|
-- For uniformity, Lake always provides us with .olean.server, so load it only when we are in
|
|
-- server mode or we need it to load further files.
|
|
if inServer || arts.oleanPrivate?.isSome then
|
|
fnames := fnames.push sFile
|
|
if let some pFile := arts.oleanPrivate? then
|
|
fnames := fnames.push pFile
|
|
return fnames
|
|
|
|
/-- Files containing data for a single module. -/
|
|
structure ModuleArtifacts where
|
|
lean? : Option System.FilePath := none
|
|
olean? : Option System.FilePath := none
|
|
oleanServer? : Option System.FilePath := none
|
|
oleanPrivate? : Option System.FilePath := none
|
|
ilean? : Option System.FilePath := none
|
|
ir? : Option System.FilePath := none
|
|
c? : Option System.FilePath := none
|
|
bc? : Option System.FilePath := none
|
|
deriving Repr, Inhabited, ToJson, FromJson
|
|
|
|
def ModuleArtifacts.oleanParts (arts : ModuleArtifacts) : Array System.FilePath := Id.run do
|
|
let mut fnames := #[]
|
|
if let some mFile := arts.olean? then
|
|
fnames := fnames.push mFile
|
|
if let some sFile := arts.oleanServer? then
|
|
fnames := fnames.push sFile
|
|
if let some pFile := arts.oleanPrivate? then
|
|
fnames := fnames.push pFile
|
|
return fnames
|
|
|
|
/--
|
|
The type of module package identifiers.
|
|
|
|
This is a {name}`String` that is used to disambiguate native symbol prefixes between
|
|
different packages (and different versions of the same package).
|
|
-/
|
|
public abbrev PkgId := String
|
|
|
|
/-- A module's setup information as described by a JSON file. -/
|
|
structure ModuleSetup where
|
|
/-- The name of the module. -/
|
|
name : Name
|
|
/-- The package to which the module belongs (if any). -/
|
|
package? : Option PkgId := none
|
|
/--
|
|
Whether the module, by default, participates in the module system.
|
|
Even if {lean}`false`, a module can still choose to participate by using
|
|
{lit}`module` in its header.
|
|
-/
|
|
isModule : Bool := false
|
|
/--
|
|
The module's direct imports.
|
|
If {lean}`none`, uses the imports from the module header.
|
|
-/
|
|
imports? : Option (Array Import) := none
|
|
/-- Pre-resolved artifacts of transitively imported modules. -/
|
|
importArts : NameMap ImportArtifacts := {}
|
|
/-- Dynamic libraries to load with the module. -/
|
|
dynlibs : Array System.FilePath := #[]
|
|
/-- Plugins to initialize with the module. -/
|
|
plugins : Array System.FilePath := #[]
|
|
/-- Additional options for the module. -/
|
|
options : LeanOptions := {}
|
|
deriving Repr, Inhabited, ToJson, FromJson
|
|
|
|
/-- Load a {lean}`ModuleSetup` from a JSON file. -/
|
|
def ModuleSetup.load (path : System.FilePath) : IO ModuleSetup := do
|
|
let contents ← IO.FS.readFile path
|
|
match Json.parse contents >>= fromJson? with
|
|
| .ok info => pure info
|
|
| .error msg => throw <| IO.userError s!"failed to load header from {path}: {msg}"
|