diff --git a/Compiler/src/Compiler.jl b/Compiler/src/Compiler.jl index a5a5f1b09a1b5..9c3581677132e 100644 --- a/Compiler/src/Compiler.jl +++ b/Compiler/src/Compiler.jl @@ -65,7 +65,7 @@ using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospeciali structdiff, tls_world_age, unconstrain_vararg_length, unionlen, uniontype_layout, uniontypes, unsafe_convert, unwrap_unionall, unwrapva, vect, widen_diagonal, _uncompressed_ir, maybe_add_binding_backedge!, datatype_min_ninitialized, - partialstruct_init_undefs, fieldcount_noerror + partialstruct_init_undefs, fieldcount_noerror, _eval_import, _eval_using using Base.Order import Base: ==, _topmod, append!, convert, copy, copy!, findall, first, get, get!, diff --git a/base/Base_compiler.jl b/base/Base_compiler.jl index e22a7d980f06c..28c07379c54d9 100644 --- a/base/Base_compiler.jl +++ b/base/Base_compiler.jl @@ -2,6 +2,9 @@ module Base +Core._import(Base, Core, :_eval_import, :_eval_import, true) +Core._import(Base, Core, :_eval_using, :_eval_using, true) + using .Core.Intrinsics, .Core.IR # to start, we're going to use a very simple definition of `include` @@ -340,6 +343,7 @@ include("ordering.jl") using .Order include("coreir.jl") +include("module.jl") include("invalidation.jl") BUILDROOT::String = "" diff --git a/base/boot.jl b/base/boot.jl index f9208c3874229..566839d430e93 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -695,6 +695,54 @@ function Symbol(a::Array{UInt8, 1}) end Symbol(s::Symbol) = s +# Minimal implementations of using/import for bootstrapping (supports only +# `import .M: a, b, c, ...`, little error checking) +let + fail() = throw(ArgumentError("unsupported import/using while bootstrapping")) + length(a::Array{T, 1}) where {T} = getfield(getfield(a, :size), 1) + function getindex(A::Array, i::Int) + Intrinsics.ult_int(Intrinsics.bitcast(UInt, Intrinsics.sub_int(i, 1)), Intrinsics.bitcast(UInt, length(A))) || fail() + memoryrefget(memoryrefnew(getfield(A, :ref), i, false), :not_atomic, false) + end + x == y = Intrinsics.eq_int(x, y) + x + y = Intrinsics.add_int(x, y) + x <= y = Intrinsics.sle_int(x, y) + + global function _eval_import(explicit::Bool, to::Module, from::Union{Expr, Nothing}, paths::Expr...) + from isa Expr || fail() + if length(from.args) == 2 && getindex(from.args, 1) === :. + from = getglobal(to, getindex(from.args, 2)) + elseif length(from.args) == 1 && getindex(from.args, 1) === :Core + from = Core + elseif length(from.args) == 1 && getindex(from.args, 1) === :Base + from = Main.Base + else + fail() + end + from isa Module || fail() + i = 1 + while i <= nfields(paths) + a = getfield(paths, i).args + length(a) == 1 || fail() + s = getindex(a, 1) + Core._import(to, from, s, s, explicit) + i += 1 + end + end + + global function _eval_using(to::Module, path::Expr) + getindex(path.args, 1) === :. || fail() + from = getglobal(to, getindex(path.args, 2)) + i = 3 + while i <= length(path.args) + from = getfield(from, getindex(path.args, i)) + i += 1 + end + from isa Module || fail() + Core._using(to, from) + end +end + # module providing the IR object model module IR diff --git a/base/c.jl b/base/c.jl index 78c48f267ca71..69ea3adf24404 100644 --- a/base/c.jl +++ b/base/c.jl @@ -2,7 +2,7 @@ # definitions related to C interface -import Core.Intrinsics: cglobal +import .Intrinsics: cglobal """ cglobal((symbol, library) [, type=Cvoid]) diff --git a/base/checked.jl b/base/checked.jl index b374d34830280..39d487cba6e37 100644 --- a/base/checked.jl +++ b/base/checked.jl @@ -16,12 +16,13 @@ export checked_neg, checked_abs, checked_add, checked_sub, checked_mul, checked_div, checked_rem, checked_fld, checked_mod, checked_cld, checked_pow, checked_length, add_with_overflow, sub_with_overflow, mul_with_overflow -import Core.Intrinsics: +import Core: Intrinsics +import .Intrinsics: checked_sadd_int, checked_ssub_int, checked_smul_int, checked_sdiv_int, checked_srem_int, checked_uadd_int, checked_usub_int, checked_umul_int, checked_udiv_int, checked_urem_int -import ..no_op_err, ..@inline, ..@noinline, ..checked_length +import Base: no_op_err, @inline, @noinline, checked_length # define promotion behavior for checked operations checked_add(x::Integer, y::Integer) = checked_add(promote(x,y)...) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index cf8a087b76489..60770c42bae7d 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2753,6 +2753,25 @@ See also [`setpropertyonce!`](@ref Base.setpropertyonce!) and [`setglobal!`](@re """ setglobalonce! +""" + _import(to::Module, from::Module, asname::Symbol, [sym::Symbol, imported::Bool]) + +With all five arguments, imports `sym` from module `from` into `to` with name +`asname`. `imported` is true for bindings created with `import` (set it to +false for `using A: ...`). + +With only the first three arguments, creates a binding for the module `from` +with name `asname` in `to`. +""" +Core._import + +""" + _using(to::Module, from::Module) + +Add `from` to the usings list of `to`. +""" +Core._using + """ typeof(x) diff --git a/base/iterators.jl b/base/iterators.jl index c7450781c4928..4b956073f0c04 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -6,7 +6,7 @@ Methods for working with Iterators. baremodule Iterators # small dance to make this work from Base or Intrinsics -import ..@__MODULE__, ..parentmodule +import Base: @__MODULE__, parentmodule const Base = parentmodule(@__MODULE__) using .Base: @inline, Pair, Pairs, AbstractDict, IndexLinear, IndexStyle, AbstractVector, Vector, @@ -17,14 +17,14 @@ using .Base: any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex, tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape, LazyString, afoldl -using Core +using .Core using Core: @doc -using .Base: - cld, fld, resize!, IndexCartesian -using .Base.Checked: checked_mul +using Base: + cld, fld, resize!, IndexCartesian, Checked +using .Checked: checked_mul -import .Base: +import Base: first, last, isempty, length, size, axes, ndims, eltype, IteratorSize, IteratorEltype, promote_typejoin, diff --git a/base/loading.jl b/base/loading.jl index b26a063247169..eed486098b35d 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2331,7 +2331,7 @@ function require(into::Module, mod::Symbol) if world == typemax(UInt) world = get_world_counter() end - return invoke_in_world(world, __require, into, mod) + return Compiler.@zone "LOAD_Require" invoke_in_world(world, __require, into, mod) end function check_for_hint(into, mod) diff --git a/base/module.jl b/base/module.jl new file mode 100644 index 0000000000000..3e95d9882339c --- /dev/null +++ b/base/module.jl @@ -0,0 +1,143 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Full-featured versions of _eval_import and _eval_using + +for m in methods(_eval_import) + delete_method(m) +end +for m in methods(_eval_using) + delete_method(m) +end + +function eval_import_path(at::Module, from::Union{Module, Nothing}, path::Expr, keyword::String) + isempty(path.args) && error("malformed import statement") + + i::Int = 1 + function next!() + i <= length(path.args) || error("invalid module path") + v = path.args[i] + i += 1 + v isa Symbol || throw(TypeError(Symbol(keyword), "", Symbol, v)) + v + end + v = next!() + m = nothing + + if from !== nothing + m = from + elseif v !== :. + # `A.B`: call the loader to obtain the root A in the current environment. + if v === :Core + m = Core + elseif v === :Base + m = Base + else + m = require(at, v) + m isa Module || error("failed to load module $v") + end + i > lastindex(path.args) && return m, nothing + v = next!() + else + # `.A.B.C`: strip off leading dots by following parent links + m = at + while (v = next!()) === :. + m = parentmodule(m) + end + end + + while true + v === :. && error("invalid $keyword path: \".\" in identifier path") + i > lastindex(path.args) && break + m = getglobal(m, v) + m isa Module || error("invalid $keyword path: \"$v\" does not name a module") + v = next!() + end + m, v +end + +function eval_import_path_all(at::Module, path::Expr, keyword::String) + m, v = eval_import_path(at, nothing, path, keyword) + if v !== nothing + m = getglobal(m, v) + m isa Module || error("invalid $keyword path: \"$v\" does not name a module") + end + m +end + +function check_macro_rename(from::Symbol, to::Symbol, keyword::String) + c1(sym) = bitcast(Char, UInt32(unsafe_load(unsafe_convert(Ptr{UInt8}, sym))) << 24) + from_c, to_c = c1(from), c1(to) + if from_c == '@' && to_c != '@' + error("cannot rename macro \"$from\" to non-macro \"$to\" in \"$keyword\"") + end + if from_c != '@' && to_c == '@' + error("cannot rename non-macro \"$from\" to macro \"$to\" in \"$keyword\"") + end +end + +""" + _eval_import(imported::Bool, to::Module, from::Union{Expr, Nothing}, paths::Expr...) + +Evaluate the import paths, calling `Core._import` for each name to be imported. +`imported` imports are created with `import`, `using A: x` sets this to false. +The `from` is the part of the import path before the `:`. This is the lowered +form of `import`, `import ...:`, and `using ...:`. + +``` +import A => _eval_import(true, Main, nothing, Expr(:., :A)) +import A.b => _eval_import(true, Main, nothing, Expr(:., :A, :b)) +import A.b as c => _eval_import(true, Main, nothing, Expr(:as, Expr(:., :A, :b), :c)) +import A.B: C.d, e => _eval_import(true, Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e)) +import A.B: C.d as e => _eval_import(true, Main, Expr(:., :A, :B), Expr(:as, Expr(:., :C, :d), :e)) +using A.B: C.d, e => _eval_import(false, Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e)) + +See also [`_import`](@ref Core._import). +``` +""" +function _eval_import(imported::Bool, to::Module, from::Union{Expr, Nothing}, paths::Expr...) + keyword = imported ? "import" : "using" + fail() = error("malformed \"$keyword\" statement") + from = from !== nothing ? eval_import_path_all(to, from, keyword) : nothing + + for path in paths + path isa Expr || fail() + asname = nothing + if path.head === :as && length(path.args) == 2 + path, asname = path.args + elseif path.head !== :. + fail() + end + old_from = from + from, name = eval_import_path(to, from, path, keyword) + + if name !== nothing + asname = asname === nothing ? name : asname + check_macro_rename(name, asname, keyword) + Core._import(to, from, asname, name, imported) + else + Core._import(to, from, asname === nothing ? nameof(from) : asname) + end + end +end + +""" + _eval_using(to::Module, path::Expr) + +Evaluate the import path to a module and call [`Core._using`](@ref) on it, +making its exports available to the `to` module; this is the lowered form of +`using A`. + +``` +using A.B => _module_using(Main, Expr(:., :A, :B)) +``` + +See also [`_using`](@ref Core._using). +""" +function _eval_using(to::Module, path::Expr) + from = eval_import_path_all(to, path, "using") + Core._using(to, from) + is_package = length(path.args) == 1 && path.args[1] !== :. + if to == Main && is_package + Core._import(to, from, nameof(from)) + end +end diff --git a/base/ordering.jl b/base/ordering.jl index 19e8a1cf18109..f2ddd20ab09f0 100644 --- a/base/ordering.jl +++ b/base/ordering.jl @@ -3,7 +3,7 @@ module Order -import ..@__MODULE__, ..parentmodule +import Base: @__MODULE__, parentmodule const Base = parentmodule(@__MODULE__) import .Base: AbstractVector, @propagate_inbounds, isless, identity, getindex, reverse, diff --git a/src/ast.c b/src/ast.c index b7edaac43f134..37621d6778b2f 100644 --- a/src/ast.c +++ b/src/ast.c @@ -30,7 +30,6 @@ JL_DLLEXPORT jl_sym_t *jl_module_sym; JL_DLLEXPORT jl_sym_t *jl_slot_sym; JL_DLLEXPORT jl_sym_t *jl_export_sym; JL_DLLEXPORT jl_sym_t *jl_public_sym; -JL_DLLEXPORT jl_sym_t *jl_import_sym; JL_DLLEXPORT jl_sym_t *jl_toplevel_sym; JL_DLLEXPORT jl_sym_t *jl_quote_sym; JL_DLLEXPORT jl_sym_t *jl_line_sym; @@ -51,7 +50,6 @@ JL_DLLEXPORT jl_sym_t *jl_pop_exception_sym; JL_DLLEXPORT jl_sym_t *jl_exc_sym; JL_DLLEXPORT jl_sym_t *jl_error_sym; JL_DLLEXPORT jl_sym_t *jl_new_sym; -JL_DLLEXPORT jl_sym_t *jl_using_sym; JL_DLLEXPORT jl_sym_t *jl_splatnew_sym; JL_DLLEXPORT jl_sym_t *jl_block_sym; JL_DLLEXPORT jl_sym_t *jl_new_opaque_closure_sym; @@ -349,8 +347,6 @@ void jl_init_common_symbols(void) jl_module_sym = jl_symbol("module"); jl_export_sym = jl_symbol("export"); jl_public_sym = jl_symbol("public"); - jl_import_sym = jl_symbol("import"); - jl_using_sym = jl_symbol("using"); jl_assign_sym = jl_symbol("="); jl_method_sym = jl_symbol("method"); jl_exc_sym = jl_symbol("the_exception"); diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 586d948f722c1..607106f35bac0 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -16,12 +16,14 @@ extern "C" { XX(_defaultctors,"_defaultctors") \ XX(_equiv_typedef,"_equiv_typedef") \ XX(_expr,"_expr") \ + XX(_import, "_import") \ XX(_primitivetype,"_primitivetype") \ XX(_setsuper,"_setsuper!") \ XX(_structtype,"_structtype") \ XX(_svec_ref,"_svec_ref") \ XX(_typebody,"_typebody!") \ XX(_typevar,"_typevar") \ + XX(_using, "_using") \ XX(applicable,"applicable") \ XX(apply_type,"apply_type") \ XX(compilerbarrier,"compilerbarrier") \ diff --git a/src/builtins.c b/src/builtins.c index e3a0380182e15..36b5d79ec0851 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -3,6 +3,8 @@ /* implementations of built-in functions */ +#include "dtypes.h" +#include "julia_atomics.h" #include "platform.h" #include @@ -1504,7 +1506,44 @@ JL_CALLABLE(jl_f_setglobalonce) return old == NULL ? jl_true : jl_false; } +// import, using -------------------------------------------------------------- +// Import binding `from.sym` as `asname` into `to`: +// _import(to::Module, from::Module, asname::Symbol, sym::Symbol, imported::Bool) +// +// Create const binding to `mod` in `to` with name `asname`: +// _import(to::Module, mod::Module, asname::Symbol) +JL_CALLABLE(jl_f__import) +{ + JL_NARGS(_import, 3, 5); + JL_TYPECHK(_import, module, args[0]); + JL_TYPECHK(_import, module, args[1]); + JL_TYPECHK(_import, symbol, args[2]); + if (nargs == 3) { + jl_import_module(jl_current_task, (jl_module_t *)args[0], (jl_module_t *)args[1], + (jl_sym_t *)args[2]); + } + else if (nargs == 4) { + jl_too_few_args("_import", 5); + } + else if (nargs == 5) { + JL_TYPECHK(_import, symbol, args[3]); + JL_TYPECHK(_import, bool, args[4]); + jl_module_import(jl_current_task, (jl_module_t *)args[0], (jl_module_t *)args[1], + (jl_sym_t *)args[2], (jl_sym_t *)args[3], args[4] == jl_true); + } + return jl_nothing; +} + +// _using(to::Module, from::Module) +JL_CALLABLE(jl_f__using) +{ + JL_NARGS(_using, 2, 2); + JL_TYPECHK(_using, module, args[0]); + JL_TYPECHK(_using, module, args[1]); + jl_module_using((jl_module_t *)args[0], (jl_module_t *)args[1]); + return jl_nothing; +} // apply_type ----------------------------------------------------------------- diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 6cb9ab630a4b1..64dcb2278d240 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -311,7 +311,6 @@ XX(jl_module_parent) \ XX(jl_module_getloc) \ XX(jl_module_public_p) \ - XX(jl_module_use) \ XX(jl_module_using) \ XX(jl_module_usings) \ XX(jl_module_uuid) \ diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 4fa6dc2e14a70..3b05f19e5456e 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2521,7 +2521,7 @@ `(= ,lhs ,rhs))) (define (expand-forms e) - (if (or (atom? e) (memq (car e) '(quote inert top core globalref module toplevel ssavalue null true false meta using import export public thismodule toplevel-only))) + (if (or (atom? e) (memq (car e) '(quote inert top core globalref module toplevel ssavalue null true false meta export public thismodule toplevel-only))) e (let ((ex (get expand-table (car e) #f))) (if ex @@ -2541,6 +2541,20 @@ (define (something e) (find (lambda (x) (not (equal? x '(null)))) e)) +(define (check-import-paths what e) + (define (check-dot-path e) + (and (list? e) (eq? (car e) '|.|) (every symbol? (cdr e)))) + (define (check-path e) + (and (pair? e) + (or (check-dot-path e) + (and (eq? (car e) 'as) + (check-dot-path (cadr e)) (symbol? (caddr e)))))) + (unless (and (list? e) + (or (every check-path e) + (and (list? (car e)) (eq? (caar e) ':) + (every check-path (cdar e))))) + (error (string "malformed \"" what "\" statement")))) + ;; table mapping expression head to a function expanding that form (define expand-table (table @@ -2939,6 +2953,38 @@ (lambda (e) (set! *current-desugar-loc* e) e) + + ;; We insert (latestworld) after every call to _eval_import or _eval_using + ;; to avoid having to do it in eval_import_path (#57316) + 'import + (lambda (e) + (check-import-paths "import" (cdr e)) + `(block + (toplevel-only import) + ,.(if (eq? (caadr e) ':) + `((call (top _eval_import) (true) (thismodule) + ,.(map (lambda (x) `(inert ,x)) (cdadr e))) + (latestworld)) + (map (lambda (x) + `(block + (call (top _eval_import) (true) (thismodule) (null) (inert ,x)) + (latestworld))) + (cdr e))))) + + 'using + (lambda (e) + (check-import-paths "using" (cdr e)) + `(block + (toplevel-only using) + ,.(if (eq? (caadr e) ':) + `((call (top _eval_import) (false) (thismodule) + ,.(map (lambda (x) `(inert ,x)) (cdadr e))) + (latestworld)) + (map (lambda (x) + `(block + (call (top _eval_using) (thismodule) (inert ,x)) + (latestworld))) + (cdr e))))) )) (define (has-return? e) @@ -3183,7 +3229,7 @@ (check-valid-name (cadr e)) ;; remove local decls '(null)) - ((memq (car e) '(using import export public)) + ((memq (car e) '(export public)) ;; no scope resolution - identifiers remain raw symbols e) ((eq? (car e) 'require-existing-local) @@ -3815,7 +3861,7 @@ f(x) = yt(x) thunk with-static-parameters toplevel-only global globalref global-if-global assign-const-if-global isglobal thismodule const atomic null true false ssavalue isdefined toplevel module lambda - error gc_preserve_begin gc_preserve_end import using export public inline noinline purity))) + error gc_preserve_begin gc_preserve_end export public inline noinline purity))) (define (local-in? s lam (tab #f)) (or (and tab (has? tab s)) @@ -4047,7 +4093,7 @@ f(x) = yt(x) ((atom? e) e) (else (case (car e) - ((quote top core global globalref thismodule lineinfo line break inert module toplevel null true false meta import using) e) + ((quote top core global globalref thismodule lineinfo line break inert module toplevel null true false meta) e) ((toplevel-only) ;; hack to avoid generating a (method x) expr for struct types (if (eq? (cadr e) 'struct) @@ -5045,7 +5091,7 @@ f(x) = yt(x) '(null)) ;; other top level expressions - ((import using export public latestworld) + ((export public latestworld) (check-top-level e) (if (not (eq? (car e) 'latestworld)) (emit e)) @@ -5288,7 +5334,7 @@ f(x) = yt(x) ((nospecialize-meta? e) ;; convert nospecialize vars to slot numbers `(meta ,(cadr e) ,@(map renumber-stuff (cddr e)))) - ((or (atom? e) (quoted? e) (memq (car e) '(using import export public global toplevel))) + ((or (atom? e) (quoted? e) (memq (car e) '(export public global toplevel))) e) ((ssavalue? e) (let ((idx (get ssavalue-table (cadr e) #f))) diff --git a/src/julia.h b/src/julia.h index 195c23f20662f..faad02c0aa50c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2111,11 +2111,9 @@ JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED); JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED); JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, enum jl_partition_kind); +JL_DLLEXPORT void jl_module_import(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *asname, jl_sym_t *s, int explici); +JL_DLLEXPORT void jl_import_module(jl_task_t *ct, jl_module_t *m, jl_module_t *import, jl_sym_t *asname); JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from); -JL_DLLEXPORT void jl_module_use(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *s); -JL_DLLEXPORT void jl_module_use_as(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname); -JL_DLLEXPORT void jl_module_import(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *s); -JL_DLLEXPORT void jl_module_import_as(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname); int jl_module_public_(jl_module_t *from, jl_sym_t *s, int exported, size_t new_world); JL_DLLEXPORT int jl_is_imported(jl_module_t *m, jl_sym_t *s); JL_DLLEXPORT int jl_module_exports_p(jl_module_t *m, jl_sym_t *var); diff --git a/src/julia_internal.h b/src/julia_internal.h index 5d418ceb2051c..b588f84d05319 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1869,7 +1869,6 @@ extern JL_DLLEXPORT jl_sym_t *jl_module_sym; extern JL_DLLEXPORT jl_sym_t *jl_slot_sym; extern JL_DLLEXPORT jl_sym_t *jl_export_sym; extern JL_DLLEXPORT jl_sym_t *jl_public_sym; -extern JL_DLLEXPORT jl_sym_t *jl_import_sym; extern JL_DLLEXPORT jl_sym_t *jl_toplevel_sym; extern JL_DLLEXPORT jl_sym_t *jl_quote_sym; extern JL_DLLEXPORT jl_sym_t *jl_line_sym; @@ -1891,7 +1890,6 @@ extern JL_DLLEXPORT jl_sym_t *jl_pop_exception_sym; extern JL_DLLEXPORT jl_sym_t *jl_exc_sym; extern JL_DLLEXPORT jl_sym_t *jl_error_sym; extern JL_DLLEXPORT jl_sym_t *jl_new_sym; -extern JL_DLLEXPORT jl_sym_t *jl_using_sym; extern JL_DLLEXPORT jl_sym_t *jl_splatnew_sym; extern JL_DLLEXPORT jl_sym_t *jl_block_sym; extern JL_DLLEXPORT jl_sym_t *jl_new_opaque_closure_sym; diff --git a/src/module.c b/src/module.c index 4c685ca574523..108f0f3c17275 100644 --- a/src/module.c +++ b/src/module.c @@ -1192,7 +1192,7 @@ static int eq_bindings(jl_binding_partition_t *owner, jl_binding_t *alias, size_ } // NOTE: we use explici since explicit is a C++ keyword -static void module_import_(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *asname, jl_sym_t *s, int explici) +JL_DLLEXPORT void jl_module_import(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *asname, jl_sym_t *s, int explici) { check_safe_import_from(from); jl_binding_t *b = jl_get_binding(from, s); @@ -1269,24 +1269,27 @@ static void module_import_(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl JL_UNLOCK(&world_counter_lock); } -JL_DLLEXPORT void jl_module_import(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *s) +JL_DLLEXPORT void jl_import_module(jl_task_t *ct, jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym_t *asname) { - module_import_(ct, to, from, s, s, 1); -} - -JL_DLLEXPORT void jl_module_import_as(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname) -{ - module_import_(ct, to, from, asname, s, 1); -} - -JL_DLLEXPORT void jl_module_use(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *s) -{ - module_import_(ct, to, from, s, s, 0); -} - -JL_DLLEXPORT void jl_module_use_as(jl_task_t *ct, jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname) -{ - module_import_(ct, to, from, asname, s, 0); + assert(m); + jl_sym_t *name = asname ? asname : import->name; + // TODO: this is a bit race-y with what error message we might print + jl_binding_t *b = jl_get_module_binding(m, name, 1); + size_t world = jl_atomic_load_acquire(&jl_world_counter); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, world); + enum jl_partition_kind kind = jl_binding_kind(bpart); + if (!jl_bkind_is_some_implicit(kind) && kind != PARTITION_KIND_DECLARED) { + // Unlike regular constant declaration, we allow this as long as we eventually end up at a constant. + jl_walk_binding_inplace(&b, &bpart, world); + if (jl_bkind_is_some_constant(jl_binding_kind(bpart))) { + // Already declared (e.g. on another thread) or imported. + if (bpart->restriction == (jl_value_t*)import) + return; + } + jl_errorf("importing %s into %s conflicts with an existing global", + jl_symbol_name(name), jl_symbol_name(m->name)); + } + jl_declare_constant_val2(b, m, name, (jl_value_t*)import, PARTITION_KIND_CONST_IMPORT); } void jl_add_usings_backedge(jl_module_t *from, jl_module_t *to) diff --git a/src/toplevel.c b/src/toplevel.c index 64a559ac774e6..2e0370aee8a5d 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -478,110 +478,10 @@ static void body_attributes(jl_array_t *body, int *has_ccall, int *has_defs, int *forced_compile = jl_has_meta(body, jl_force_compile_sym); } -extern size_t jl_require_world; -static jl_module_t *call_require(jl_task_t *ct, jl_module_t *mod, jl_sym_t *var) JL_GLOBALLY_ROOTED -{ - JL_TIMING(LOAD_IMAGE, LOAD_Require); - jl_timing_printf(JL_TIMING_DEFAULT_BLOCK, "%s", jl_symbol_name(var)); - - int build_mode = jl_options.incremental && jl_generating_output(); - jl_module_t *m = NULL; - static jl_value_t *require_func = NULL; - if (require_func == NULL && jl_base_module != NULL) { - require_func = jl_get_global(jl_base_module, jl_symbol("require")); - } - if (require_func != NULL) { - ct->world_age = jl_atomic_load_acquire(&jl_world_counter); - if (build_mode && jl_require_world < ct->world_age) - ct->world_age = jl_require_world; - jl_value_t *reqargs[3]; - reqargs[0] = require_func; - reqargs[1] = (jl_value_t*)mod; - reqargs[2] = (jl_value_t*)var; - m = (jl_module_t*)jl_apply(reqargs, 3); - } - if (m == NULL || !jl_is_module(m)) { - jl_errorf("failed to load module %s", jl_symbol_name(var)); - } - ct->world_age = jl_atomic_load_acquire(&jl_world_counter); - return m; -} - -// either: -// - sets *name and returns the module to import *name from -// - sets *name to NULL and returns a module to import -// also updates world_age -static jl_module_t *eval_import_path(jl_task_t *ct, jl_module_t *where, jl_module_t *from JL_PROPAGATES_ROOT, - jl_array_t *args, jl_sym_t **name, const char *keyword) JL_GLOBALLY_ROOTED -{ - if (jl_array_nrows(args) == 0) - jl_errorf("malformed \"%s\" statement", keyword); - jl_sym_t *var = (jl_sym_t*)jl_array_ptr_ref(args, 0); - size_t i = 1; - jl_module_t *m = NULL; - *name = NULL; - if (!jl_is_symbol(var)) - jl_type_error(keyword, (jl_value_t*)jl_symbol_type, (jl_value_t*)var); - - if (from != NULL) { - m = from; - i = 0; - } - else if (var != jl_dot_sym) { - // `A.B`: call the loader to obtain the root A in the current environment. - if (jl_core_module && var == jl_core_module->name) { - m = jl_core_module; - } - else if (jl_base_module && var == jl_base_module->name) { - m = jl_base_module; - } - else { - m = call_require(ct, where, var); - } - if (i == jl_array_nrows(args)) - return m; - } - else { - // `.A.B.C`: strip off leading dots by following parent links - m = where; - while (1) { - if (i >= jl_array_nrows(args)) - jl_error("invalid module path"); - var = (jl_sym_t*)jl_array_ptr_ref(args, i); - if (var != jl_dot_sym) - break; - i++; - assert(m); - m = m->parent; - } - } - - ct->world_age = jl_atomic_load_acquire(&jl_world_counter); - - while (1) { - var = (jl_sym_t*)jl_array_ptr_ref(args, i); - if (!jl_is_symbol(var)) - jl_type_error(keyword, (jl_value_t*)jl_symbol_type, (jl_value_t*)var); - if (var == jl_dot_sym) - jl_errorf("invalid %s path: \".\" in identifier path", keyword); - if (i == jl_array_nrows(args)-1) - break; - m = (jl_module_t*)jl_eval_global_var(m, var); - JL_GC_PROMISE_ROOTED(m); - if (!jl_is_module(m)) - jl_errorf("invalid %s path: \"%s\" does not name a module", keyword, jl_symbol_name(var)); - i++; - } - *name = var; - return m; -} - int jl_is_toplevel_only_expr(jl_value_t *e) JL_NOTSAFEPOINT { return jl_is_expr(e) && (((jl_expr_t*)e)->head == jl_module_sym || - ((jl_expr_t*)e)->head == jl_import_sym || - ((jl_expr_t*)e)->head == jl_using_sym || ((jl_expr_t*)e)->head == jl_export_sym || ((jl_expr_t*)e)->head == jl_public_sym || ((jl_expr_t*)e)->head == jl_thunk_sym || @@ -599,10 +499,9 @@ int jl_needs_lowering(jl_value_t *e) JL_NOTSAFEPOINT return 0; jl_expr_t *ex = (jl_expr_t*)e; jl_sym_t *head = ex->head; - if (head == jl_module_sym || head == jl_import_sym || head == jl_using_sym || - head == jl_export_sym || head == jl_public_sym || head == jl_thunk_sym || - head == jl_toplevel_sym || head == jl_error_sym || head == jl_incomplete_sym || - head == jl_method_sym) { + if (head == jl_module_sym || head == jl_export_sym || head == jl_public_sym || + head == jl_thunk_sym || head == jl_toplevel_sym || head == jl_error_sym || + head == jl_incomplete_sym || head == jl_method_sym) { return 0; } if (head == jl_global_sym || head == jl_const_sym) { @@ -642,62 +541,6 @@ JL_DLLEXPORT jl_method_instance_t *jl_method_instance_for_thunk(jl_code_info_t * return mi; } -static void import_module(jl_task_t *ct, jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym_t *asname) -{ - assert(m); - jl_sym_t *name = asname ? asname : import->name; - // TODO: this is a bit race-y with what error message we might print - jl_binding_t *b = jl_get_module_binding(m, name, 1); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, ct->world_age); - enum jl_partition_kind kind = jl_binding_kind(bpart); - if (!jl_bkind_is_some_implicit(kind) && kind != PARTITION_KIND_DECLARED) { - // Unlike regular constant declaration, we allow this as long as we eventually end up at a constant. - jl_walk_binding_inplace(&b, &bpart, ct->world_age); - if (jl_bkind_is_some_constant(jl_binding_kind(bpart))) { - // Already declared (e.g. on another thread) or imported. - if (bpart->restriction == (jl_value_t*)import) - return; - } - jl_errorf("importing %s into %s conflicts with an existing global", - jl_symbol_name(name), jl_symbol_name(m->name)); - } - jl_declare_constant_val2(b, m, name, (jl_value_t*)import, PARTITION_KIND_CONST_IMPORT); -} - -// in `import A.B: x, y, ...`, evaluate the `A.B` part if it exists -static jl_module_t *eval_import_from(jl_task_t *ct, jl_module_t *m JL_PROPAGATES_ROOT, jl_expr_t *ex, const char *keyword) -{ - if (jl_expr_nargs(ex) == 1 && jl_is_expr(jl_exprarg(ex, 0))) { - jl_expr_t *fr = (jl_expr_t*)jl_exprarg(ex, 0); - if (fr->head == jl_colon_sym) { - if (jl_expr_nargs(fr) > 0 && jl_is_expr(jl_exprarg(fr, 0))) { - jl_expr_t *path = (jl_expr_t*)jl_exprarg(fr, 0); - if (((jl_expr_t*)path)->head == jl_dot_sym) { - jl_sym_t *name = NULL; - jl_module_t *from = eval_import_path(ct, m, NULL, path->args, &name, keyword); - if (name != NULL) { - from = (jl_module_t*)jl_eval_global_var(from, name); - if (!from || !jl_is_module(from)) - jl_errorf("invalid %s path: \"%s\" does not name a module", keyword, jl_symbol_name(name)); - } - return from; - } - } - jl_errorf("malformed \"%s:\" statement", keyword); - } - } - return NULL; -} - -static void check_macro_rename(jl_sym_t *from, jl_sym_t *to, const char *keyword) -{ - char *n1 = jl_symbol_name(from), *n2 = jl_symbol_name(to); - if (n1[0] == '@' && n2[0] != '@') - jl_errorf("cannot rename macro \"%s\" to non-macro \"%s\" in \"%s\"", n1, n2, keyword); - if (n1[0] != '@' && n2[0] == '@') - jl_errorf("cannot rename non-macro \"%s\" to macro \"%s\" in \"%s\"", n1, n2, keyword); -} - // Eval `throw(ErrorException(msg)))` in module `m`. // Used in `jl_toplevel_eval_flex` instead of `jl_throw` so that the error // location in julia code gets into the backtrace. @@ -818,111 +661,6 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val JL_GC_POP(); return val; } - else if (head == jl_using_sym) { - jl_sym_t *name = NULL; - jl_module_t *from = eval_import_from(ct, m, ex, "using"); - size_t i = 0; - if (from) { - i = 1; - ex = (jl_expr_t*)jl_exprarg(ex, 0); - } - for (; i < jl_expr_nargs(ex); i++) { - jl_value_t *a = jl_exprarg(ex, i); - if (jl_is_expr(a) && ((jl_expr_t*)a)->head == jl_dot_sym) { - name = NULL; - jl_module_t *import = eval_import_path(ct, m, from, ((jl_expr_t*)a)->args, &name, "using"); - if (from) { - // `using A: B` and `using A: B.c` syntax - jl_module_use(ct, m, import, name); - } - else { - jl_module_t *u = import; - if (name != NULL) - u = (jl_module_t*)jl_eval_global_var(import, name); - if (!jl_is_module(u)) - jl_eval_errorf(m, *toplevel_filename, *toplevel_lineno, - "invalid using path: \"%s\" does not name a module", - jl_symbol_name(name)); - // `using A` and `using A.B` syntax - jl_module_using(m, u); - if (m == jl_main_module && name == NULL) { - // TODO: for now, `using A` in Main also creates an explicit binding for `A` - // This will possibly be extended to all modules. - import_module(ct, m, u, NULL); - } - } - continue; - } - else if (from && jl_is_expr(a) && ((jl_expr_t*)a)->head == jl_as_sym && jl_expr_nargs(a) == 2 && - jl_is_expr(jl_exprarg(a, 0)) && ((jl_expr_t*)jl_exprarg(a, 0))->head == jl_dot_sym) { - jl_sym_t *asname = (jl_sym_t*)jl_exprarg(a, 1); - if (jl_is_symbol(asname)) { - jl_expr_t *path = (jl_expr_t*)jl_exprarg(a, 0); - name = NULL; - jl_module_t *import = eval_import_path(ct, m, from, ((jl_expr_t*)path)->args, &name, "using"); - assert(name); - check_macro_rename(name, asname, "using"); - // `using A: B as C` syntax - jl_module_use_as(ct, m, import, name, asname); - continue; - } - } - jl_eval_errorf(m, *toplevel_filename, *toplevel_lineno, - "syntax: malformed \"using\" statement"); - } - JL_GC_POP(); - ct->world_age = last_age; - return jl_nothing; - } - else if (head == jl_import_sym) { - jl_sym_t *name = NULL; - jl_module_t *from = eval_import_from(ct, m, ex, "import"); - size_t i = 0; - if (from) { - i = 1; - ex = (jl_expr_t*)jl_exprarg(ex, 0); - } - for (; i < jl_expr_nargs(ex); i++) { - jl_value_t *a = jl_exprarg(ex, i); - if (jl_is_expr(a) && ((jl_expr_t*)a)->head == jl_dot_sym) { - name = NULL; - jl_module_t *import = eval_import_path(ct, m, from, ((jl_expr_t*)a)->args, &name, "import"); - if (name == NULL) { - // `import A` syntax - import_module(ct, m, import, NULL); - } - else { - // `import A.B` or `import A: B` syntax - jl_module_import(ct, m, import, name); - } - continue; - } - else if (jl_is_expr(a) && ((jl_expr_t*)a)->head == jl_as_sym && jl_expr_nargs(a) == 2 && - jl_is_expr(jl_exprarg(a, 0)) && ((jl_expr_t*)jl_exprarg(a, 0))->head == jl_dot_sym) { - jl_sym_t *asname = (jl_sym_t*)jl_exprarg(a, 1); - if (jl_is_symbol(asname)) { - jl_expr_t *path = (jl_expr_t*)jl_exprarg(a, 0); - name = NULL; - jl_module_t *import = eval_import_path(ct, m, from, ((jl_expr_t*)path)->args, &name, "import"); - if (name == NULL) { - // `import A as B` syntax - import_module(ct, m, import, asname); - } - else { - check_macro_rename(name, asname, "import"); - // `import A.B as C` syntax - jl_module_import_as(ct, m, import, name, asname); - } - continue; - } - } - jl_eval_errorf(m, *toplevel_filename, *toplevel_lineno, - "syntax: malformed \"import\" statement"); - } - JL_GC_POP(); - ct->world_age = last_age; - return jl_nothing; - } else if (head == jl_export_sym || head == jl_public_sym) { int exp = (head == jl_export_sym); volatile int any_new = 0; diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index f6314ed92ee53..8e4bf4fd9db85 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -360,26 +360,31 @@ function check_for_missing_packages_and_run_hooks(ast) end function _modules_to_be_loaded!(ast::Expr, mods::Vector{Symbol}) + function add!(ctx) + if ctx.head == :as + ctx = ctx.args[1] + end + if ctx.args[1] != :. # don't include local import `import .Foo` + push!(mods, ctx.args[1]) + end + end ast.head === :quote && return mods # don't search if it's not going to be run during this eval - if ast.head === :using || ast.head === :import - for arg in ast.args - arg = arg::Expr - arg1 = first(arg.args) - if arg1 isa Symbol # i.e. `Foo` - if arg1 != :. # don't include local import `import .Foo` - push!(mods, arg1) - end - else # i.e. `Foo: bar` - sym = first((arg1::Expr).args)::Symbol - if sym != :. # don't include local import `import .Foo: a` - push!(mods, sym) - end + if ast.head == :call + if length(ast.args) == 5 && ast.args[1] === GlobalRef(Base, :_eval_import) + ctx = ast.args[4] + if ctx isa QuoteNode # i.e. `Foo: bar` + ctx = ctx.value + else + ctx = ast.args[5].value end + add!(ctx) + elseif length(ast.args) == 3 && ast.args[1] == GlobalRef(Base, :_eval_using) + add!(ast.args[3].value) end end if ast.head !== :thunk for arg in ast.args - if isexpr(arg, (:block, :if, :using, :import)) + if isexpr(arg, (:block, :if)) _modules_to_be_loaded!(arg, mods) end end diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index ee787e55d78c8..b2172096a2e6b 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1550,59 +1550,61 @@ end @testset "Install missing packages via hooks" begin @testset "Parse AST for packages" begin - mods = REPL.modules_to_be_loaded(Base.parse_input_line("using Foo")) + test_find_packages(e) = + REPL.modules_to_be_loaded(Meta.lower(@__MODULE__, e)) + test_find_packages(s::String) = + REPL.modules_to_be_loaded(Meta.lower(@__MODULE__, Meta.parse(s))) + + mods = test_find_packages("using Foo") @test mods == [:Foo] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("import Foo")) + mods = test_find_packages("import Foo") @test mods == [:Foo] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("using Foo, Bar")) + mods = test_find_packages("using Foo, Bar") @test mods == [:Foo, :Bar] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("import Foo, Bar")) + mods = test_find_packages("import Foo, Bar") @test mods == [:Foo, :Bar] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("using Foo.bar, Foo.baz")) + mods = test_find_packages("using Foo.bar, Foo.baz") @test mods == [:Foo] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("if false using Foo end")) + mods = test_find_packages("if false using Foo end") @test mods == [:Foo] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("if false if false using Foo end end")) + mods = test_find_packages("if false if false using Foo end end") @test mods == [:Foo] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("if false using Foo, Bar end")) + mods = test_find_packages("if false using Foo, Bar end") @test mods == [:Foo, :Bar] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("if false using Foo: bar end")) + mods = test_find_packages("if false using Foo: bar end") @test mods == [:Foo] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("import Foo.bar as baz")) + mods = test_find_packages("import Foo.bar as baz") @test mods == [:Foo] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("using .Foo")) + mods = test_find_packages("using .Foo") @test isempty(mods) - mods = REPL.modules_to_be_loaded(Base.parse_input_line("using Base")) + mods = test_find_packages("using Base") @test isempty(mods) - mods = REPL.modules_to_be_loaded(Base.parse_input_line("using Base: nope")) + mods = test_find_packages("using Base: nope") @test isempty(mods) - mods = REPL.modules_to_be_loaded(Base.parse_input_line("using Main")) + mods = test_find_packages("using Main") @test isempty(mods) - mods = REPL.modules_to_be_loaded(Base.parse_input_line("using Core")) + mods = test_find_packages("using Core") @test isempty(mods) - mods = REPL.modules_to_be_loaded(Base.parse_input_line(":(using Foo)")) - @test isempty(mods) - mods = REPL.modules_to_be_loaded(Base.parse_input_line("ex = :(using Foo)")) + mods = test_find_packages(":(using Foo)") @test isempty(mods) - - mods = REPL.modules_to_be_loaded(Base.parse_input_line("Foo")) + mods = test_find_packages("ex = :(using Foo)") @test isempty(mods) - mods = REPL.modules_to_be_loaded(Base.parse_input_line("@eval using Foo")) + mods = test_find_packages("@eval using Foo") @test isempty(mods) - mods = REPL.modules_to_be_loaded(Base.parse_input_line("begin using Foo; @eval using Bar end")) + mods = test_find_packages("begin using Foo; @eval using Bar end") @test mods == [:Foo] - mods = REPL.modules_to_be_loaded(Base.parse_input_line("Core.eval(Main,\"using Foo\")")) + mods = test_find_packages("Core.eval(Main,\"using Foo\")") @test isempty(mods) - mods = REPL.modules_to_be_loaded(Base.parse_input_line("begin using Foo; Core.eval(Main,\"using Foo\") end")) + mods = test_find_packages("begin using Foo; Core.eval(Main,\"using Foo\") end") @test mods == [:Foo] - mods = REPL.modules_to_be_loaded(:(import .Foo: a)) + mods = test_find_packages(:(import .Foo: a)) @test isempty(mods) - mods = REPL.modules_to_be_loaded(:(using .Foo: a)) + mods = test_find_packages(:(using .Foo: a)) @test isempty(mods) end end diff --git a/sysimage.mk b/sysimage.mk index 550373bfba588..987ca4035870e 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -45,6 +45,7 @@ COMPILER_SRCS := $(addprefix $(JULIAHOME)/, \ base/indices.jl \ base/iterators.jl \ base/invalidation.jl \ + base/module.jl \ base/namedtuple.jl \ base/number.jl \ base/operators.jl \ diff --git a/test/loading.jl b/test/loading.jl index d12cd2769ef1d..2dcf129a741a9 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1731,3 +1731,9 @@ end rm(depot_path, force=true, recursive=true) end end + +# Test `import Package as M` +module M57965 + import Random as R +end +@test M57965.R === Base.require(M57965, :Random) diff --git a/test/worlds.jl b/test/worlds.jl index dd98170721b1a..42cc74a010670 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -558,3 +558,12 @@ module C57316; import ..X57316.Y57316 as Z, .Z.Y57316 as W; end @test !isdefined(B57316, :X57316) @test !isdefined(C57316, :X57316) @test !isdefined(C57316, :Y57316) + +# jl_module_import should always manipulate the latest world +module M57965 +function f() + @eval Random = 1 + Core._eval_import(true, @__MODULE__, nothing, Expr(:., :Random)) +end +end +@test_throws ErrorException("importing Random into M57965 conflicts with an existing global") M57965.f()s