Skip to content

Commit

Permalink
Add public keyword (#50105)
Browse files Browse the repository at this point in the history
Co-authored-by: Claire Foster <[email protected]>
  • Loading branch information
2 people authored and NHDaly committed Sep 20, 2023
1 parent 1175944 commit 1e70913
Show file tree
Hide file tree
Showing 30 changed files with 432 additions and 90 deletions.
12 changes: 11 additions & 1 deletion base/docs/basedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,23 @@ kw"import"
"""
export
`export` is used within modules to tell Julia which functions should be
`export` is used within modules to tell Julia which names should be
made available to the user. For example: `export foo` makes the name
`foo` available when [`using`](@ref) the module.
See the [manual section about modules](@ref modules) for details.
"""
kw"export"

"""
public
`public` is used within modules to tell Julia which names are part of the
public API of the module . For example: `public foo` indicates that the name
`foo` is public, without making it available available when [`using`](@ref)
the module. See the [manual section about modules](@ref modules) for details.
"""
kw"public"

"""
as
Expand Down
81 changes: 81 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1063,3 +1063,84 @@ export
@view,
@views,
@static

# TODO: use normal syntax once JuliaSyntax.jl becomes available at this point in bootstrapping
eval(Expr(:public,
# Modules
:Checked,
:Filesystem,
:Order,
:Sort,

# Types
:AbstractLock,
:AsyncCondition,
:CodeUnits,
:Event,
:Fix1,
:Fix2,
:Generator,
:ImmutableDict,
:OneTo,
:UUID,

# Semaphores
:Semaphore,
:acquire,
:release,

# collections
:IteratorEltype,
:IteratorSize,
:to_index,
:vect,
:isdone,
:front,
:rest,
:split_rest,
:tail,
:checked_length,

# Loading
:DL_LOAD_PATH,
:load_path,
:active_project,

# Reflection and introspection
:isambiguous,
:isexpr,
:isidentifier,
:issingletontype,
:identify_package,
:locate_package,
:moduleroot,
:jit_total_bytes,
:summarysize,
:isexported,
:ispublic,

# Opperators
:operator_associativity,
:operator_precedence,
:isbinaryoperator,
:isoperator,
:isunaryoperator,

# C interface
:cconvert,
:unsafe_convert,

# Error handling
:exit_on_sigint,
:windowserror,

# Macros
Symbol("@assume_effects"),
Symbol("@constprop"),
Symbol("@locals"),
Symbol("@propagate_inbounds"),

# misc
:notnothing,
:runtests,
:text_colors))
86 changes: 81 additions & 5 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,24 +75,100 @@ end
"""
names(x::Module; all::Bool = false, imported::Bool = false)
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 a `Module`, excluding deprecated names.
If `all` is true, then the list also includes non-public 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 explicitly mark names from `Main` as public.
See also: [`@locals`](@ref Base.@locals), [`@__MODULE__`](@ref).
See also: [`isexported`](@ref), [`ispublic`](@ref), [`@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)

"""
isexported(m::Module, s::Symbol) -> Bool
Returns whether a symbol is exported from a module.
See also: [`ispublic`](@ref), [`names`](@ref)
```jldoctest
julia> module Mod
export foo
public bar
end
Mod
julia> Base.isexported(Mod, :foo)
true
julia> Base.isexported(Mod, :bar)
false
julia> Base.isexported(Mod, :baz)
false
```
"""
isexported(m::Module, s::Symbol) = ccall(:jl_module_exports_p, Cint, (Any, Any), m, s) != 0

"""
ispublic(m::Module, s::Symbol) -> Bool
Returns whether a symbol is marked as public in a module.
Exported symbols are considered public.
See also: [`isexported`](@ref), [`names`](@ref)
```jldoctest
julia> module Mod
export foo
public bar
end
Mod
julia> Base.ispublic(Mod, :foo)
true
julia> Base.ispublic(Mod, :bar)
true
julia> Base.ispublic(Mod, :baz)
false
```
"""
ispublic(m::Module, s::Symbol) = ccall(:jl_module_public_p, Cint, (Any, Any), m, s) != 0

# TODO: this is vaguely broken because it only works for explicit calls to
# `Base.deprecate`, not the @deprecated macro:
isdeprecated(m::Module, s::Symbol) = ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s) != 0

"""
isbindingresolved(m::Module, s::Symbol) -> Bool
Returns whether the binding of a symbol in a module is resolved.
See also: [`isexported`](@ref), [`ispublic`](@ref), [`isdeprecated`](@ref)
```jldoctest
julia> module Mod
foo() = 17
end
Mod
julia> Base.isbindingresolved(Mod, :foo)
true
julia> Base.isbindingresolved(Mod, :bar)
false
```
"""
isbindingresolved(m::Module, var::Symbol) = ccall(:jl_binding_resolved_p, Cint, (Any, Any), m, var) != 0

function binding_module(m::Module, s::Symbol)
Expand Down
2 changes: 1 addition & 1 deletion base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2226,7 +2226,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)
print(io, head, ' ')
show_list(io, mapany(allow_macroname, args), ", ", indent)

Expand Down
2 changes: 1 addition & 1 deletion deps/JuliaSyntax.version
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
JULIASYNTAX_BRANCH = main
JULIASYNTAX_SHA1 = 045d156c44dbb87769c7416d049a7c08908539d4
JULIASYNTAX_SHA1 = a9110fa8ecbe79943bb9525b4ccd99a3976cfcb7
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a42bbb42babbbd727556f6bc01455826
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
d542766e72b57418b9b4e17743f89d8535c1f36497346b57538bd0cb451e64af9493015692179f80ec4ee8cf18349c1e0888f5710db895e19f9bb0322f0f7464
4 changes: 4 additions & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ However, you can create variables with names:
Finally:
`where` is parsed as an infix operator for writing parametric method and type definitions;
`in` and `isa` are parsed as infix operators;
`public` is parsed as a keyword when beginning a toplevel statement;
`outer` is parsed as a keyword when used to modify the scope of a variable in an iteration specification of a `for` loop;
and `as` is used as a keyword to rename an identifier brought into scope by `import` or `using`.
Creation of variables named `where`, `in`, `isa`, `outer` and `as` is allowed, though.

```@docs
module
export
public
import
using
as
Expand Down Expand Up @@ -451,6 +453,8 @@ Base.@__DIR__
Base.@__LINE__
Base.fullname
Base.names
Base.isexported
Base.ispublic
Base.nameof(::Function)
Base.functionloc(::Any, ::Any)
Base.functionloc(::Method)
Expand Down
6 changes: 3 additions & 3 deletions doc/src/base/reflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ Julia provides a variety of runtime reflection capabilities.

## Module bindings

The exported names for a `Module` are available using [`names(m::Module)`](@ref), which will return
an array of [`Symbol`](@ref) elements representing the exported bindings. `names(m::Module, all = true)`
returns symbols for all bindings in `m`, regardless of export status.
The public names for a `Module` are available using [`names(m::Module)`](@ref), which will return
an array of [`Symbol`](@ref) elements representing the public bindings. `names(m::Module, all = true)`
returns symbols for all bindings in `m`, regardless of public status.

## DataType fields

Expand Down
27 changes: 22 additions & 5 deletions doc/src/manual/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,28 @@ 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.

Public symbols are those marked with either `public foo` or `export foo`.

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
9 changes: 7 additions & 2 deletions doc/src/manual/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Modules in Julia help organize code into coherent units. They are delimited synt
allows the same name to be used for different functions or global variables without conflict, as long as they are in separate modules.

2. Modules have facilities for detailed namespace management: each defines a set of names it
`export`s, and can import names from other modules with `using` and `import` (we explain these below).
`export`s and marks as `public`, and can import names from other modules with `using` and
`import` (we explain these below).

3. Modules can be precompiled for faster loading, and may contain code for runtime initialization.

Expand All @@ -16,7 +17,7 @@ Typically, in larger Julia packages you will see module code organized into file
```julia
module SomeModule

# export, using, import statements are usually here; we discuss these below
# export, public, using, import statements are usually here; we discuss these below

include("file1.jl")
include("file2.jl")
Expand Down Expand Up @@ -103,6 +104,10 @@ Also, some modules don't export names at all. This is usually done if they use c
words, such as `derivative`, in their API, which could easily clash with the export lists of other
modules. We will see how to manage name clashes below.

To mark a name as public without exporting it into the namespace of folks who call `using NiceStuff`,
one can use `public` instead of `export`. This marks the public name(s) as part of the public API,
but does not have any namespace implications.

### Standalone `using` and `import`

Possibly the most common way of loading a module is `using ModuleName`. This [loads](@ref
Expand Down
7 changes: 4 additions & 3 deletions doc/src/manual/noteworthy-differences.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,15 +413,16 @@ For users coming to Julia from R, these are some noteworthy differences:
file are `include`d only once (No `#ifdef` confusion).

### Julia &hArr; C/C++: Module interface
* C++ exposes interfaces using "public" `.h`/`.hpp` files whereas Julia `module`s `export`
symbols that are intended for their users.
* C++ exposes interfaces using "public" `.h`/`.hpp` files whereas Julia `module`s mark
specific symbols that are intended for their users as `public`or `export`ed.
* Often, Julia `module`s simply add functionality by generating new "methods" to existing
functions (ex: `Base.push!`).
* Developers of Julia packages therefore cannot rely on header files for interface
documentation.
* Interfaces for Julia packages are typically described using docstrings, README.md,
static web pages, ...
* Some developers choose not to `export` all symbols required to use their package/module.
* Some developers choose not to `export` all symbols required to use their package/module,
but should still mark unexported user facing symbols as `public`.
* Users might be expected to access these components by qualifying functions/structs/...
with the package/module name (ex: `MyModule.run_this_task(...)`).

Expand Down
2 changes: 2 additions & 0 deletions src/ast.c
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -1939,7 +1939,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
Loading

0 comments on commit 1e70913

Please sign in to comment.