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

Backports for Julia 1.10.8 #56653

Merged
merged 26 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
78de77e
Restrict binary ops for Diagonal and Symmetric to Number eltypes (#55…
jishnub Jul 28, 2024
ab4f153
Add compat entry for `Base.donotdelete` (#55773)
LilithHafner Sep 14, 2024
cc996c7
Fix `log_quasitriu` for internal scaling `s=0` (#56311)
aravindh-krishnamoorthy Oct 28, 2024
ace962e
🤖 [backports-release-1.10] Bump the Pkg stdlib from 06f7a7e5b to 8459…
DilumAluthgeBot Nov 24, 2024
87c83b0
Prevent pre-compilation package from triggering its own extensions (#…
topolarity Nov 24, 2024
64e0f11
Add preference for version named manifest files (#43845) (#56600)
IanButterworth Nov 28, 2024
4f7d79e
bump SparseArrays to latest v1.10
KristofferC Dec 1, 2024
964fdf0
Handle no-postdominator case in finalizer pass
topolarity Jun 11, 2024
0c205c9
update irutils.jl and fix the irpasses.jl test
aviatesk Dec 3, 2024
70b34b2
subtype: fast path for Type == TypeVar (#56640)
N5N3 Nov 26, 2024
708c42d
fix `exp(weirdNaN)` (#56784)
oscardssmith Dec 10, 2024
e295286
Fix partially_inline for unreachable (#56787)
wsmoses Dec 12, 2024
1ddd0b6
bump Pkg to latest 1.10
Dec 13, 2024
4675b3b
Revert "Restrict binary ops for Diagonal and Symmetric to Number elty…
Jan 2, 2025
e62a06c
xref `UnionAll` in the doc string of `where` (#56411)
nsajko Dec 15, 2024
a76b887
docs: fix edge case in rational number conversion `float(a//b)` (#56772)
Priynsh Dec 18, 2024
fffbe5c
dict docs: document that ordering of keys/values/pairs match iterate …
cossio Dec 18, 2024
2215785
Extend `Base.rationalize` instead of defining new function (#56793)
sostock Dec 19, 2024
84ec1cc
Don't report only-inferred methods as recompiles (#56914)
IanButterworth Jan 2, 2025
befc611
[BinaryPlatforms] Parse `rc64`/`riscv64` triplets
giordano Jan 8, 2025
c7ff203
🤖 [backports-release-1.10] Bump the SparseArrays stdlib from 78035e1 …
DilumAluthgeBot Jan 10, 2025
0c13b15
🤖 [backports-release-1.10] Bump the Pkg stdlib from 0ac49eb3e to 6390…
DilumAluthgeBot Jan 10, 2025
24062bf
Backport "serialization: fix relocatability bug" to 1.10 (#56973)
vchuravy Jan 10, 2025
c872119
disable flaky tests - part backport from #53682
IanButterworth Jan 12, 2025
926eedc
REPL: Limit method lookup when completing kwargs (#56963)
IanButterworth Jan 6, 2025
4d7bdbf
fix kwarg limit method backport
KristofferC Jan 13, 2025
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
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
Loading