Skip to content
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

Using eval into the current module/package in __init__ causes precompilation error when the package is import-ed in another package #39648

Open
kareevia opened this issue Feb 13, 2021 · 2 comments

Comments

@kareevia
Copy link

kareevia commented Feb 13, 2021

As I understood from the documentation (see https://docs.julialang.org/en/v1/manual/modules/ , the line "Calling eval to cause a side-effect in another module. This will also cause a warning to be emitted when the incremental precompile flag is set."), a module is allowed to eval into itself in its __init__() function. However, it doesn't seem to work.

Here I have 2 packages, Pkg1:

module Pkg1

greet() = print("Hello World!")

function __init__()
  @eval testingvar = "Hello world!"
end

end # module

and Pkg2:

module Pkg2

import Pkg1

greet() = print("Hello World!")

end # module

Trying to precompile Pkg2 causes an error:

(Pkg2) pkg> precompile
Precompiling project...
  Progress [========================================>]  1/1
  ✗ Pkg2
0 dependencies successfully precompiled in 3 seconds (1 already precompiled)

ERROR: The following 1 direct dependency failed to precompile:

Pkg2 [c9c43be6-8fdb-4bf3-a3b4-e57d694db016]

ERROR: LoadError: InitError: Evaluation into the closed module `Pkg1` breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating `Pkg1` with `eval` during precompilation - don't do this.
Stacktrace:
  [1] eval
    @ ./boot.jl:360 [inlined]
  [2] __init__()
    @ Pkg1 ~/ws.local/Julia/bugs/init-bug/Pkg1/src/Pkg1.jl:6
  [3] _include_from_serialized(path::String, depmods::Vector{Any})
    @ Base ./loading.jl:670
  [4] _require_search_from_serialized(pkg::Base.PkgId, sourcepath::String)
    @ Base ./loading.jl:756
  [5] _require(pkg::Base.PkgId)
    @ Base ./loading.jl:994
  [6] require(uuidkey::Base.PkgId)
    @ Base ./loading.jl:910
  [7] require(into::Module, mod::Symbol)
    @ Base ./loading.jl:897
  [8] include
    @ ./Base.jl:386 [inlined]
  [9] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::Nothing)
    @ Base ./loading.jl:1209
 [10] top-level scope
    @ none:1
 [11] eval
    @ ./boot.jl:360 [inlined]
 [12] eval(x::Expr)
    @ Base.MainInclude ./client.jl:446
 [13] top-level scope
    @ none:1
during initialization of module Pkg1
in expression starting at /home/icarus/ws.local/Julia/bugs/init-bug/Pkg2/src/Pkg2.jl:1

versioninfo:

Julia Version 1.6.0-beta1
Commit b84990e1ac (2021-01-08 12:42 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-6560U CPU @ 2.20GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.0 (ORCJIT, skylake)

A side note: evaling even into a local anonymous module causes a similar error. For example, if the Pkg1 would be like this:

module Pkg1

greet() = print("Hello World!")

function __init__()
  @eval Module() testingvar = "Hello world!"
  #@eval testingvar = "Hello world!"
end

end # module

precompilation of Pkg2 would produce this

(Pkg2) pkg> precompile
Precompiling project...
  Progress [========================================>]  2/2
  ✗ Pkg2
1 dependency successfully precompiled in 2 seconds

ERROR: The following 1 direct dependency failed to precompile:

Pkg2 [c9c43be6-8fdb-4bf3-a3b4-e57d694db016]

ERROR: LoadError: InitError: Evaluation into the closed module `anonymous` breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating `anonymous` with `eval` during precompilation - don't do this.
Stacktrace:
  [1] eval
    @ ./boot.jl:360 [inlined]
  [2] __init__()
    @ Pkg1 ~/ws.local/Julia/bugs/init-bug/Pkg1/src/Pkg1.jl:6
  [3] _include_from_serialized(path::String, depmods::Vector{Any})
    @ Base ./loading.jl:670
  [4] _require_search_from_serialized(pkg::Base.PkgId, sourcepath::String)
    @ Base ./loading.jl:756
  [5] _require(pkg::Base.PkgId)
    @ Base ./loading.jl:994
  [6] require(uuidkey::Base.PkgId)
    @ Base ./loading.jl:910
  [7] require(into::Module, mod::Symbol)
    @ Base ./loading.jl:897
  [8] include
    @ ./Base.jl:386 [inlined]
  [9] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::Nothing)
    @ Base ./loading.jl:1209
 [10] top-level scope
    @ none:1
 [11] eval
    @ ./boot.jl:360 [inlined]
 [12] eval(x::Expr)
    @ Base.MainInclude ./client.jl:446
 [13] top-level scope
    @ none:1
during initialization of module Pkg1
in expression starting at /home/icarus/ws.local/Julia/bugs/init-bug/Pkg2/src/Pkg2.jl:1
@Keno
Copy link
Member

Keno commented Feb 14, 2021

You're probably looking for

@eval function __init__

which will happen at evaluation time of the first package. Putting the @eval inside will call eval every time the package is loaded, which is what precompilation is complaining about (since you're modifying a module that's not currently being precompiled).

@kareevia
Copy link
Author

Thank you for your comment and clarifications!

What would @eval function __init__ do? I guess it is an alternative way to define __init__ in the current module? If that so, that is not the application of eval which I considered.

I thought I could use eval in __init__ to initialize some globals of the module in runtime in a more convenient/programmatic way. I didn't find any clear indications that it prohibited to use eval in __init__ for such purposes. Moreover, I thought that the line Calling eval to cause a side-effect in another module. This will also cause a warning to be emitted when the incremental precompile flag is set. indirectly implies, that the module could eval itself in its __init__.

Now, after several re-readings, I see that I have possibly misunderstood the documentation and the cited line from documentation should not be interpreted in context of uses of __init__ function. Then eval seems to be efficiently prohibited for use in __init__ (unless the module is intended to be never imported by another precompilible module). If that is true, the documentation is written quite confusing on that matter (on my opinion).

To summarize, if that is intended behavior (that eval should not be used in __init__), then everything looks working right. Then I would only suggest to indicate that more clearly in documentation, if possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants