Skip to content

Commit d2c8dc8

Browse files
committed
Expand import/using to newer Base runtime calls
Calls to `import` and `using` are expanded by lowering as of the changes in JuliaLang/julia#57965 and no longer dealt with by the C function `jl_toplevel_eval_flex`. This implies we can't use `eval()` for these if we want to activate JuliaLowering in Core, or we'll hit a stack overflow. I've chosen to duplicate the flisp lowering here for consistency and import paths are thus lowered to a restricted kind of quoted `Expr`. (It's mildly annoying to rely on quoted `Expr` in the lowered paths than the previous use of `Core.svec` but deleting the svec representation allows us to use `Base._eval_import` and `Base._eval_using` directly so seems like a worthy simplification.)
1 parent cf29215 commit d2c8dc8

File tree

4 files changed

+55
-81
lines changed

4 files changed

+55
-81
lines changed

src/desugaring.jl

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4098,7 +4098,9 @@ end
40984098
#-------------------------------------------------------------------------------
40994099
# Expand import / using / export
41004100

4101-
function _append_importpath(ctx, path_spec, path)
4101+
function expand_importpath(path)
4102+
@chk kind(path) == K"importpath"
4103+
path_spec = Expr(:.)
41024104
prev_was_dot = true
41034105
for component in children(path)
41044106
k = kind(component)
@@ -4114,14 +4116,15 @@ function _append_importpath(ctx, path_spec, path)
41144116
throw(LoweringError(component, "invalid import path: `.` in identifier path"))
41154117
end
41164118
prev_was_dot = is_dot
4117-
push!(path_spec, @ast(ctx, component, name::K"String"))
4119+
push!(path_spec.args, Symbol(name))
41184120
end
4119-
path_spec
4121+
return path_spec
41204122
end
41214123

4122-
function expand_import(ctx, ex)
4124+
function expand_import_or_using(ctx, ex)
41234125
is_using = kind(ex) == K"using"
4124-
if kind(ex[1]) == K":"
4126+
has_from_path = kind(ex[1]) == K":"
4127+
if has_from_path
41254128
# import M: x.y as z, w
41264129
# (import (: (importpath M) (as (importpath x y) z) (importpath w)))
41274130
# =>
@@ -4131,47 +4134,50 @@ function expand_import(ctx, ex)
41314134
# (call core.svec 2 "x" "y" "z" 1 "w" "w"))
41324135
@chk numchildren(ex[1]) >= 2
41334136
from = ex[1][1]
4134-
@chk kind(from) == K"importpath"
4135-
from_path = @ast ctx from [K"call"
4136-
"svec"::K"core"
4137-
_append_importpath(ctx, SyntaxList(ctx), from)...
4138-
]
4137+
from_path = @ast ctx from QuoteNode(expand_importpath(from))::K"Value"
41394138
paths = ex[1][2:end]
41404139
else
41414140
# import A.B
41424141
# (using (importpath A B))
4143-
# (call module_import true nothing (call core.svec 1 "w"))
4142+
# (call eval_import true nothing (call core.svec 1 "w"))
41444143
@chk numchildren(ex) >= 1
41454144
from_path = nothing_(ctx, ex)
41464145
paths = children(ex)
41474146
end
4148-
path_spec = SyntaxList(ctx)
4149-
for path in paths
4147+
# Here we represent the paths as quoted `Expr` data structures
4148+
path_specs = SyntaxList(ctx)
4149+
for spec in paths
41504150
as_name = nothing
4151-
if kind(path) == K"as"
4152-
@chk numchildren(path) == 2
4153-
as_name = path[2]
4154-
@chk kind(as_name) == K"Identifier"
4155-
path = path[1]
4151+
if kind(spec) == K"as"
4152+
@chk numchildren(spec) == 2
4153+
@chk kind(spec[2]) == K"Identifier"
4154+
as_name = Symbol(spec[2].name_val)
4155+
path = QuoteNode(Expr(:as, expand_importpath(spec[1]), as_name))
4156+
else
4157+
path = QuoteNode(expand_importpath(spec))
41564158
end
4157-
@chk kind(path) == K"importpath"
4158-
push!(path_spec, @ast(ctx, path, numchildren(path)::K"Integer"))
4159-
_append_importpath(ctx, path_spec, path)
4160-
push!(path_spec, isnothing(as_name) ? nothing_(ctx, ex) :
4161-
@ast(ctx, as_name, as_name.name_val::K"String"))
4159+
push!(path_specs, @ast ctx spec path::K"Value")
41624160
end
41634161
@ast ctx ex [K"block"
41644162
[K"assert" "toplevel_only"::K"Symbol" [K"inert" ex]]
4165-
[K"call"
4166-
module_import ::K"Value"
4167-
ctx.mod ::K"Value"
4168-
is_using ::K"Value"
4169-
from_path
4163+
if is_using && !has_from_path
4164+
# Using A, B - rather different from
41704165
[K"call"
4171-
"svec"::K"core"
4172-
path_spec...
4166+
"_eval_using"::K"top"
4167+
ctx.mod ::K"Value"
4168+
path_specs...
41734169
]
4174-
]
4170+
else
4171+
[K"call"
4172+
"_eval_import"::K"top"
4173+
(!is_using) ::K"Bool"
4174+
ctx.mod ::K"Value"
4175+
from_path
4176+
path_specs...
4177+
]
4178+
end
4179+
::K"latestworld"
4180+
[K"removable" "nothing"::K"core"]
41754181
]
41764182
end
41774183

