Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add public keyword #50105

Merged
merged 69 commits into from Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
5b3d8c1
mwe including `export (scoped-true), a, b, c` and `names`
Jun 7, 2023
665855e
add `Base.isinternal` and use it to flag internal methods in their do…
Jun 7, 2023
52f519e
update documentation
Jun 7, 2023
03f9e02
add more verbose clarificaiton of what is and is not public API (TODO…
Jun 26, 2023
7eb64a2
add prelimianry list of scoped-exported symbols for review
Jun 26, 2023
c7192ea
more docs (implement option 1 of https://github.com/JuliaLang/julia/p…
Jun 26, 2023
ec3b2f3
address @jariji's comments
Jun 26, 2023
804f5ef
Merge branch 'master' into scoped-export
LilithHafner Jun 29, 2023
7d404f0
Merge branch 'master' into scoped-export
Jul 8, 2023
4dc10c4
use JuliaSyntax#320 branch
Jul 8, 2023
5fd1f64
bump JuliaSyntax
Jul 8, 2023
b85ae46
more changes for scoped export -> public
Jul 8, 2023
acc928a
try to make public list functional
Jul 10, 2023
a5162c2
bump juliasyntax
Jul 18, 2023
aacc187
bump JuliaSyntax
Jul 19, 2023
145ad7f
fix lowering
Jul 19, 2023
aea3d22
clarify internal symbol warning and add test for it
Jul 19, 2023
f7d57ba
add test for names + public
Jul 19, 2023
34844bb
don't autocomplete internals (ideally we _would_ autocomplete interna…
Jul 19, 2023
8241760
Merge branch 'master' into scoped-export
Jul 19, 2023
920244a
update FAQ (this change introduces comedy and therefore may be contro…
Jul 19, 2023
9e61f96
Rename isinternal to ispublic and make all resolved symbols in Main p…
Jul 19, 2023
f75cb68
fix typo in test/reflection.jl
Jul 21, 2023
db7ba05
change module docstring warning
Jul 28, 2023
7dbdd5e
Make warning an Admonition
Jul 28, 2023
ac11ea4
bump JuliaSyntax
Jul 28, 2023
bd0ee87
fix doc tests
Jul 29, 2023
1dc9652
remove internal warning tests from stdlib/REPL/test/docview.jl becaus…
Jul 29, 2023
17b8834
restore autocomplete for non-public symbols
Jul 29, 2023
6e2ddec
Merge branch 'master' into scoped-export
LilithHafner Jul 29, 2023
1000988
initialize public to false (woops!)
Jul 29, 2023
c5955c0
more fixups for doc tests
Jul 29, 2023
5ed2c7d
remove joke
Jul 29, 2023
5da758e
fix repl tests
Jul 29, 2023
de8be2b
fix precompile tests
Jul 29, 2023
d75725b
bump juliasyntax to merged version
Jul 30, 2023
05352df
Make `compilecache` internal
LilithHafner Aug 3, 2023
4b04f31
Remove `_datatype_*` publics
LilithHafner Aug 3, 2023
051f824
Remove symbols from public list
LilithHafner Aug 3, 2023
8a56ab4
Add some modules to public list (@vtjnash)
LilithHafner Aug 3, 2023
6e2d809
remove newlines between bullet points
Aug 10, 2023
69cf8d4
Apply suggestions from code review
LilithHafner Aug 13, 2023
9d74beb
Apply suggestions from code review
LilithHafner Aug 13, 2023
4b18e1d
Merge branch 'master' into scoped-export
Aug 13, 2023
4ba71ab
add show tests
Aug 13, 2023
ce3a798
group public symbols a little better
Aug 13, 2023
ca8eb44
revert names to the most basic implementation (simply swap export=>pu…
Aug 14, 2023
6d1e2c7
fix typo
Aug 14, 2023
2678523
fix typo
Aug 14, 2023
528469b
revert unnecessary whitespace change
Aug 14, 2023
082fb86
add tests for ispublic and add more tests for isexported
Aug 14, 2023
3d09dae
fix some tests
Aug 14, 2023
12bab72
Do a better job of logging nonpublic access in help mode
Sep 4, 2023
0112e2c
remove obsolete tests
Sep 4, 2023
66427c6
finalize struct
Sep 4, 2023
067d69c
update tests
Sep 4, 2023
b688b60
add tests and fix "public names" header
Sep 4, 2023
6ce6fdb
update documentation throughout to refer to public and export now-cor…
Sep 5, 2023
e57c63f
fix cross refs
Sep 5, 2023
1a6e3dc
update varinfo documentation
Sep 5, 2023
ebcdf24
Merge branch 'master' into scoped-export
LilithHafner Sep 5, 2023
d4418fe
Update checksums
Sep 5, 2023
c4924c9
fix some tests
Sep 5, 2023
5835699
more test fixes
Sep 5, 2023
153b538
try switching to a different JuliaSyntax version
Sep 5, 2023
158d59f
don't accidentally drop the module in testing
Sep 5, 2023
41f3729
fix some doctests
Sep 5, 2023
fd5fb90
Merge branch 'master' into scoped-export
LilithHafner Sep 6, 2023
ea7a141
Docstring fixup for Base.ispublic
LilithHafner Sep 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 61 additions & 0 deletions base/exports.jl
Expand Up @@ -1063,3 +1063,64 @@ export
@view,
@views,
@static

# TODO: use normal syntax once JuliaSyntax.jl becomes available at this point in bootstrapping
eval(Expr(:public,
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
Symbol("@assume_effects"),
Symbol("@constprop"),
Symbol("@locals"),
Symbol("@propagate_inbounds"),
:AbstractLock,
:AsyncCondition,
:Checked,
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add Filesystem, Sorting, Math,Orders, and possibly others?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filesystem, Sort, and Order, yes. For Math, I think all public symobls in Math are already exported from Base so there is no need.

:CodeUnits,
:DL_LOAD_PATH,
:Event,
:Filesystem,
:Fix1,
:Fix2,
:Generator,
:ImmutableDict,
:IteratorEltype,
:IteratorSize,
:OneTo,
:Order,
:Pairs,
:Semaphore,
:Sort,
:UUID,
:acquire,
:active_project,
:cconvert,
:checked_length,
:exit_on_sigint,
:front,
:identify_package,
:isambiguous,
:isbinaryoperator,
:isdone,
:isexpr,
:isidentifier,
:isoperator,
:issingletontype,
:isunaryoperator,
:jit_total_bytes,
:load_path,
:locate_package,
:moduleroot,
:notnothing,
:operator_associativity,
:operator_precedence,
:promote_typejoin,
:release,
:require,
:rest,
:runtests,
:split_rest,
:summarysize,
:tail,
:text_colors,
:to_index,
:unsafe_convert,
:vect,
:windowserror))
21 changes: 12 additions & 9 deletions base/reflection.jl
Expand Up @@ -73,25 +73,28 @@ function fullname(m::Module)
end

"""
names(x::Module; all::Bool = false, imported::Bool = false)
names(x::Module; qualified::Bool=true, all::Bool = false, imported::Bool = false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we decide to try making new keywords here correspond somehow to the flags in jl_binding_t so that the filtering behavior of names() is much easier to specify? I forgot which conclusion we came to!


Get an array of the names exported by a `Module`, excluding deprecated names.
If `all` is true, then the list also includes non-exported names defined in the module,
Get an array of the public names of `Module`, excluding deprecated names.

If `qualified` is false, then return only exported names, not public but unexported names.
If `all` is true, then the list also includes private names defined in the module,
deprecated names, and compiler-generated names.
If `imported` is true, then names explicitly imported from other modules
are also included.

As a special case, all names defined in `Main` are considered \"exported\",
since it is not idiomatic to explicitly export names from `Main`.
As a special case, all names defined in `Main` are considered \"public\",
since it is not idiomatic to mark names from `Main` as public.

See also: [`@locals`](@ref Base.@locals), [`@__MODULE__`](@ref).
"""
names(m::Module; all::Bool = false, imported::Bool = false) =
sort!(unsorted_names(m; all, imported))
unsorted_names(m::Module; all::Bool = false, imported::Bool = false) =
ccall(:jl_module_names, Array{Symbol,1}, (Any, Cint, Cint), m, all, imported)
names(m::Module; qualified::Bool = true, all::Bool = false, imported::Bool = false) =
sort!(unsorted_names(m; qualified, all, imported))
unsorted_names(m::Module; qualified::Bool = true, all::Bool = false, imported::Bool = false) =
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we throw if qualified=false and all=true? These keywords seem poorly named and inconsistent. Let's change this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If qualified is false, then return only exported names, not public but unexported names."

only exported names that are unexported names?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a possible solution:

"""
    names(x::Module; visibility = :public, deprecated = false, imported = false, hidden = false)

Get a vector of the names of `Module`, filtered by the keyword arguments.

- `visibility` can be one of `:exported`, `:public`, or `:all`.
   - If `visibility` is `:public` then internal names are filtered out
   - If `visibility` is `:exported` then all unexported names are filtered out.
- If `deprecated` is `false`, then deprecated names are filtered out.
- If `imported` is `false`, then internal names that are imported from other modules are filtered out.
- If `hidden` is `false`, then hidden (e.g. compiler-generated) names are filtered out.
  Hidden names are implementation details of Julia itself and are not intended to be used by
  user code.

Setting `visibility` to `:all` or any of the other keywords to `true` disables the
corresponding filter. The result is the intersection of the names that pass all the enabled
filters.

As a special case, all names defined in `Main` are considered \"public\",
since it is not idiomatic to mark names from `Main` as public.

For backwards compatibility, it is also possible to pass `all=true` which is equivalent to
passing `visibility=:all`, `deprecated=true`, and `hidden=true` (but not `imported=true`).
It is an error to pass `all=true` and a contradictory value for `visibility` or `deprecated`.

!!! Compat 1.11
    The `visibility`, `deprecated`, and `hidden` keyword arguments were added in Julia 1.11.
    To support prior versions of julia, use the `all` keyword argument instead.

See also: [`@locals`](@ref Base.@locals), [`@__MODULE__`](@ref).
"""

This docstring should fully describe the proposed behavior and so is ready for review. If the docstring doesn't make it completely clear what I mean, then it's not a good enough docstring.

I'm holding back on implementing this until I know what jl_atomic_load_relaxed(&b->owner) == b means.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think hidden is documented elsewhere and I don't know what "compiler-generated names" refers to.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edited to explain hidden a bit more.

tbh I'm not entirely sure what the deal with compiler-generated names is. They typically start with an #, and they are an implementation detail, so maybe we shouldn't expose them at all. Because compiler-generated names are an implementation detail, it is technically not a violation of symver to make all=true no longer return them because their very existence was never guaranteed to continue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think calling this "hidden" is a good idea, if we want to expose this at all. It just seems like another easy-to-confuse thing, thinking of "object hiding" in other languages.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never heard of "object hiding", would you provide a link please?
I called this hidden because that is the variable name used on the c side already.
If you would prefer a different name, feel free to recommend one.
All words have multiple existing meanings, and most applicable words have existing meanings in other programming languages. Because all translation is lossy, those meanings will never perfectly align with the meaning of a word in Julia. For example, the concept of "data hiding" in c++ is similar to, though not identical to this usage of the word hiding. The main difference is that "data hiding" refers to hiding data, specifically, and this case, which is not prefixed with "data", is not data specific. This case also follows the Julian model of making things harder to reach rather than prohibiting access altogether. Examining c++'s usage of the term "data hiding" makes me more rather than less inclined to call this concept "hiding" here.
It is possible to not expose this at all, but that comes at the cost of not covering all of the existing functionality provided by the all keyword.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for not using the exact terminology. I was indeed referring to "data hiding" or "information hiding". I used "object hiding" here because in Julia, this has the effect of generally hiding any object. The semantic meaning is the same. It's precisely this closeness to the existing concept that's the reason for my objection.

ccall(:jl_module_names, Array{Symbol,1}, (Any, Cint, Cint, Cint), m, qualified, all, imported)

isexported(m::Module, s::Symbol) = ccall(:jl_module_exports_p, Cint, (Any, Any), m, s) != 0
ispublic(m::Module, s::Symbol) = m == Main ? isbindingresolved(m, s) : ccall(:jl_module_public_p, Cint, (Any, Any), m, s) != 0
isdeprecated(m::Module, s::Symbol) = ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s) != 0
isbindingresolved(m::Module, var::Symbol) = ccall(:jl_binding_resolved_p, Cint, (Any, Any), m, var) != 0

Expand Down
2 changes: 1 addition & 1 deletion base/show.jl
Expand Up @@ -2232,7 +2232,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In
print(io, head, ' ')
show_list(io, args, ", ", indent, 0, quote_level)

elseif head === :export
elseif head in (:export, :public)
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
print(io, head, ' ')
show_list(io, mapany(allow_macroname, args), ", ", indent)

Expand Down
4 changes: 2 additions & 2 deletions deps/JuliaSyntax.version
@@ -1,4 +1,4 @@
JULIASYNTAX_BRANCH = main
JULIASYNTAX_SHA1 = 8731bab86f14762cca8cf24224d8c7a6a89c21c5
JULIASYNTAX_BRANCH = scoped-export
JULIASYNTAX_SHA1 = ad9b16681389dbe3f21a89897f7a86dec793f72a
JULIASYNTAX_GIT_URL := https://github.com/JuliaLang/JuliaSyntax.jl.git
JULIASYNTAX_TAR_URL = https://api.github.com/repos/JuliaLang/JuliaSyntax.jl/tarball/$1

This file was deleted.

This file was deleted.

@@ -0,0 +1 @@
85321fa6216516497d4aeba055441722
@@ -0,0 +1 @@
fc3ee2da0aedcd56c96e177e3ae854bf4e50889edad88e1da0cd6a0f00a4d7381a564457931a5bc31f94f9cbe36ca4bb6fd57674ce919e58f268be56c10e89f3
28 changes: 23 additions & 5 deletions doc/src/manual/faq.md
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
Expand Up @@ -22,11 +22,29 @@ On the other hand, language *interoperability* is extremely useful: we want to e

### How does Julia define its public API?

Julia `Base` and standard library functionality described in the
[the documentation](https://docs.julialang.org/) that is not marked as unstable
(e.g. experimental and internal) is covered by [SemVer](https://semver.org/).
Functions, types, and constants are not part of the public API if they are not
included in the documentation, _even if they have docstrings_.
Julia's public [API](https://en.wikipedia.org/wiki/API) is the behavior described in
documentation of public symbols from `Base` and the standard libraries. Functions,
types, and constants are not part of the public API if they are not public, even if
they have docstrings or are described in the documentation. Further, only the documented
behavior of public symbols is part of the public API. Undocumented behavior of public
symbols is internal.
Comment on lines +25 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still some symbols which are documented in the manual and not yet marked as public (e.g. Base.EnvDict). Is there a plan to try to find them all? otherwise should we also keep around for the time being the old version mentioning the documentation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sorta responded to this way up the thread: #50105 (comment)

The plan is to find them all by 1.11. #51335.


Public symbols are those marked with either `public foo` or `export foo`.
Exported symbols are automatically considered public
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved

In other words:

- Documented behavior of public symbols is part of the public API.
- Undocumented behavior of public symbols is not part of the public API.
- Documented behavior of private symbols is not part of the public API.
- Undocumented behavior of private symbols is not part of the public API.

You can get a complete list of the public symbols from a module with `names(MyModule)`.

Package authors are encouraged to define their public API similarly.

Anything in Julia's Public API is covered by [SemVer](https://semver.org/) and therefore
will not be removed or receive meaningful breaking changes before Julia 2.0.

### There is a useful undocumented function/type/constant. Can I use it?

Expand Down
2 changes: 2 additions & 0 deletions src/ast.c
Expand Up @@ -28,6 +28,7 @@ JL_DLLEXPORT jl_sym_t *jl_top_sym;
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;
Expand Down Expand Up @@ -304,6 +305,7 @@ void jl_init_common_symbols(void)
jl_lambda_sym = jl_symbol("lambda");
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("=");
Expand Down
2 changes: 1 addition & 1 deletion src/builtins.c
Expand Up @@ -1927,7 +1927,7 @@ static void add_intrinsic(jl_module_t *inm, const char *name, enum intrinsic f)
jl_value_t *i = jl_permbox32(jl_intrinsic_type, 0, (int32_t)f);
jl_sym_t *sym = jl_symbol(name);
jl_set_const(inm, sym, i);
jl_module_export(inm, sym);
jl_module_public(inm, sym, 1);
}

void jl_init_intrinsic_properties(void) JL_GC_DISABLED
Expand Down
3 changes: 2 additions & 1 deletion src/jl_exported_funcs.inc
Expand Up @@ -326,13 +326,14 @@
XX(jl_methtable_lookup) \
XX(jl_mi_cache_insert) \
XX(jl_module_build_id) \
XX(jl_module_export) \
XX(jl_module_exports_p) \
XX(jl_module_globalref) \
XX(jl_module_import) \
XX(jl_module_name) \
XX(jl_module_names) \
XX(jl_module_parent) \
XX(jl_module_public) \
XX(jl_module_public_p) \
XX(jl_module_use) \
XX(jl_module_using) \
XX(jl_module_usings) \
Expand Down
6 changes: 3 additions & 3 deletions src/julia-syntax.scm
Expand Up @@ -2364,7 +2364,7 @@
`(= ,lhs ,rhs)))

(define (expand-forms e)
(if (or (atom? e) (memq (car e) '(quote inert top core globalref outerref module toplevel ssavalue null true false meta using import export thismodule toplevel-only)))
(if (or (atom? e) (memq (car e) '(quote inert top core globalref outerref module toplevel ssavalue null true false meta using import export public thismodule toplevel-only)))
e
(let ((ex (get expand-table (car e) #f)))
(if ex
Expand Down Expand Up @@ -3708,7 +3708,7 @@ f(x) = yt(x)
thunk with-static-parameters toplevel-only
global globalref outerref const-if-global thismodule
const atomic null true false ssavalue isdefined toplevel module lambda
error gc_preserve_begin gc_preserve_end import using export inline noinline)))
error gc_preserve_begin gc_preserve_end import using export public inline noinline)))

(define (local-in? s lam (tab #f))
(or (and tab (has? tab s))
Expand Down Expand Up @@ -4836,7 +4836,7 @@ f(x) = yt(x)
val))

;; other top level expressions
((import using export)
((import using export public)
(check-top-level e)
(emit e)
(let ((have-ret? (and (pair? code) (pair? (car code)) (eq? (caar code) 'return))))
Expand Down
7 changes: 4 additions & 3 deletions src/julia.h
Expand Up @@ -594,11 +594,12 @@ typedef struct _jl_binding_t {
_Atomic(struct _jl_binding_t*) owner; // for individual imported bindings (NULL until 'resolved')
_Atomic(jl_value_t*) ty; // binding type
uint8_t constp:1;
uint8_t exportp:1;
uint8_t exportp:1; // `public foo` sets `publicp`, `export foo` sets both `publicp` and `exportp`
uint8_t publicp:1; // exportp without publicp is not allowed.
uint8_t imported:1;
uint8_t usingfailed:1;
uint8_t deprecated:2; // 0=not deprecated, 1=renamed, 2=moved to another package
uint8_t padding:2;
uint8_t padding:1;
} jl_binding_t;

typedef struct {
Expand Down Expand Up @@ -1739,7 +1740,7 @@ JL_DLLEXPORT void jl_module_use(jl_module_t *to, jl_module_t *from, jl_sym_t *s)
JL_DLLEXPORT void jl_module_use_as(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname);
JL_DLLEXPORT void jl_module_import(jl_module_t *to, jl_module_t *from, jl_sym_t *s);
JL_DLLEXPORT void jl_module_import_as(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname);
JL_DLLEXPORT void jl_module_export(jl_module_t *from, jl_sym_t *s);
JL_DLLEXPORT void jl_module_public(jl_module_t *from, jl_sym_t *s, int exp);
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
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);
JL_DLLEXPORT void jl_add_standard_imports(jl_module_t *m);
Expand Down
1 change: 1 addition & 0 deletions src/julia_internal.h
Expand Up @@ -1502,6 +1502,7 @@ extern JL_DLLEXPORT jl_sym_t *jl_top_sym;
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;
Expand Down
18 changes: 13 additions & 5 deletions src/module.c
Expand Up @@ -49,7 +49,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui
if (default_names) {
jl_set_const(m, name, (jl_value_t*)m);
}
jl_module_export(m, name);
jl_module_public(m, name, 1);
JL_GC_POP();
return m;
}
Expand Down Expand Up @@ -180,6 +180,7 @@ static jl_binding_t *new_binding(jl_module_t *mod, jl_sym_t *name)
b->globalref = NULL;
b->constp = 0;
b->exportp = 0;
b->publicp = 0;
b->imported = 0;
b->deprecated = 0;
b->usingfailed = 0;
Expand Down Expand Up @@ -670,10 +671,11 @@ JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from)
}
}

JL_DLLEXPORT void jl_module_export(jl_module_t *from, jl_sym_t *s)
JL_DLLEXPORT void jl_module_public(jl_module_t *from, jl_sym_t *s, int exp)
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
{
jl_binding_t *b = jl_get_module_binding(from, s, 1);
b->exportp = 1;
b->publicp = 1;
b->exportp = exp;
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
}

JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var)
Expand All @@ -694,6 +696,12 @@ JL_DLLEXPORT int jl_module_exports_p(jl_module_t *m, jl_sym_t *var)
return b && b->exportp;
}

JL_DLLEXPORT int jl_module_public_p(jl_module_t *m, jl_sym_t *var)
{
jl_binding_t *b = jl_get_module_binding(m, var, 0);
return b && b->publicp;
}

JL_DLLEXPORT int jl_binding_resolved_p(jl_module_t *m, jl_sym_t *var)
{
jl_binding_t *b = jl_get_module_binding(m, var, 0);
Expand Down Expand Up @@ -934,7 +942,7 @@ JL_DLLEXPORT jl_value_t *jl_module_usings(jl_module_t *m)
return (jl_value_t*)a;
}

JL_DLLEXPORT jl_value_t *jl_module_names(jl_module_t *m, int all, int imported)
JL_DLLEXPORT jl_value_t *jl_module_names(jl_module_t *m, int qualified, int all, int imported)
{
jl_array_t *a = jl_alloc_array_1d(jl_array_symbol_type, 0);
JL_GC_PUSH1(&a);
Expand All @@ -945,7 +953,7 @@ JL_DLLEXPORT jl_value_t *jl_module_names(jl_module_t *m, int all, int imported)
break;
jl_sym_t *asname = b->globalref->name;
int hidden = jl_symbol_name(asname)[0]=='#';
if ((b->exportp ||
if (((qualified ? b->publicp : b->exportp) ||
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
(imported && b->imported) ||
(jl_atomic_load_relaxed(&b->owner) == b && !b->imported && (all || m == jl_main_module))) &&
(all || (!b->deprecated && !hidden))) {
Expand Down
14 changes: 9 additions & 5 deletions src/toplevel.c
Expand Up @@ -560,6 +560,7 @@ int jl_is_toplevel_only_expr(jl_value_t *e) JL_NOTSAFEPOINT
((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 ||
((jl_expr_t*)e)->head == jl_global_sym ||
((jl_expr_t*)e)->head == jl_const_sym ||
Expand All @@ -575,8 +576,9 @@ int jl_needs_lowering(jl_value_t *e) JL_NOTSAFEPOINT
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_thunk_sym || head == jl_toplevel_sym ||
head == jl_error_sym || head == jl_incomplete_sym || head == jl_method_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) {
Expand Down Expand Up @@ -837,12 +839,14 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int
JL_GC_POP();
return jl_nothing;
}
else if (head == jl_export_sym) {
else if (head == jl_export_sym || head == jl_public_sym) {
int exp = (head == jl_export_sym);
for (size_t i = 0; i < jl_array_len(ex->args); i++) {
jl_sym_t *name = (jl_sym_t*)jl_array_ptr_ref(ex->args, i);
if (!jl_is_symbol(name))
jl_eval_errorf(m, "syntax: malformed \"export\" statement");
jl_module_export(m, name);
jl_eval_errorf(m, exp ? "syntax: malformed \"export\" statement" :
"syntax: malformed \"public\" statement");
jl_module_public(m, name, exp);
}
JL_GC_POP();
return jl_nothing;
Expand Down