Skip to content

Commit

Permalink
Backports for 1.11.1 (#56025)
Browse files Browse the repository at this point in the history
Backported PRs:
- [x] #55945 <!-- Fix logic in `?` docstring example -->
- [x] #55932 <!-- REPL: make UndefVarError aware of imported modules -->
- [x] #55968 <!-- [build] avoid libedit linkage and align libccalllazy*
SONAMEs -->
- [x] #55977 <!-- fix comma logic in time_print -->
- [x] #55982 <!-- `@time` actually fix time report commas & add tests
-->
- [x] #55743 <!-- doc: heap snapshot viewing -->
- [x] #55851 <!-- [REPL] Fix #55850 by using `safe_realpath` instead of
`abspath` in `projname` -->
- [x] #55992 <!-- Avoid `stat`-ing stdlib path if it's unreadable -->
- [x] #55589 <!-- prevent loading other extensions when precompiling an
extension -->
- [x] #54009 <!-- allow extensions to trigger from packages in [deps]
-->
- [x] #56019 <!-- Fix no-arg `ScopedValues.@with` within a scope -->
- [x] #56023 <!-- Sockets: Warn when local network access not granted.
-->
- [x] #55569 <!-- Add a docs section about loading/precomp/ttfx time
tuning -->
- [x] #55824 <!-- Replace regex package module checks with actual code
checks -->
- [x] #56041 <!-- Don't show keymap `@error` for hints -->
- [x] #53469
- [x] #56029 <!-- fix `_growbeg!` unncessary resizing -->
- [x] #56103
- [x] #55941 <!-- Fix zero elements for block-matrix kron involving
Diagonal -->
  • Loading branch information
KristofferC authored Oct 15, 2024
2 parents 501a4f2 + 4e03986 commit f9e4d07
Show file tree
Hide file tree
Showing 56 changed files with 625 additions and 334 deletions.
2 changes: 1 addition & 1 deletion base/Enums.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Base.print(io::IO, x::Enum) = print(io, _symbol(x))
function Base.show(io::IO, x::Enum)
sym = _symbol(x)
if !(get(io, :compact, false)::Bool)
from = get(io, :module, Base.active_module())
from = get(io, :module, Main)
def = parentmodule(typeof(x))
if from === nothing || !Base.isvisible(sym, def, from)
show(io, def)
Expand Down
9 changes: 7 additions & 2 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1062,10 +1062,11 @@ function _growbeg!(a::Vector, delta::Integer)
setfield!(a, :ref, @inbounds memoryref(ref, 1 - delta))
else
@noinline (function()
@_terminates_locally_meta
memlen = length(mem)
# since we will allocate the array in the middle of the memory we need at least 2*delta extra space
# the +1 is because I didn't want to have an off by 1 error.
newmemlen = max(overallocation(memlen), len + 2 * delta + 1)
newmemlen = max(overallocation(len), len + 2 * delta + 1)
newoffset = div(newmemlen - newlen, 2) + 1
# If there is extra data after the end of the array we can use that space so long as there is enough
# space at the end that there won't be quadratic behavior with a mix of growth from both ends.
Expand All @@ -1074,10 +1075,14 @@ function _growbeg!(a::Vector, delta::Integer)
if newoffset + newlen < memlen
newoffset = div(memlen - newlen, 2) + 1
newmem = mem
unsafe_copyto!(newmem, newoffset + delta, mem, offset, len)
for j in offset:newoffset+delta-1
@inbounds _unsetindex!(mem, j)
end
else
newmem = array_new_memory(mem, newmemlen)
unsafe_copyto!(newmem, newoffset + delta, mem, offset, len)
end
unsafe_copyto!(newmem, newoffset + delta, mem, offset, len)
setfield!(a, :ref, @inbounds memoryref(newmem, newoffset))
end)()
end
Expand Down
9 changes: 6 additions & 3 deletions base/docs/basedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -931,11 +931,14 @@ expression, rather than the side effects that evaluating `b` or `c` may have.
See the manual section on [control flow](@ref man-conditional-evaluation) for more details.
# Examples
```
```jldoctest
julia> x = 1; y = 2;
julia> x > y ? println("x is larger") : println("y is larger")
y is larger
julia> x > y ? println("x is larger") : println("x is not larger")
x is not larger
julia> x > y ? "x is larger" : x == y ? "x and y are equal" : "y is larger"
"y is larger"
```
"""
kw"?", kw"?:"
Expand Down
3 changes: 2 additions & 1 deletion base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,8 @@ macro _noub_meta()
#=:inaccessiblememonly=#false,
#=:noub=#true,
#=:noub_if_noinbounds=#false,
#=:consistent_overlay=#false))
#=:consistent_overlay=#false,
#=:nortcall=#false))
end
# can be used in place of `@assume_effects :notaskstate` (supposed to be used for bootstrapping)
macro _notaskstate_meta()
Expand Down
4 changes: 2 additions & 2 deletions base/experimental.jl
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,9 @@ function show_error_hints(io, ex, args...)
for handler in hinters
try
@invokelatest handler(io, ex, args...)
catch err
catch
tn = typeof(handler).name
@error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error"
@error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error" exception=current_exceptions()
end
end
end
Expand Down
133 changes: 68 additions & 65 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1350,7 +1350,9 @@ function run_module_init(mod::Module, i::Int=1)
end

function run_package_callbacks(modkey::PkgId)
run_extension_callbacks(modkey)
if !precompiling_extension
run_extension_callbacks(modkey)
end
assert_havelock(require_lock)
unlock(require_lock)
try
Expand Down Expand Up @@ -1405,13 +1407,12 @@ function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missi
proj_pkg = project_file_name_uuid(implicit_project_file, pkg.name)
if pkg == proj_pkg
d_proj = parsed_toml(implicit_project_file)
weakdeps = get(d_proj, "weakdeps", nothing)::Union{Nothing, Vector{String}, Dict{String,Any}}
extensions = get(d_proj, "extensions", nothing)::Union{Nothing, Dict{String, Any}}
extensions === nothing && return
weakdeps === nothing && return
if weakdeps isa Dict{String, Any}
return _insert_extension_triggers(pkg, extensions, weakdeps)
end
weakdeps = get(Dict{String, Any}, d_proj, "weakdeps")::Dict{String,Any}
deps = get(Dict{String, Any}, d_proj, "deps")::Dict{String,Any}
total_deps = merge(weakdeps, deps)
return _insert_extension_triggers(pkg, extensions, total_deps)
end

# Now look in manifest
Expand All @@ -1426,35 +1427,43 @@ function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missi
uuid = get(entry, "uuid", nothing)::Union{String, Nothing}
uuid === nothing && continue
if UUID(uuid) == pkg.uuid
weakdeps = get(entry, "weakdeps", nothing)::Union{Nothing, Vector{String}, Dict{String,Any}}
extensions = get(entry, "extensions", nothing)::Union{Nothing, Dict{String, Any}}
extensions === nothing && return
weakdeps === nothing && return
if weakdeps isa Dict{String, Any}
return _insert_extension_triggers(pkg, extensions, weakdeps)
weakdeps = get(Dict{String, Any}, entry, "weakdeps")::Union{Vector{String}, Dict{String,Any}}
deps = get(Dict{String, Any}, entry, "deps")::Union{Vector{String}, Dict{String,Any}}

function expand_deps_list(deps′::Vector{String})
deps′_expanded = Dict{String, Any}()
for (dep_name, entries) in d
dep_name in deps′ || continue
entries::Vector{Any}
if length(entries) != 1
error("expected a single entry for $(repr(dep_name)) in $(repr(project_file))")
end
entry = first(entries)::Dict{String, Any}
uuid = entry["uuid"]::String
deps′_expanded[dep_name] = uuid
end
return deps′_expanded
end

d_weakdeps = Dict{String, Any}()
for (dep_name, entries) in d
dep_name in weakdeps || continue
entries::Vector{Any}
if length(entries) != 1
error("expected a single entry for $(repr(dep_name)) in $(repr(project_file))")
end
entry = first(entries)::Dict{String, Any}
uuid = entry["uuid"]::String
d_weakdeps[dep_name] = uuid
if weakdeps isa Vector{String}
weakdeps = expand_deps_list(weakdeps)
end
if deps isa Vector{String}
deps = expand_deps_list(deps)
end
@assert length(d_weakdeps) == length(weakdeps)
return _insert_extension_triggers(pkg, extensions, d_weakdeps)

total_deps = merge(weakdeps, deps)
return _insert_extension_triggers(pkg, extensions, total_deps)
end
end
end
end
return nothing
end

function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any}, weakdeps::Dict{String, Any})
function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any}, totaldeps::Dict{String, Any})
for (ext, triggers) in extensions
triggers = triggers::Union{String, Vector{String}}
triggers isa String && (triggers = [triggers])
Expand All @@ -1468,7 +1477,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any}
push!(trigger1, gid)
for trigger in triggers
# TODO: Better error message if this lookup fails?
uuid_trigger = UUID(weakdeps[trigger]::String)
uuid_trigger = UUID(totaldeps[trigger]::String)
trigger_id = PkgId(uuid_trigger, trigger)
if !haskey(explicit_loaded_modules, trigger_id) || haskey(package_locks, trigger_id)
trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, trigger_id)
Expand All @@ -1480,6 +1489,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any}
end
end

precompiling_package::Bool = false
loading_extension::Bool = false
precompiling_extension::Bool = false
function run_extension_callbacks(extid::ExtensionId)
Expand Down Expand Up @@ -2172,6 +2182,11 @@ For more details regarding code loading, see the manual sections on [modules](@r
[parallel computing](@ref code-availability).
"""
function require(into::Module, mod::Symbol)
if into === Base.__toplevel__ && precompiling_package
# this error type needs to match the error type compilecache throws for non-125 errors.
error("`using/import $mod` outside of a Module detected. Importing a package outside of a module \
is not allowed during package precompilation.")
end
if _require_world_age[] != typemax(UInt)
Base.invoke_in_world(_require_world_age[], __require, into, mod)
else
Expand Down Expand Up @@ -2750,41 +2765,10 @@ function load_path_setup_code(load_path::Bool=true)
return code
end

"""
check_src_module_wrap(srcpath::String)
Checks that a package entry file `srcpath` has a module declaration, and that it is before any using/import statements.
"""
function check_src_module_wrap(pkg::PkgId, srcpath::String)
module_rgx = r"^(|end |\"\"\" )\s*(?:@)*(?:bare)?module\s"
load_rgx = r"\b(?:using|import)\s"
load_seen = false
inside_string = false
for s in eachline(srcpath)
if count("\"\"\"", s) == 1
# ignore module docstrings
inside_string = !inside_string
end
inside_string && continue
if contains(s, module_rgx)
if load_seen
throw(ErrorException("Package $(repr("text/plain", pkg)) source file $srcpath has a using/import before a module declaration."))
end
return true
end
if startswith(s, load_rgx)
load_seen = true
end
end
throw(ErrorException("Package $(repr("text/plain", pkg)) source file $srcpath does not contain a module declaration."))
end

# this is called in the external process that generates precompiled package files
function include_package_for_output(pkg::PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String},
concrete_deps::typeof(_concrete_dependencies), source::Union{Nothing,String})

check_src_module_wrap(pkg, input)

append!(empty!(Base.DEPOT_PATH), depot_path)
append!(empty!(Base.DL_LOAD_PATH), dl_load_path)
append!(empty!(Base.LOAD_PATH), load_path)
Expand All @@ -2811,11 +2795,22 @@ function include_package_for_output(pkg::PkgId, input::String, depot_path::Vecto
finally
Core.Compiler.track_newly_inferred.x = false
end
# check that the package defined the expected module so we can give a nice error message if not
Base.check_package_module_loaded(pkg)
end

function check_package_module_loaded(pkg::PkgId)
if !haskey(Base.loaded_modules, pkg)
# match compilecache error type for non-125 errors
error("$(repr("text/plain", pkg)) did not define the expected module `$(pkg.name)`, \
check for typos in package module name")
end
return nothing
end

const PRECOMPILE_TRACE_COMPILE = Ref{String}()
function create_expr_cache(pkg::PkgId, input::String, output::String, output_o::Union{Nothing, String},
concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, internal_stderr::IO = stderr, internal_stdout::IO = stdout)
concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, internal_stderr::IO = stderr, internal_stdout::IO = stdout, isext::Bool=false)
@nospecialize internal_stderr internal_stdout
rm(output, force=true) # Remove file if it exists
output_o === nothing || rm(output_o, force=true)
Expand Down Expand Up @@ -2884,7 +2879,8 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o::
write(io.in, """
empty!(Base.EXT_DORMITORY) # If we have a custom sysimage with `EXT_DORMITORY` prepopulated
Base.track_nested_precomp($precomp_stack)
Base.precompiling_extension = $(loading_extension)
Base.precompiling_extension = $(loading_extension | isext)
Base.precompiling_package = true
Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)),
$(repr(load_path)), $deps, $(repr(source_path(nothing))))
""")
Expand Down Expand Up @@ -2941,18 +2937,18 @@ This can be used to reduce package load times. Cache files are stored in
`DEPOT_PATH[1]/compiled`. See [Module initialization and precompilation](@ref)
for important notes.
"""
function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}())
function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), isext::Bool=false)
@nospecialize internal_stderr internal_stdout
path = locate_package(pkg)
path === nothing && throw(ArgumentError("$(repr("text/plain", pkg)) not found during precompilation"))
return compilecache(pkg, path, internal_stderr, internal_stdout; flags, reasons)
return compilecache(pkg, path, internal_stderr, internal_stdout; flags, reasons, isext)
end

const MAX_NUM_PRECOMPILE_FILES = Ref(10)

function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, internal_stdout::IO = stdout,
keep_loaded_modules::Bool = true; flags::Cmd=``, cacheflags::CacheFlags=CacheFlags(),
reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}())
reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), isext::Bool=false)

@nospecialize internal_stderr internal_stdout
# decide where to put the resulting cache file
Expand Down Expand Up @@ -2992,7 +2988,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
close(tmpio_o)
close(tmpio_so)
end
p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, internal_stderr, internal_stdout)
p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, internal_stderr, internal_stdout, isext)

if success(p)
if cache_objects
Expand Down Expand Up @@ -3778,10 +3774,17 @@ end

# now check if this file's content hash has changed relative to its source files
if stalecheck
if !samefile(includes[1].filename, modpath) && !samefile(fixup_stdlib_path(includes[1].filename), modpath)
@debug "Rejecting cache file $cachefile because it is for file $(includes[1].filename) not file $modpath"
record_reason(reasons, "wrong source")
return true # cache file was compiled from a different path
if !samefile(includes[1].filename, modpath)
# In certain cases the path rewritten by `fixup_stdlib_path` may
# point to an unreadable directory, make sure we can `stat` the
# file before comparing it with `modpath`.
stdlib_path = fixup_stdlib_path(includes[1].filename)
if !(isreadable(stdlib_path) && samefile(stdlib_path, modpath))
!samefile(fixup_stdlib_path(includes[1].filename), modpath)
@debug "Rejecting cache file $cachefile because it is for file $(includes[1].filename) not file $modpath"
record_reason(reasons, "wrong source")
return true # cache file was compiled from a different path
end
end
for (modkey, req_modkey) in requires
# verify that `require(modkey, name(req_modkey))` ==> `req_modkey`
Expand Down
49 changes: 2 additions & 47 deletions base/precompilation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ function precompilepkgs(pkgs::Vector{String}=String[];
all_extdeps_available = true
for extdep_uuid in extdep_uuids
extdep_name = env.names[extdep_uuid]
if extdep_uuid in keys(env.deps) || Base.in_sysimage(Base.PkgId(extdep_uuid, extdep_name))
if extdep_uuid in keys(env.deps)
push!(ext_deps, Base.PkgId(extdep_uuid, extdep_name))
else
all_extdeps_available = false
Expand Down Expand Up @@ -429,51 +429,6 @@ function precompilepkgs(pkgs::Vector{String}=String[];
# consider exts of direct deps to be direct deps so that errors are reported
append!(direct_deps, keys(filter(d->last(d) in keys(env.project_deps), exts)))

# An extension effectively depends on another extension if it has all the the
# dependencies of that other extension
function expand_dependencies(depsmap)
function visit!(visited, node, all_deps)
if node in visited
return
end
push!(visited, node)
for dep in get(Set{Base.PkgId}, depsmap, node)
if !(dep in all_deps)
push!(all_deps, dep)
visit!(visited, dep, all_deps)
end
end
end

depsmap_transitive = Dict{Base.PkgId, Set{Base.PkgId}}()
for package in keys(depsmap)
# Initialize a set to keep track of all dependencies for 'package'
all_deps = Set{Base.PkgId}()
visited = Set{Base.PkgId}()
visit!(visited, package, all_deps)
# Update depsmap with the complete set of dependencies for 'package'
depsmap_transitive[package] = all_deps
end
return depsmap_transitive
end

depsmap_transitive = expand_dependencies(depsmap)

for (_, extensions_1) in pkg_exts_map
for extension_1 in extensions_1
deps_ext_1 = depsmap_transitive[extension_1]
for (_, extensions_2) in pkg_exts_map
for extension_2 in extensions_2
extension_1 == extension_2 && continue
deps_ext_2 = depsmap_transitive[extension_2]
if issubset(deps_ext_2, deps_ext_1)
push!(depsmap[extension_1], extension_2)
end
end
end
end
end

@debug "precompile: deps collected"
# this loop must be run after the full depsmap has been populated
for (pkg, pkg_exts) in pkg_exts_map
Expand Down Expand Up @@ -839,7 +794,7 @@ function precompilepkgs(pkgs::Vector{String}=String[];
t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor) do
Base.with_logger(Base.NullLogger()) do
# The false here means we ignore loaded modules, so precompile for a fresh session
Base.compilecache(pkg, sourcepath, std_pipe, std_pipe, false; flags, cacheflags)
Base.compilecache(pkg, sourcepath, std_pipe, std_pipe, false; flags, cacheflags, isext = haskey(exts, pkg))
end
end
if ret isa Base.PrecompilableError
Expand Down
Loading

0 comments on commit f9e4d07

Please sign in to comment.