@@ -4421,7 +4427,7 @@ function expand_forms_2(ctx::DesugaringContext, ex::SyntaxTree, docs=nothing)
44214427
elseif k == K"module"
44224428
expand_module(ctx, ex)
44234429
elseif k == K"import" || k == K"using"
4424-
expand_import(ctx, ex)
4430+
expand_import_or_using(ctx, ex)
44254431
elseif k == K"export" || k == K"public"
44264432
expand_public(ctx, ex)
44274433
elseif k == K"abstract" || k == K"primitive"

src/linear_ir.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,9 @@ function compile(ctx::LinearIRContext, ex, needs_value, in_tail_pos)
855855
end
856856
ctx.is_toplevel_thunk && emit_latestworld(ctx, ex)
857857
elseif k == K"latestworld"
858+
if needs_value
859+
throw(LoweringError(ex, "misplaced latestsworld"))
860+
end
858861
emit_latestworld(ctx, ex)
859862
elseif k == K"latestworld_if_toplevel"
860863
ctx.is_toplevel_thunk && emit_latestworld(ctx, ex)

src/runtime.jl

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ end
110110

111111
#--------------------------------------------------
112112
# Functions called by closure conversion
113-
function eval_closure_type(mod, closure_type_name, field_names, field_is_box)
113+
function eval_closure_type(mod::Module, closure_type_name::Symbol, field_names, field_is_box)
114114
type_params = Core.TypeVar[]
115115
field_types = []
116116
for (name, isbox) in zip(field_names, field_is_box)
@@ -129,7 +129,7 @@ function eval_closure_type(mod, closure_type_name, field_names, field_is_box)
129129
false,
130130
length(field_names))
131131
Core._setsuper!(type, Core.Function)
132-
Base.eval(mod, :(const $closure_type_name = $type))
132+
@ccall jl_set_const(mod::Module, closure_type_name::Symbol, type::Any)::Cvoid
133133
Core._typebody!(false, type, Core.svec(field_types...))
134134
type
135135
end
@@ -176,38 +176,6 @@ function eval_module(parentmod, modname, body)
176176
))
177177
end
178178

