What is closed module?

I have a package named MyPkg, which depends on MyPkg2. When I load MyPkg, I get the following error:

julia> using MyPkg
[ Info: Precompiling MyPkg [<MyPkg uuid>]
ERROR: LoadError: Evaluation into the closed module `MyPkg2` breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating `MyPkg2` with `eval` during precompilation - don't do this.

I tried to construct a MWE reproducing this error, but packages defined in REPL don’t seem to produce this error.

This makes me question the meaning of “closed module” in the error message: does it refer to a module defined as a package, compared to a module defined in REPL?

1 Like

So that is likely what is happening. Some package is doing eval(MyPkg2, ...) while it is being precompiled.

1 Like

I know eval is producing the error. I am trying to update the package such that it achieves the desired functionality without producing this error. For that, I was trying to reproduce the same error by modules defined in REPL that have similar structures as my packages. Once I have the modules producing the same error, I would be able to tweak the modules until I remove the error.

However, I am not able to produce the same error by the modules defined in REPL. (The reported error message is from packages.) So, I was wondering if this is because my modules in REPL are too simplified to mimic the behaviors of the packages, or because the modules defined in REPL are not “closed modules” that the error message is mentioning. If the latter is the case, I would never be able to reproduce the error by modules defined in REPL, so my attempt to find the solution to the error by tweaking modules defined in REPL will be in vain.

I think that is the important part. Your modules in the REPL are presumably not getting precompiled.

1 Like

That’s a good point. Then do you think the term “closed module” does not bear any particular technical meaning?

I have found (1.11 and 1.10) that any module other than the one currently being precompiled, is considered closed, even a locally used Module() object is considered closed. However, submodules of the current module are open, which you can use as temporary modules.

Make sure you’re testing with a Pkg loaded package, not a REPL defined module.

module PkgTest4
using Inherit

init_fn::Union{Function, Nothing} = nothing

macro greet()
	if !isdefined(@__MODULE__, :shadow)
		Core.eval(@__MODULE__, :(
			module shadow
			end
			# using .shadow
		))
	end
	# tmpmod = Module()
	# Core.eval(tmpmod, :(println("inside temp module ")))  #NOTE: cannot eval into a temp module
	Core.eval(shadow, :(@warn("eval inside $(@__MODULE__) "))) #can eval into the current module or submodule during compile time (not yet closed)
	if isprecompiling()
		@info("Hello macro! (precompiling)")
		global init_fn = eval(:(() -> println("I'm a function created inside a macro"))) #don't use @info in code that will be called from __init__()
	else
		@info("Hello macro! (not precompiling)")  #no problem with @info here
	end
end

function init_greet()
	if isprecompiling()
		println("Hello fn! (precompiling)")		#there's no compilation with println, but 200ms with @info even inside a function!
	else
		println("Hello fn! (not precompiling)")
	end
end

@greet

function __init__()
	init_greet()	#1.5ms no compilation with println, but very high with @info!
	init_fn()
	@greet	# does nothing because the macro returns nothing
	# println(1 + 2)		#15ms
	# @info("PkgTest4.__init__")	#190ms
end

@info("bottom of file")


end # module PkgTest4

@show @__DIR__
cd(joinpath(@__DIR__, ".."))
@show pwd()

using Pkg
Pkg.activate(".")

begin
	@time import PackageExtensionsExample
	@time import Inherit
	@time import PkgTest4
end
begin
	import PkgTest4
	@time PkgTest4.@greet	#prints "Hello macro! (not precompiling)" because it's being compiled as a script statement
end

I mean… don’t use eval from inside macros. It’s almost never what you want to do. Hard to know what you’re trying to do, but I’d highly recommend avoiding this problem altogether by avoiding eval in macros.

6 Likes