Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions base/experimental.jl
Original file line number Diff line number Diff line change
Expand Up @@ -695,4 +695,55 @@ function wait_with_timeout(c::GenericCondition; first::Bool=false, timeout::Real
end
end

"""
Base.Experimental.@reexport using Module
Automatically re-export all exported names from a module when using it.
# Examples
```jldoctest
julia> module A
export foo
foo() = "foo from A"
end
A
julia> module B
using Base.Experimental: @reexport
@reexport using ..A
# Now B exports foo, even though it's defined in A
end
B
julia> using .B
julia> foo()
"foo from A"
```
!!! warning
This interface is experimental and subject to change or removal without notice.
"""
macro reexport(ex)
if !Meta.isexpr(ex, :using) || isempty(ex.args)
error("@reexport must be used with a `using` statement, e.g., `@reexport using MyModule`")
end

# Check for `using Foo: x, y` syntax (not supported)
if any(arg -> Meta.isexpr(arg, :(:)), ex.args)
error("@reexport does not support `using Module: names` syntax")
end

# Generate _eval_using calls for each module in the using statement
calls = Expr(:block)
for mod_path in ex.args
push!(calls.args, :($(Core._eval_using)($(__module__), $(QuoteNode(mod_path)), $(Base.JL_MODULE_USING_REEXPORT))))
end
push!(calls.args, Expr(:latestworld))
push!(calls.args, :nothing)

return esc(calls)
end

end # module
4 changes: 2 additions & 2 deletions base/module.jl
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ using A.B => _module_using(Main, Expr(:., :A, :B))
See also [`_using`](@ref Core._using).
"""
function _eval_using(to::Module, path::Expr)
function _eval_using(to::Module, path::Expr, flags::UInt8=UInt8(0))
from = eval_import_path_all(to, path, "using")
Core._using(to, from)
Core._using(to, from, flags)
is_package = length(path.args) == 1 && path.args[1] !== :.
if to == Main && is_package
Core._import(to, from, nameof(from))
Expand Down
3 changes: 3 additions & 0 deletions base/runtime_internals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,15 @@ const PARTITION_KIND_BACKDATED_CONST = 0xb
const PARTITION_FLAG_EXPORTED = 0x10
const PARTITION_FLAG_DEPRECATED = 0x20
const PARTITION_FLAG_DEPWARN = 0x40
const PARTITION_FLAG_IMPLICITLY_EXPORTED = 0x80

const PARTITION_MASK_KIND = 0x0f
const PARTITION_MASK_FLAG = 0xf0

const BINDING_FLAG_ANY_IMPLICIT_EDGES = 0x8

const JL_MODULE_USING_REEXPORT = 0x1

is_defined_const_binding(kind::UInt8) = (kind == PARTITION_KIND_CONST || kind == PARTITION_KIND_CONST_IMPORT || kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_BACKDATED_CONST)
is_some_const_binding(kind::UInt8) = (is_defined_const_binding(kind) || kind == PARTITION_KIND_UNDEF_CONST)
is_some_imported(kind::UInt8) = (kind == PARTITION_KIND_IMPLICIT_GLOBAL || kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_EXPLICIT || kind == PARTITION_KIND_IMPORTED)
Expand Down
4 changes: 4 additions & 0 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3294,6 +3294,10 @@ function print_partition(io::IO, partition::Core.BindingPartition)
if (partition.kind & PARTITION_FLAG_EXPORTED) != 0
print(io, "exported")
end
if (partition.kind & PARTITION_FLAG_IMPLICITLY_EXPORTED) != 0
first ? (first = false) : print(io, ",")
print(io, "re-exported")
end
if (partition.kind & PARTITION_FLAG_DEPRECATED) != 0
first ? (first = false) : print(io, ",")
print(io, "deprecated")
Expand Down
9 changes: 7 additions & 2 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1579,10 +1579,15 @@ JL_CALLABLE(jl_f__import)
// _using(to::Module, from::Module)
JL_CALLABLE(jl_f__using)
{
JL_NARGS(_using, 2, 2);
JL_NARGS(_using, 2, 3);
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]);
size_t flags = 0;
if (nargs == 3) {
JL_TYPECHK(_using, uint8, args[2]);
flags = jl_unbox_uint8(args[2]);
}
jl_module_using((jl_module_t *)args[0], (jl_module_t *)args[1], flags);
return jl_nothing;
}

Expand Down
4 changes: 2 additions & 2 deletions src/gc-stock.c
Original file line number Diff line number Diff line change
Expand Up @@ -2168,9 +2168,9 @@ STATIC_INLINE void gc_mark_module_binding(jl_ptls_t ptls, jl_module_t *mb_parent
jl_value_t *obj_parent = (jl_value_t *)mb_parent;
struct _jl_module_using *objary_begin = (struct _jl_module_using *)mb_parent->usings.items;
struct _jl_module_using *objary_end = objary_begin + nusings;
static_assert(sizeof(struct _jl_module_using) == 3*sizeof(void *), "Mismatch in _jl_module_using size");
static_assert(sizeof(struct _jl_module_using) == 4*sizeof(void *), "Mismatch in _jl_module_using size");
static_assert(offsetof(struct _jl_module_using, mod) == 0, "Expected `mod` at the beginning of _jl_module_using");
gc_mark_objarray(ptls, obj_parent, (jl_value_t**)objary_begin, (jl_value_t**)objary_end, 3, nptr);
gc_mark_objarray(ptls, obj_parent, (jl_value_t**)objary_begin, (jl_value_t**)objary_end, 4, nptr);
}
else {
gc_mark_push_remset(ptls, (jl_value_t *)mb_parent, nptr);
Expand Down
11 changes: 10 additions & 1 deletion src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,9 @@ static const uint8_t PARTITION_FLAG_DEPRECATED = 0x20;
// implies _DEPRECATED. However, the reverse is not true. Such bindings are usually used for functions,
// where calling the function itself will provide a (better) deprecation warning/error.
static const uint8_t PARTITION_FLAG_DEPWARN = 0x40;
// _IMPLICITLY_EXPORTED: This binding partition is implicitly exported via @reexport. Unlike _EXPORTED,
// this flag is set during implicit resolution and can be removed if the resolution changes.
static const uint8_t PARTITION_FLAG_IMPLICITLY_EXPORTED = 0x80;

#if defined(_COMPILER_MICROSOFT_)
#define JL_ALIGNED_ATTR(alignment) \
Expand Down Expand Up @@ -853,6 +856,8 @@ typedef struct _jl_module_t {
int8_t max_methods;
// If cleared no binding partition in this module has PARTITION_FLAG_EXPORTED and min_world > jl_require_world.
_Atomic(int8_t) export_set_changed_since_require_world;
// Set if this module has any reexport usings (used to bypass fast-path in implicit resolution)
_Atomic(int8_t) has_reexports;
jl_mutex_t lock;
intptr_t hash;
} jl_module_t;
Expand All @@ -861,8 +866,12 @@ struct _jl_module_using {
jl_module_t *mod;
size_t min_world;
size_t max_world;
size_t flags;
};

// Flags for _jl_module_using.flags
static const uint8_t JL_MODULE_USING_REEXPORT = 0x1;

struct _jl_globalref_t {
JL_DATA_TYPE
jl_module_t *mod;
Expand Down Expand Up @@ -2038,7 +2047,7 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b JL_
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_using(jl_module_t *to, jl_module_t *from, size_t flags);
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);
Expand Down
10 changes: 7 additions & 3 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -916,19 +916,19 @@ typedef struct _modstack_t {
// The analyzer doesn't like looking through the arraylist, so just model the
// access for it using this function
STATIC_INLINE struct _jl_module_using *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT {
return (struct _jl_module_using *)&(m->usings.items[3*i]);
return (struct _jl_module_using *)&(m->usings.items[4*i]);
}
STATIC_INLINE jl_module_t *module_usings_getmod(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT {
return module_usings_getidx(m, i)->mod;
}
#endif

STATIC_INLINE size_t module_usings_length(jl_module_t *m) JL_NOTSAFEPOINT {
return m->usings.len/3;
return m->usings.len/4;
}

STATIC_INLINE size_t module_usings_max(jl_module_t *m) JL_NOTSAFEPOINT {
return m->usings.max/3;
return m->usings.max/4;
}

JL_DLLEXPORT jl_sym_t *jl_module_name(jl_module_t *m) JL_NOTSAFEPOINT;
Expand Down Expand Up @@ -1029,6 +1029,10 @@ STATIC_INLINE int jl_bkind_is_real_constant(enum jl_partition_kind kind) JL_NOTS
return kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_CONST || kind == PARTITION_KIND_CONST_IMPORT;
}

STATIC_INLINE int jl_bpart_is_exported(uint8_t flags) JL_NOTSAFEPOINT {
return flags & (PARTITION_FLAG_EXPORTED | PARTITION_FLAG_IMPLICITLY_EXPORTED);
}

JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world) JL_GLOBALLY_ROOTED;
JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition_with_hint(jl_binding_t *b JL_PROPAGATES_ROOT, jl_binding_partition_t *previous_part, size_t world) JL_GLOBALLY_ROOTED;
JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_GLOBALLY_ROOTED;
Expand Down
Loading