179-
# Evaluate content of `import` or `using` statement
180-
function module_import(into_mod::Module, is_using::Bool,
181-
from_mod::Union{Nothing,Core.SimpleVector}, paths::Core.SimpleVector)
182-
# For now, this function converts our lowered representation back to Expr
183-
# and calls eval() to avoid replicating all of the fiddly logic in
184-
# jl_toplevel_eval_flex.
185-
# TODO: ccall Julia runtime functions directly?
186-
# * jl_module_using jl_module_use_as
187-
# * import_module jl_module_import_as
188-
path_args = []
189-
i = 1
190-
while i < length(paths)
191-
nsyms = paths[i]::Int
192-
n = i + nsyms
193-
path = Expr(:., [Symbol(paths[i+j]::String) for j = 1:nsyms]...)
194-
as_name = paths[i+nsyms+1]
195-
push!(path_args, isnothing(as_name) ? path :
196-
Expr(:as, path, Symbol(as_name)))
197-
i += nsyms + 2
198-
end
199-
ex = if isnothing(from_mod)
200-
Expr(is_using ? :using : :import,
201-
path_args...)
202-
else
203-
from_path = Expr(:., [Symbol(s::String) for s in from_mod]...)
204-
Expr(is_using ? :using : :import,
205-
Expr(:(:), from_path, path_args...))
206-
end
207-
eval(into_mod, ex)
208-
nothing
209-
end
210-
211179
function module_public(mod::Module, is_exported::Bool, identifiers...)
212180
# symbol jl_module_public is no longer exported as of #57765
213181
eval(mod, Expr((is_exported ? :export : :public), map(Symbol, identifiers)...))

test/import_ir.jl

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,33 @@
22
# Basic import
33
import A: b
44
#---------------------
5-
1 (call core.svec "A")
6-
2 (call core.svec 1 "b" core.nothing)
7-
3 (call JuliaLowering.module_import TestMod false %%₂)
8-
4 (return %₃)
5+
1 (call top._eval_import true TestMod :($(QuoteNode(:($(Expr(:., :A)))))) :($(QuoteNode(:($(Expr(:., :b)))))))
6+
2 latestworld
7+
3 (return core.nothing)
98

109
########################################
1110
# Import with paths and `as`
1211
import A.B.C: b, c.d as e
1312
#---------------------
14-
1 (call core.svec "A" "B" "C")
15-
2 (call core.svec 1 "b" core.nothing 2 "c" "d" "e")
16-
3 (call JuliaLowering.module_import TestMod false %%₂)
17-
4 (return %₃)
13+
1 (call top._eval_import true TestMod :($(QuoteNode(:($(Expr(:., :A, :B, :C)))))) :($(QuoteNode(:($(Expr(:., :b)))))) :($(QuoteNode(:(c.d as e)))))
14+
2 latestworld
15+
3 (return core.nothing)
1816

1917
########################################
2018
# Using
2119
using A
2220
#---------------------
23-
1 (call core.svec 1 "A" core.nothing)
24-
2 (call JuliaLowering.module_import TestMod true core.nothing %₁)
25-
3 (return %)
21+
1 (call top._eval_using TestMod :($(QuoteNode(:($(Expr(:., :A)))))))
22+
2 latestworld
23+
3 (return core.nothing)
2624

2725
########################################
2826
# Using with paths and `as`
2927
using A.B.C: b, c.d as e
3028
#---------------------
31-
1 (call core.svec "A" "B" "C")
32-
2 (call core.svec 1 "b" core.nothing 2 "c" "d" "e")
33-
3 (call JuliaLowering.module_import TestMod true %%₂)
34-
4 (return %₃)
29+
1 (call top._eval_import false TestMod :($(QuoteNode(:($(Expr(:., :A, :B, :C)))))) :($(QuoteNode(:($(Expr(:., :b)))))) :($(QuoteNode(:(c.d as e)))))
30+
2 latestworld
31+
3 (return core.nothing)
3532

3633
########################################
3734
# Error: Import not at top level

0 commit comments

Comments
 (0)