Skip to content

Commit

Permalink
Backports for Julia 1.10.8 (#56653)
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferC authored Jan 14, 2025
2 parents 4976d05 + 4d7bdbf commit 213feae
Show file tree
Hide file tree
Showing 44 changed files with 365 additions and 41 deletions.
12 changes: 8 additions & 4 deletions base/abstractdict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ Return an iterator over all keys in a dictionary.
When the keys are stored internally in a hash table,
as is the case for `Dict`,
the order in which they are returned may vary.
But `keys(a)` and `values(a)` both iterate `a` and
return the elements in the same order.
But `keys(a)`, `values(a)` and `pairs(a)` all iterate `a`
and return the elements in the same order.
# Examples
```jldoctest
Expand All @@ -112,8 +112,8 @@ Return an iterator over all values in a collection.
When the values are stored internally in a hash table,
as is the case for `Dict`,
the order in which they are returned may vary.
But `keys(a)` and `values(a)` both iterate `a` and
return the elements in the same order.
But `keys(a)`, `values(a)` and `pairs(a)` all iterate `a`
and return the elements in the same order.
# Examples
```jldoctest
Expand All @@ -136,6 +136,10 @@ values(a::AbstractDict) = ValueIterator(a)
Return an iterator over `key => value` pairs for any
collection that maps a set of keys to a set of values.
This includes arrays, where the keys are the array indices.
When the entries are stored internally in a hash table,
as is the case for `Dict`, the order in which they are returned may vary.
But `keys(a)`, `values(a)` and `pairs(a)` all iterate `a`
and return the elements in the same order.
# Examples
```jldoctest
Expand Down
1 change: 1 addition & 0 deletions base/binaryplatforms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ const arch_mapping = Dict(
"armv7l" => "arm(v7l)?", # if we just see `arm-linux-gnueabihf`, we assume it's `armv7l`
"armv6l" => "armv6l",
"powerpc64le" => "p(ower)?pc64le",
"riscv64" => "(rv64|riscv64)",
)
# Keep this in sync with `CPUID.ISAs_by_family`
# These are the CPUID side of the microarchitectures targeted by GCC flags in BinaryBuilder.jl
Expand Down
2 changes: 2 additions & 0 deletions base/compiler/ssair/domtree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,8 @@ end
Compute the nearest common (post-)dominator of `a` and `b`.
"""
function nearest_common_dominator(domtree::GenericDomTree, a::BBNumber, b::BBNumber)
a == 0 && return a
b == 0 && return b
alevel = domtree.nodes[a].level
blevel = domtree.nodes[b].level
# W.l.g. assume blevel <= alevel
Expand Down
1 change: 1 addition & 0 deletions base/compiler/ssair/passes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,7 @@ function try_resolve_finalizer!(ir::IRCode, idx::Int, finalizer_idx::Int, defuse
end
all(check_defuse, defuse.uses) || return nothing
all(check_defuse, defuse.defs) || return nothing
bb_insert_block != 0 || return nothing # verify post-dominator of all uses exists

# Check #3
dominates(domtree, finalizer_bb, bb_insert_block) || return nothing
Expand Down
5 changes: 4 additions & 1 deletion base/docs/basedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1441,7 +1441,7 @@ kw"new"
"""
where
The `where` keyword creates a type that is an iterated union of other types, over all
The `where` keyword creates a [`UnionAll`](@ref) type, which may be thought of as an iterated union of other types, over all
values of some variable. For example `Vector{T} where T<:Real` includes all [`Vector`](@ref)s
where the element type is some kind of `Real` number.
Expand Down Expand Up @@ -3296,6 +3296,9 @@ unused and delete the entire benchmark code).
!!! compat "Julia 1.8"
This method was added in Julia 1.8.
!!! compat "Julia 1.8"
This method was added in Julia 1.8.
# Examples
```julia
Expand Down
12 changes: 10 additions & 2 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,12 @@ end
## generic project & manifest API ##

const project_names = ("JuliaProject.toml", "Project.toml")
const manifest_names = ("JuliaManifest.toml", "Manifest.toml")
const manifest_names = (
"JuliaManifest-v$(VERSION.major).$(VERSION.minor).toml",
"Manifest-v$(VERSION.major).$(VERSION.minor).toml",
"JuliaManifest.toml",
"Manifest.toml",
)
const preferences_names = ("JuliaLocalPreferences.toml", "LocalPreferences.toml")

function locate_project_file(env::String)
Expand Down Expand Up @@ -1215,6 +1220,7 @@ function run_module_init(mod::Module, i::Int=1)
end

function run_package_callbacks(modkey::PkgId)
(modkey == precompilation_target) && return nothing
run_extension_callbacks(modkey)
assert_havelock(require_lock)
unlock(require_lock)
Expand Down Expand Up @@ -1338,7 +1344,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any}
# TODO: Better error message if this lookup fails?
uuid_trigger = UUID(totaldeps[trigger]::String)
trigger_id = PkgId(uuid_trigger, trigger)
if !haskey(Base.loaded_modules, trigger_id) || haskey(package_locks, trigger_id)
if !haskey(Base.loaded_modules, trigger_id) || haskey(package_locks, trigger_id) || (trigger_id == precompilation_target)
trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, trigger_id)
push!(trigger1, gid)
else
Expand All @@ -1350,6 +1356,7 @@ end

