julia
julia copied to clipboard
Module inside a `begin ... end` leads to false-positive parsing error
module Test
begin
module Inner
end
end
end
julia> include("Test.jl")
ERROR: LoadError: syntax: "module" expression not at top level
Stacktrace:
[1] top-level scope
@ ~/Test.jl:2
[2] include(fname::String)
@ Base.MainInclude ./client.jl:444
[3] top-level scope
@ REPL[1]:1
in expression starting at /home/vchuravy/Test.jl:1
Two issues:
- The line-number is the outer module not the inner module
- I believe that
begin...endshould be neutral here and we are still in top-level
If we move the inner module into it's own file. Inner.jl and include it instead this works without issue,
Encountered while parsing and re-evaluating some test-code
cc: @gbaraldi
There's also the following lowering error:
julia> :(begin
module Inner
end
end)
quote
#= REPL[12]:2 =#
module Inner
#= REPL[12]:2 =#
#= REPL[12]:2 =#
end
end
julia> Meta.lower(Main, :(begin
module Inner
end
end))
:($(Expr(:error, "\"module\" expression not at top level")))
We'd run into this lowering error if the code above parsed. Making the desired expression by hand, we have:
julia> :(module Test
begin
$(Expr(:module, true, :Inner, Expr(:block)))
end
end)
:(module Test
#= REPL[22]:1 =#
#= REPL[22]:2 =#
begin
#= REPL[22]:3 =#
module Inner
end
end
end)
julia> eval(:(module Test
begin
$(Expr(:module, true, :Inner, Expr(:block)))
end
end))
WARNING: replacing module Test.
ERROR: syntax: "module" expression not at top level
Stacktrace:
[1] top-level scope
@ REPL[23]:2
[2] eval
@ ./boot.jl:432 [inlined]
[3] eval(x::Expr)
@ Main ./sysimg.jl:48
[4] top-level scope
@ REPL[23]:1
I think these are bugs in the existing system? module should be able to be nested inside top level begin ... end blocks because every statement inside such a block is evaluated in the latest world, due to being inside a top level thunk.
But that "everything in the latest world" notion of top-level isn't enough - modules shouldn't be able to exist inside nontrivial scopes like let blocks. Otherwise users might expect the following to work
let
x = 100
module B
import ..x
y = x
end
end
by analogy to the case of nested modules
module A
x = 100
module B
import ..x
y = x
end
end
Or even this
let
x = 100
module B
y = x
end
end
Perhaps there's no fundamental reason this couldn't work, but it changes/generalizes several nontrivial things about the way lowering and evaluation of modules currently works. And is probably a bunch of work for fairly marginal utility.
But that "everything in the latest world" notion of top-level isn't enough - modules shouldn't be able to exist inside nontrivial scopes like let blocks.
Actually we've got at least one other construct which is allowed at top level - including inside begin ... end blocks - but not inside local scopes
julia> let
macro foo()
10
end
end
ERROR: syntax: macro definition not allowed inside a local scope
Stacktrace:
[1] top-level scope
@ REPL[39]:1
julia> begin
macro foo()
10
end
end
@foo (macro with 1 method)
Presumably the rules for modules would be the same.
I implemented the "modules are only allowed at global toplevel" rule in JuliaLowering.jl, and used the same mechanism for macros.
https://github.com/c42f/JuliaLowering.jl/commit/658a7c9d022c8ff296ef18f7a28146946c158838