Skip to content

Commit c638398

Browse files
committed
Move import/using logic to Julia; add low-level Core._import, _using
Renames _module_import -> _eval_import and _module_using -> _eval_using (we have jl_module_using and jl_module_using in C and it's confusing enough already), and moves the logic into Julia. Since we need _eval_import very early in bootstrapping, boot.jl provides a minimal version that understands only `import M: a, b, ...`. Some imports in code included by Base_compiler.jl has been updated to match this. The Julia _eval_* functions call two new low-level builtins: - Core._import calls jl_module_import with the normal 5 argument form, or jl_import_module for the special case of importing a package to get a PARTITION_KIND_CONST_IMPORT binding (3 argument form). - Core._using is a direct wrapper around jl_module_using.
1 parent 063c75e commit c638398

File tree

15 files changed

+310
-244
lines changed

15 files changed

+310
-244
lines changed

base/Base_compiler.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ include("ordering.jl")
340340
using .Order
341341

342342
include("coreir.jl")
343+
include("module.jl")
343344
include("invalidation.jl")
344345

345346
BUILDROOT::String = ""

base/boot.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,54 @@ function Symbol(a::Array{UInt8, 1})
695695
end
696696
Symbol(s::Symbol) = s
697697

698+
# Minimal implementations of using/import for bootstrapping (supports only
699+
# `import .M: a, b, c, ...`, little error checking)
700+
let
701+
fail() = throw(ArgumentError("unsupported import/using while bootstrapping"))
702+
length(a::Array{T, 1}) where {T} = getfield(getfield(a, :size), 1)
703+
function getindex(A::Array, i::Int)
704+
Intrinsics.ult_int(Intrinsics.bitcast(UInt, Intrinsics.sub_int(i, 1)), Intrinsics.bitcast(UInt, length(A))) || fail()
705+
memoryrefget(memoryrefnew(getfield(A, :ref), i, false), :not_atomic, false)
706+
end
707+
x == y = Intrinsics.eq_int(x, y)
708+
x + y = Intrinsics.add_int(x, y)
709+
x <= y = Intrinsics.sle_int(x, y)
710+
711+
global function _eval_import(explicit::Bool, to::Module, from::Union{Expr, Nothing}, paths::Expr...)
712+
from isa Expr || fail()
713+
if length(from.args) == 2 && getindex(from.args, 1) === :.
714+
from = getglobal(to, getindex(from.args, 2))
715+
elseif length(from.args) == 1 && getindex(from.args, 1) === :Core
716+
from = Core
717+
elseif length(from.args) == 1 && getindex(from.args, 1) === :Base
718+
from = Main.Base
719+
else
720+
fail()
721+
end
722+
from isa Module || fail()
723+
i = 1
724+
while i <= nfields(paths)
725+
a = getfield(paths, i).args
726+
length(a) == 1 || fail()
727+
s = getindex(a, 1)
728+
Core._import(to, from, s, s, explicit)
729+
i += 1
730+
end
731+
end
732+
733+
global function _eval_using(to::Module, path::Expr)
734+
getindex(path.args, 1) === :. || fail()
735+
from = getglobal(to, getindex(path.args, 2))
736+
i = 3
737+
while i <= length(path.args)
738+
from = getfield(from, getindex(path.args, i))
739+
i += 1
740+
end
741+
from isa Module || fail()
742+
Core._using(to, from)
743+
end
744+
end
745+
698746
# module providing the IR object model
699747
module IR
700748

base/c.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# definitions related to C interface
44

5-
import Core.Intrinsics: cglobal
5+
import .Intrinsics: cglobal
66

77
"""
88
cglobal((symbol, library) [, type=Cvoid])

base/checked.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ export checked_neg, checked_abs, checked_add, checked_sub, checked_mul,
1616
checked_div, checked_rem, checked_fld, checked_mod, checked_cld, checked_pow,
1717
checked_length, add_with_overflow, sub_with_overflow, mul_with_overflow
1818

19-
import Core.Intrinsics:
19+
import Core: Intrinsics
20+
import .Intrinsics:
2021
checked_sadd_int, checked_ssub_int, checked_smul_int, checked_sdiv_int,
2122
checked_srem_int,
2223
checked_uadd_int, checked_usub_int, checked_umul_int, checked_udiv_int,
2324
checked_urem_int
24-
import ..no_op_err, ..@inline, ..@noinline, ..checked_length
25+
import Base: no_op_err, @inline, @noinline, checked_length
2526

2627
# define promotion behavior for checked operations
2728
checked_add(x::Integer, y::Integer) = checked_add(promote(x,y)...)

base/docs/basedocs.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2753,6 +2753,25 @@ See also [`setpropertyonce!`](@ref Base.setpropertyonce!) and [`setglobal!`](@re
27532753
"""
27542754
setglobalonce!
27552755

2756+
"""
2757+
_import(to::Module, from::Module, asname::Symbol, [sym::Symbol, imported::Bool])
2758+
2759+
With all five arguments, imports `sym` from module `from` into `to` with name
2760+
`asname`. `imported` is true for bindings created with `import` (set it to
2761+
false for `using A: ...`).
2762+
2763+
With only the first three arguments, creates a binding for the module `from`
2764+
with name `asname` in `to`.
2765+
"""
2766+
Core._import
2767+
2768+
"""
2769+
_using(to::Module, from::Module)
2770+
2771+
Add `from` to the usings list of `to`.
2772+
"""
2773+
Core._using
2774+
27562775
"""
27572776
typeof(x)
27582777

base/iterators.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Methods for working with Iterators.
66
baremodule Iterators
77

88
# small dance to make this work from Base or Intrinsics
9-
import ..@__MODULE__, ..parentmodule
9+
import Base: @__MODULE__, parentmodule
1010
const Base = parentmodule(@__MODULE__)
1111
using .Base:
1212
@inline, Pair, Pairs, AbstractDict, IndexLinear, IndexStyle, AbstractVector, Vector,
@@ -17,14 +17,14 @@ using .Base:
1717
any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex,
1818
tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape, LazyString,
1919
afoldl
20-
using Core
20+
using .Core
2121
using Core: @doc
2222

23-
using .Base:
24-
cld, fld, resize!, IndexCartesian
25-
using .Base.Checked: checked_mul
23+
using Base:
24+
cld, fld, resize!, IndexCartesian, Checked
25+
using .Checked: checked_mul
2626

27-
import .Base:
27+
import Base:
2828
first, last,
2929
isempty, length, size, axes, ndims,
3030
eltype, IteratorSize, IteratorEltype, promote_typejoin,

base/module.jl

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
# Full-featured versions of _eval_import and _eval_using
4+
5+
for m in methods(Core._eval_import)
6+
delete_method(m)
7+
end
8+
for m in methods(Core._eval_using)
9+
delete_method(m)
10+
end
11+
12+
function call_require(into::Module, name::Symbol)
13+
# TODO: JL_TIMING(LOAD_IMAGE, LOAD_Require)
14+
build_mode = Bool(Base.JLOptions().incremental) && Bool(ccall(:jl_generating_output, Cint, ()))
15+
m = nothing
16+
req_w = unsafe_load(cglobal(:jl_require_world, UInt))
17+
cur_w, tls_w = get_world_counter(), tls_world_age()
18+
if isdefined(Base, :require)
19+
w = build_mode && req_w < tls_w ? req_w : cur_w
20+
m = invoke_in_world(w, getglobal(Base, :require), into, name)
21+
end
22+
m isa Module || error("failed to load module $v")
23+
m
24+
end
25+
26+
function eval_import_path(at::Module, from::Union{Module, Nothing}, path::Expr, keyword::String)
27+
isempty(path.args) && error("malformed import statement")
28+
29+
i = 1
30+
function next!()
31+
i <= length(path.args) || error("invalid module path")
32+
v = path.args[i]
33+
i += 1
34+
v isa Symbol || throw(TypeError(Symbol(keyword), "", Symbol, v))
35+
v
36+
end
37+
v = next!()
38+
m = nothing
39+
40+
if from !== nothing
41+
m = from
42+
elseif v !== :.
43+
# `A.B`: call the loader to obtain the root A in the current environment.
44+
if v === :Core
45+
m = Core
46+
elseif v === :Base
47+
m = Base
48+
else
49+
m = call_require(at, v)
50+
end
51+
i > lastindex(path.args) && return m, nothing
52+
v = next!()
53+
else
54+
# `.A.B.C`: strip off leading dots by following parent links
55+
m = at
56+
while (v = next!()) === :.
57+
m = parentmodule(m)
58+
end
59+
end
60+
61+
while true
62+
v === :. && error("invalid $keyword path: \".\" in identifier path")
63+
i > lastindex(path.args) && break
64+
m = getglobal(m, v)
65+
m isa Module || error("invalid $keyword path: \"$v\" does not name a module")
66+
v = next!()
67+
end
68+
m, v
69+
end
70+
71+
function eval_import_path_all(at::Module, path::Expr, keyword::String)
72+
m, v = eval_import_path(at, nothing, path, keyword)
73+
if v !== nothing
74+
m = getglobal(m, v)
75+
m isa Module || error("invalid $keyword path: \"$v\" does not name a module")
76+
end
77+
m
78+
end
79+
80+
function check_macro_rename(from::Symbol, to::Symbol, keyword::String)
81+
c1(sym) = bitcast(Char, UInt32(unsafe_load(unsafe_convert(Ptr{UInt8}, sym))) << 24)
82+
from_c, to_c = c1(from), c1(to)
83+
if from_c == '@' && to_c != '@'
84+
error("cannot rename macro \"$from\" to non-macro \"$to\" in \"$keyword\"")
85+
end
86+
if from_c != '@' && to_c == '@'
87+
error("cannot rename non-macro \"$from\" to macro \"$to\" in \"$keyword\"")
88+
end
89+
end
90+
91+
"""
92+
_eval_import(imported::Bool, to::Module, from::Union{Expr, Nothing}, paths::Expr...)
93+
94+
Evaluate the import paths, calling `Core._import` for each name to be imported.
95+
`imported` imports are created with `import`, `using A: x` sets this to false.
96+
The `from` is the part of the import path before the `:`. This is the lowered
97+
form of `import`, `import ...:`, and `using ...:`.
98+
99+
```
100+
import A => _eval_import(true, Main, nothing, Expr(:., :A))
101+
import A.b => _eval_import(true, Main, nothing, Expr(:., :A, :b))
102+
import A.b as c => _eval_import(true, Main, nothing, Expr(:as, Expr(:., :A, :b), :c))
103+
import A.B: C.d, e => _eval_import(true, Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))
104+
import A.B: C.d as e => _eval_import(true, Main, Expr(:., :A, :B), Expr(:as, Expr(:., :C, :d), :e))
105+
using A.B: C.d, e => _eval_import(false, Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))
106+
107+
See also [`_import`](@ref Core._import).
108+
```
109+
"""
110+
function Core._eval_import(imported::Bool, to::Module, from::Union{Expr, Nothing}, paths::Expr...)
111+
keyword = imported ? "import" : "using"
112+
fail() = error("malformed \"$keyword\" statement")
113+
from = from !== nothing ? eval_import_path_all(to, from, keyword) : nothing
114+
115+
for path in paths
116+
path isa Expr || fail()
117+
asname = nothing
118+
if path.head === :as && length(path.args) == 2
119+
path, asname = path.args
120+
elseif path.head !== :.
121+
fail()
122+
end
123+
old_from = from
124+
from, name = eval_import_path(to, from, path, keyword)
125+
asname = asname === nothing ? name : asname
126+
127+
if name !== nothing
128+
check_macro_rename(name, asname, keyword)
129+
Core._import(to, from, asname, name, imported)
130+
else
131+
Core._import(to, from, nameof(from))
132+
end
133+
end
134+
end
135+
136+
"""
137+
_eval_using(to::Module, path::Expr)
138+
139+
Evaluate the import path to a module and call [`Core._using`](@ref) on it,
140+
making its exports available to the `to` module; this is the lowered form of
141+
`using A`.
142+
143+
```
144+
using A.B => _module_using(Main, Expr(:., :A, :B))
145+
```
146+
147+
See also [`_using`](@ref Core._using).
148+
"""
149+
function Core._eval_using(to::Module, path::Expr)
150+
from = eval_import_path_all(to, path, "using")
151+
Core._using(to, from)
152+
is_package = length(path.args) == 1 && path.args[1] !== :.
153+
if to == Main && is_package
154+
Core._import(to, from, nameof(from))
155+
end
156+
end

base/ordering.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module Order
44

55

6-
import ..@__MODULE__, ..parentmodule
6+
import Base: @__MODULE__, parentmodule
77
const Base = parentmodule(@__MODULE__)
88
import .Base:
99
AbstractVector, @propagate_inbounds, isless, identity, getindex, reverse,

src/builtin_proto.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ JL_CALLABLE(jl_f__equiv_typedef);
8484
JL_CALLABLE(jl_f_get_binding_type);
8585
JL_CALLABLE(jl_f__compute_sparams);
8686
JL_CALLABLE(jl_f__svec_ref);
87-
JL_CALLABLE(jl_f__module_import);
88-
JL_CALLABLE(jl_f__module_using);
87+
JL_CALLABLE(jl_f__import);
88+
JL_CALLABLE(jl_f__using);
8989
#ifdef __cplusplus
9090
}
9191
#endif

0 commit comments

Comments
 (0)