loading_extension::Bool = false
precompiling_extension::Bool = false
precompilation_target::Union{Nothing,PkgId} = nothing
function run_extension_callbacks(extid::ExtensionId)
assert_havelock(require_lock)
succeeded = try
Expand Down Expand Up @@ -2342,6 +2349,7 @@ 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.precompiling_extension = $(loading_extension)
Base.precompilation_target = $(pkg_str(pkg))
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
2 changes: 1 addition & 1 deletion base/mathconstants.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ end
Base.@assume_effects :foldable function (::Type{T})(x::_KnownIrrational, r::RoundingMode) where {T<:Union{Float32,Float64}}
Base._irrational_to_float(T, x, r)
end
Base.@assume_effects :foldable function rationalize(::Type{T}, x::_KnownIrrational; tol::Real=0) where {T<:Integer}
Base.@assume_effects :foldable function Base.rationalize(::Type{T}, x::_KnownIrrational; tol::Real=0) where {T<:Integer}
Base._rationalize_irrational(T, x, tol)
end
Base.@assume_effects :foldable function Base.lessrational(rx::Rational, x::_KnownIrrational)
Expand Down
5 changes: 5 additions & 0 deletions base/meta.jl
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,15 @@ function _partially_inline!(@nospecialize(x), slot_replacements::Vector{Any},
return x
end
if isa(x, Core.ReturnNode)
# Unreachable doesn't have val defined
if !isdefined(x, :val)
return x
else
return Core.ReturnNode(
_partially_inline!(x.val, slot_replacements, type_signature, static_param_values,
slot_offset, statement_offset, boundscheck),
)
end
end
if isa(x, Core.GotoIfNot)
return Core.GotoIfNot(
Expand Down
2 changes: 2 additions & 0 deletions base/special/exp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ end
small_part = muladd(jU, expm1b_kernel(base, r), jL) + jU

if !(abs(x) <= SUBNORM_EXP(base, T))
isnan(x) && return x
x >= MAX_EXP(base, T) && return Inf
x <= MIN_EXP(base, T) && return 0.0
if k <= -53
Expand Down Expand Up @@ -243,6 +244,7 @@ end
hi, lo = Base.canonicalize2(1.0, kern)
small_part = fma(jU, hi, muladd(jU, (lo+xlo), very_small))
if !(abs(x) <= SUBNORM_EXP(base, T))
isnan(x) && return x
x >= MAX_EXP(base, T) && return Inf
x <= MIN_EXP(base, T) && return 0.0
if k <= -53
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
220272702d818b059c86d96e5d7b6483
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f87ae5aee2c875028cca1b6a3a2a6b494317c20b204ee25b0f62e4fa44ad7d74833e0d16ed0fe90747ed81bc1d075427f24f8820573b74335574ceaae14fda5b
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
12b074088311e98aa7b9d03da61cec9b
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
285ceafd68508831fbaa84ce9bb02da6d0cd1619303640dc46a59877214f300ff891c73a8a92a0a34d0e560f7495edfa1a7656e64afc5b908c40849a21dea60e

This file was deleted.

This file was deleted.

4 changes: 2 additions & 2 deletions doc/src/manual/code-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Code inclusion is quite straightforward and simple: it evaluates the given sourc

A *package* is a source tree with a standard layout providing functionality that can be reused by other Julia projects. A package is loaded by `import X` or `using X` statements. These statements also make the module named `X`—which results from loading the package code—available within the module where the import statement occurs. The meaning of `X` in `import X` is context-dependent: which `X` package is loaded depends on what code the statement occurs in. Thus, handling of `import X` happens in two stages: first, it determines **what** package is defined to be `X` in this context; second, it determines **where** that particular `X` package is found.

These questions are answered by searching through the project environments listed in [`LOAD_PATH`](@ref) for project files (`Project.toml` or `JuliaProject.toml`), manifest files (`Manifest.toml` or `JuliaManifest.toml`), or folders of source files.
These questions are answered by searching through the project environments listed in [`LOAD_PATH`](@ref) for project files (`Project.toml` or `JuliaProject.toml`), manifest files (`Manifest.toml` or `JuliaManifest.toml`, or the same names suffixed by `-v{major}.{minor}.toml` for specific versions), or folders of source files.


## Federation of packages
Expand Down Expand Up @@ -63,7 +63,7 @@ Each kind of environment defines these three maps differently, as detailed in th

### Project environments

A project environment is determined by a directory containing a project file called `Project.toml`, and optionally a manifest file called `Manifest.toml`. These files may also be called `JuliaProject.toml` and `JuliaManifest.toml`, in which case `Project.toml` and `Manifest.toml` are ignored. This allows for coexistence with other tools that might consider files called `Project.toml` and `Manifest.toml` significant. For pure Julia projects, however, the names `Project.toml` and `Manifest.toml` are preferred.
A project environment is determined by a directory containing a project file called `Project.toml`, and optionally a manifest file called `Manifest.toml`. These files may also be called `JuliaProject.toml` and `JuliaManifest.toml`, in which case `Project.toml` and `Manifest.toml` are ignored. This allows for coexistence with other tools that might consider files called `Project.toml` and `Manifest.toml` significant. For pure Julia projects, however, the names `Project.toml` and `Manifest.toml` are preferred. However, from Julia v1.10.8 onwards, `(Julia)Manifest-v{major}.{minor}.toml` is recognized as a format to make a given julia version use a specific manifest file i.e. in the same folder, a `Manifest-v1.11.toml` would be used by v1.11 and `Manifest.toml` by any other julia version.

The roots, graph and paths maps of a project environment are defined as follows:

Expand Down
19 changes: 18 additions & 1 deletion doc/src/manual/complex-and-rational-numbers.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,30 @@ julia> float(3//4)
```

Conversion from rational to floating-point respects the following identity for any integral values
of `a` and `b`, with the exception of the two cases `b == 0` and `a == 0 && b < 0`:
of `a` and `b`, except when `a==0 && b <= 0`:

```jldoctest
julia> a = 1; b = 2;
julia> isequal(float(a//b), a/b)
true
julia> a, b = 0, 0
(0, 0)
julia> float(a//b)
ERROR: ArgumentError: invalid rational: zero(Int64)//zero(Int64)
Stacktrace:
[...]
julia> a/b
NaN
julia> a, b = 0, -1
(0, -1)
julia> float(a//b), a/b
(0.0, -0.0)
```

Constructing infinite rational values is acceptable:
Expand Down
10 changes: 9 additions & 1 deletion src/jitlayers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,15 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES
}
else {
// identify whether this is an invalidated method that is being recompiled
is_recompile = jl_atomic_load_relaxed(&mi->cache) != NULL;
// Is a recompile if there is cached code, and it was compiled (not only inferred) before
jl_code_instance_t *codeinst_old = jl_atomic_load_relaxed(&mi->cache);
while (codeinst_old != NULL) {
if (jl_atomic_load_relaxed(&codeinst_old->invoke) != NULL) {
is_recompile = 1;
break;
}
codeinst_old = jl_atomic_load_relaxed(&codeinst_old->next);
}
}
if (src == NULL && jl_is_method(mi->def.method) &&
jl_symbol_name(mi->def.method->name)[0] != '@') {
Expand Down
12 changes: 10 additions & 2 deletions src/staticdata.c
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ void *native_functions; // opaque jl_native_code_desc_t blob used for fetching

// table of struct field addresses to rewrite during saving
static htable_t field_replace;
static htable_t relocatable_ext_cis;

// array of definitions for the predefined function pointers
// (reverse of fptr_to_id)
Expand Down Expand Up @@ -656,7 +657,8 @@ static int needs_uniquing(jl_value_t *v) JL_NOTSAFEPOINT

static void record_field_change(jl_value_t **addr, jl_value_t *newval) JL_NOTSAFEPOINT
{
ptrhash_put(&field_replace, (void*)addr, newval);
if (*addr != newval)
ptrhash_put(&field_replace, (void*)addr, newval);
}

static jl_value_t *get_replaceable_field(jl_value_t **addr, int mutabl) JL_GC_DISABLED
Expand Down Expand Up @@ -797,6 +799,8 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_
// TODO: if (ci in ci->defs->cache)
record_field_change((jl_value_t**)&ci->next, NULL);
}
if (jl_atomic_load_relaxed(&ci->inferred) && !is_relocatable_ci(&relocatable_ext_cis, ci))
record_field_change((jl_value_t**)&ci->inferred, jl_nothing);
}

if (immediate) // must be things that can be recursively handled, and valid as type parameters
Expand Down Expand Up @@ -1505,6 +1509,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED
// will check on deserialize if this cache entry is still valid
}
}
newm->relocatability = 0;
}

newm->invoke = NULL;
Expand Down Expand Up @@ -2384,7 +2389,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new
*edges = jl_alloc_vec_any(0);
*method_roots_list = jl_alloc_vec_any(0);
// Collect the new method roots
jl_collect_new_roots(*method_roots_list, *new_specializations, worklist_key);
jl_collect_new_roots(&relocatable_ext_cis, *method_roots_list, *new_specializations, worklist_key);
jl_collect_edges(*edges, *ext_targets, *new_specializations, world);
}
assert(edges_map == NULL); // jl_collect_edges clears this when done
Expand Down Expand Up @@ -2770,6 +2775,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
assert((ct->reentrant_timing & 0b1110) == 0);
ct->reentrant_timing |= 0b1000;
if (worklist) {
htable_new(&relocatable_ext_cis, 0);
jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist),
&extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
if (!emit_split) {
Expand All @@ -2786,6 +2792,8 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges);
if (_native_data != NULL)
native_functions = NULL;
if (worklist)
htable_free(&relocatable_ext_cis);
// make sure we don't run any Julia code concurrently before this point
// Re-enable running julia code for postoutput hooks, atexit, etc.
jl_gc_enable_finalizers(ct, 1);
Expand Down
14 changes: 13 additions & 1 deletion src/staticdata_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,17 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited,
return found;
}

static int is_relocatable_ci(htable_t *relocatable_ext_cis, jl_code_instance_t *ci)
{
if (!ci->relocatability)
return 0;
jl_method_instance_t *mi = ci->def;
jl_method_t *m = mi->def.method;
if (!ptrhash_has(relocatable_ext_cis, ci) && jl_object_in_image((jl_value_t*)m) && (!jl_is_method(m) || jl_object_in_image((jl_value_t*)m->module)))
return 0;
return 1;
}

// Given the list of CodeInstances that were inferred during the build, select
// those that are (1) external, (2) still valid, (3) are inferred to be called
// from the worklist or explicitly added by a `precompile` statement, and
Expand Down Expand Up @@ -261,7 +272,7 @@ static jl_array_t *queue_external_cis(jl_array_t *list)
}

// New roots for external methods
static void jl_collect_new_roots(jl_array_t *roots, jl_array_t *new_specializations, uint64_t key)
static void jl_collect_new_roots(htable_t *relocatable_ext_cis, jl_array_t *roots, jl_array_t *new_specializations, uint64_t key)
{
htable_t mset;
htable_new(&mset, 0);
Expand All @@ -272,6 +283,7 @@ static void jl_collect_new_roots(jl_array_t *roots, jl_array_t *new_specializati
jl_method_t *m = ci->def->def.method;
assert(jl_is_method(m));
ptrhash_put(&mset, (void*)m, (void*)m);
ptrhash_put(relocatable_ext_cis, (void*)ci, (void*)ci);
}
int nwithkey;
void *const *table = mset.table;
Expand Down
42 changes: 42 additions & 0 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,42 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t
return sub;
}

static int equal_var(jl_tvar_t *v, jl_value_t *x, jl_stenv_t *e)
{
assert(e->Loffset == 0);
// Theoretically bounds change would be merged for union inputs.
// But intersection is not happy as splitting helps to avoid circular env.
assert(!e->intersection || !jl_is_uniontype(x));
jl_varbinding_t *vb = lookup(e, v);
if (e->intersection && vb != NULL && vb->lb == vb->ub && jl_is_typevar(vb->lb))
return equal_var((jl_tvar_t *)vb->lb, x, e);
record_var_occurrence(vb, e, 2);
if (vb == NULL)
return e->ignore_free || (
local_forall_exists_subtype(x, v->lb, e, 2, !jl_has_free_typevars(x)) &&
local_forall_exists_subtype(v->ub, x, e, 0, 0));
if (!vb->right)
return local_forall_exists_subtype(x, vb->lb, e, 2, !jl_has_free_typevars(x)) &&
local_forall_exists_subtype(vb->ub, x, e, 0, 0);
if (vb->lb == x)
return var_lt(v, x, e, 0);
if (!subtype_ccheck(x, vb->ub, e))
return 0;
jl_value_t *lb = simple_join(vb->lb, x);
JL_GC_PUSH1(&lb);
if (!e->intersection || !jl_is_typevar(lb) || !reachable_var(lb, v, e))
vb->lb = lb;
JL_GC_POP();
if (vb->ub == x)
return 1;
if (!subtype_ccheck(vb->lb, x, e))
return 0;
// skip `simple_meet` here as we have proven `x <: vb->ub`
if (!e->intersection || !reachable_var(x, v, e))
vb->ub = x;
return 1;
}

static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)
{
if (obviously_egal(x, y)) return 1;
Expand Down Expand Up @@ -1602,6 +1638,12 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)
}
}

if (e->Loffset == 0 && jl_is_typevar(y) && jl_is_type(x) && (!e->intersection || !jl_is_uniontype(x))) {
// Fastpath for Type == TypeVar.
// Avoid duplicated `<:` check between adjacent `var_gt` and `var_lt`
return equal_var((jl_tvar_t *)y, x, e);
}

jl_saved_unionstate_t oldLunions; push_unionstate(&oldLunions, &e->Lunions);

int sub = local_forall_exists_subtype(x, y, e, 2, -1);
Expand Down
Loading

0 comments on commit 213feae

Please sign in to comment.