Skip to content

subtype: distribute a ∀ var over the arms of its union upper bound - #1

Open
Keno wants to merge 2788 commits into
masterfrom
kf/subtype-forall-ub-split
Open

Keno wants to merge 2788 commits into
masterfrom
kf/subtype-forall-ub-split

Conversation

@Keno

@Keno Keno commented Jun 11, 2026 •

Copy link
Copy Markdown
Member

A left-side (universally quantified) where variable with a trivial lower bound, a union upper bound, and only covariant occurrences in the body ranges over each arm of its upper bound independently, i.e. the UnionAll distributes over the arms:

(Tuple{T,T} where T<:Union{A,B}) ==
    Union{Tuple{T,T} where T<:A, Tuple{T,T} where T<:B}

(diagonality, if any, is preserved: each value of such a variable is concrete and therefore lies entirely within a single arm).

Exploit this in subtype_unionall on the ∀ path by splitting the upper bound before descending into the body: descend the ub's Union tree with pick_union_decision(e, 0), so each Union node registers as an ordinary left-union decision and the enclosing ∀∃ loop enumerates all arms. This makes judgments like

(Tuple{T,T} where T<:Union{Float64,Int64}) <:
    Union{Tuple{Float64,Float64},Tuple{Int64,Int64}}

true, and fixes a previously @test_broken type equality. The gate is checked by a new static walker: covariant here means reachable purely through Tuple parameters, Union components, and Vararg element types; any occurrence under a non-Tuple datatype parameter, in a Vararg length, or inside an inner UnionAll disables the split. The ∃ side, lower bounds, and intersection are unaffected.

jl_obvious_subtype was verified to return "unknown" (never definitely-false) on the newly-true judgments, so it cannot mask the new result in release builds nor trip the consistency assert in debug builds.

Since jl_types_equal and specificity results may shift, a PkgEval run is advisable. The subtype, specificity, ambiguous, and core test suites pass locally, as do clang-sa-subtype and clang-sagc-subtype.

This pull request was written with the assistance of generative AI (Claude).

🤖 Generated with Claude Code

DilumAluthgeBot and others added 30 commits May 13, 2026 12:01
Stdlib: Downloads
URL: https://github.com/JuliaLang/Downloads.jl.git
Stdlib branch: master
Julia branch: master
Old commit: 168d18d
New commit: faa1314
Julia version: 1.14.0-DEV
Downloads version: 1.7.0 (It's okay that it doesn't match)
Bump invoked by: @DilumAluthge
Powered by:
[BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl)

Diff:
JuliaLang/Downloads.jl@168d18d...faa1314

```
$ git log --oneline 168d18d..faa1314
faa1314 Bump julia-actions/setup-julia from 2 to 3 (JuliaLang#292)
e59fd8b Bump julia-actions/cache from 2 to 3 (JuliaLang#290)
f1937ce Bump codecov/codecov-action from 5 to 6 (JuliaLang#291)
```

Co-authored-by: DilumAluthge <5619885+DilumAluthge@users.noreply.github.com>
Stdlib: LibCURL
URL: https://github.com/JuliaWeb/LibCURL.jl.git
Stdlib branch: master
Julia branch: master
Old commit: 2e3f4cc
New commit: b8ee014
Julia version: 1.14.0-DEV
LibCURL version: 1.0.0 (Does not match)
Bump invoked by: @DilumAluthge
Powered by:
[BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl)

Diff:
JuliaWeb/LibCURL.jl@2e3f4cc...b8ee014

```
$ git log --oneline 2e3f4cc..b8ee014
b8ee014 Bump julia-actions/cache from 2 to 3 (JuliaLang#120)
71acb9f Bump codecov/codecov-action from 5 to 6 (JuliaLang#121)
```

Co-authored-by: DilumAluthge <5619885+DilumAluthge@users.noreply.github.com>
…g#61645)

This is a significant cleanup of he semantics of subtyping envout by removing any semantic relation between TypeVars and the inputs. See JuliaLang#61634 and JuliaLang#61645 for more details.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Stdlib: Tar
URL: https://github.com/JuliaIO/Tar.jl.git
Stdlib branch: master
Julia branch: master
Old commit: 6a61f5c
New commit: 237cfcb
Julia version: 1.14.0-DEV
Tar version: 1.10.0 (Does not match)
Bump invoked by: @DilumAluthge
Powered by:
[BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl)

Diff:
JuliaIO/Tar.jl@6a61f5c...237cfcb

```
$ git log --oneline 6a61f5c..237cfcb
237cfcb Typo and tweaks from AI review (JuliaLang#195)
c168bb1 avoid using a global buffer for `skip` since it is racy
4c35aaf avoid making no progress with cyclic symlinks
f106eec be more accurate in checking bad Windows paths, trailing dot and space is also not allowed
68ed6c1 fix typo doing double `typemax`
6ed14a8 use `UInt8('\n')` to be consistent with other char -> uint conversions
6c4b9b6 NFC: correct argument to `check_extract_tarball`
97cc930 Bump julia-actions/setup-julia from 2 to 3 (JuliaLang#194)
```

Co-authored-by: DilumAluthge <5619885+DilumAluthge@users.noreply.github.com>
Stdlib: SHA
URL: https://github.com/JuliaCrypto/SHA.jl.git
Stdlib branch: master
Julia branch: master
Old commit: e7069c3
New commit: 51a0726
Julia version: 1.14.0-DEV
SHA version: 1.13.0 (Does not match)
Bump invoked by: @DilumAluthge
Powered by:
[BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl)

Diff:
JuliaCrypto/SHA.jl@e7069c3...51a0726

```
$ git log --oneline e7069c3..51a0726
51a0726 Update project version to 1.13.0 (JuliaLang#131)
4b34db6 Bump julia-actions/cache from 2 to 3 (JuliaLang#129)
028a2fb Bump codecov/codecov-action from 5 to 6 (JuliaLang#130)
```

Co-authored-by: DilumAluthge <5619885+DilumAluthge@users.noreply.github.com>
…ookup tables (JuliaLang#61766)

closes JuliaLang#44175

`serialize` contains an optimization to deduplicate identical objects
both for performance benefit and to retain aliasing relationships during
round trips.

conceptually, it does this by: for each value that is eligible for
`serialize_cycle` (in practice, this mostly resolves to `ismutable`),
maintain `counter` for the index at which we encountered that object,
and save the `objectid` for future lookups. Then, if we see the same
`objectid` later, serialize a `BACKREF` to the appropriate `counter`.

On the deser side, when we see a `BACKREF`, access the object at the
index of the associated `counter` rather than making a new object.

however, it uses an `IdDict` for both purposes. this PR squeezes out a
lot more performance by using a `Dict` for the serialization lookup and
a `Vector` for the deser.

questions for the reviewers mostly concern compatibility. this does
change the fields of `Serializer` which is (unfortunately) a public
type. but I'm not sure if the fields themselves are considered public? I
have headed off in advance impacts to, e.g.
[`Distributed.ClusterSerializer`](https://github.com/JuliaLang/Distributed.jl/blob/fde987fbb05c65bb6bd183396137383931107059/src/clusterserialize.jl#L10-L14)
which makes assumptions about the way that `Serialization.serialize`
makes use of a `.table` field on `AbstractSerializer`, by factoring out
these field accesses into getter/setter functions and then specializing
on `Serializer`. Is this an appropriate compromise? or should we just
patch `Distributed` to not rely on this assumption? or is the change to
`Serialization` considered wholesale breaking. there was a similar
discussion in JuliaLang#43554

here are performance numbers on a few workloads:

on the motivating MWE from
JuliaLang#44175:
<details>
<summary> setup</summary>

```julia
mutable struct MinimalNode
    childvalues::Union{NTuple{4, Int8}, NTuple{3, Int8}, NTuple{2, Int8}, NTuple{1, Int8}, Tuple{}};
    childnodes::Union{NTuple{4, MinimalNode}, NTuple{3, MinimalNode}, NTuple{2, MinimalNode}, NTuple{1, MinimalNode}, Tuple{}};
end

function addbranches!(mn::MinimalNode, aleft::Int64, bleft::Int64, parity::Int64, depth = 0)

    (depth == 12) && (return nothing);

    if parity == 1
        (aleft <= 1) && (return nothing);
        mn.childvalues = Tuple(1:aleft);
        mn.childnodes = Tuple([MinimalNode((), ()) for ii in 1:aleft]);
        aleft -= 1;
    else
        (bleft <= 1) && (return nothing);
        mn.childvalues = Tuple(1:bleft);
        mn.childnodes = Tuple([MinimalNode((), ()) for ii in 1:bleft]);
        bleft -= 1;
    end

    addbranches!.(mn.childnodes, aleft, bleft, 3-parity, depth + 1);
    
end

function buildfaketree()
    root = MinimalNode((), ());
    addbranches!(root, 4, 4, 1, 0);
    return root;
end


using Serialization;
MN = [buildfaketree() for ii in 1:10000];
@time serialize("test.jls", MN);
35 seconds
```
</details>

PR:
```julia
julia> @time serialize("test.jls", MN);
  6.419340 seconds (15.78 M allocations: 784.602 MiB, 17.18% gc time)

julia> @time deserialize("test.jls");
  4.382877 seconds (20.79 M allocations: 817.876 MiB, 32.13% gc time)
```

master:
```julia
julia> @time serialize("test_master.jls", MN);
 17.084042 seconds (26.55 M allocations: 917.083 MiB, 5.38% gc time)

julia> @time deserialize("test_master.jls");
 20.479949 seconds (31.56 M allocations: 1.204 GiB, 15.45% gc time)
```

but we also improve something as simple as `Vector{String}`

setup:
```julia
julia> using Random

julia> v = map((_)->randstring(10), 1:1000000);
```

PR:
```julia
julia> @time serialize("string.jls", v);
  0.240658 seconds (37 allocations: 59.501 MiB, 9.13% gc time)

julia> @time deserialize("string.jls");
  0.084324 seconds (1.00 M allocations: 64.112 MiB)
```

master:
```julia
julia> @time serialize("string.jls", v);
  0.592193 seconds (999.51 k allocations: 43.252 MiB, 11.42% gc time)

julia> @time deserialize("string.jls");
  0.371651 seconds (2.00 M allocations: 81.410 MiB, 15.02% gc time)
```

assisted as always by Opus 4.7
…uliaLang#61802)

Two one-line typo fixes for duplicated words in source comments:
- `src/signals-unix.c` — "redefine this as as an \"unreachable reached\"
error message" → "redefine this as an \"unreachable reached\" error
message"
- `JuliaLowering/src/desugaring.jl` — "is the list of statements which
needs to to be emitted" → "...which needs to be emitted"

No code/behavior change.

Co-authored-by: Maya Chen <275405107+otjdiepluong@users.noreply.github.com>
…liaLang#61791)

The diagonal rule was changed to semi-static in JuliaLang#34272 — it uses
`var_occurs_invariant(u->body, u->var)` (a structural check on the
UnionAll body) rather than the dynamic `occurs_inv` counter, since the
latter only reflects whichever invariant positions were actually
traversed. The recently-added diagonality-change check in
`env_unchanged` (JuliaLang#61503), however, still tested `v->occurs_inv == 0`, so
it could spuriously flag a variable as "newly diagonal" when its body
has T only in branches the algorithm never visited.

Cache `var_occurs_invariant(u->body, u->var)` once at varbinding
creation as `body_occurs_inv` and use it in `env_unchanged` (and at the
existing recompute sites in `subtype_unionall` and
`intersect_unionall`).

The two checks can be made to diverge by hiding a `Ref{T}` in a Union
branch that is never picked dynamically (added as a regression test);
However, the divergence only changes how much `exists_subtype`
exploration is performed after `sub == true` and not the boolean result
or the chosen ∃-bindings. As a result, this is a performance fix, but
not a correctness fix.

Co-authored-by: Keno Fischer <Keno@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
as for the pairs version

Co-authored-by: Andy Dienes <51664769+adienes@users.noreply.github.com>
This reduces the number of invalidations of loading BufferIO.jl commit
957f141 from 1026 to 549. The large number of invalidations come from
excessive world splitting on un-inferred IO objects, which, ironically,
are probably intentionally left un-inferred to reduce unnecessary
compilation.

This is a poor bandaid solution: World splitting delenda est and all
that. If that is not feasible, then an option to selectively disable it
e.g. through a future `@max_methods 0` would impove the situation. For
example, about 400 methods are invalidated due to the addition of a new
`write(::T, ::Char)` method, which invalidates the single, world split
`write(::IO, ::Char)` method.

However, deeper fixes of this issue is outside the scope of this PR.

---------

Co-authored-by: Jeff Bezanson <jeff.bezanson@gmail.com>
Co-authored-by: Andy Dienes <51664769+adienes@users.noreply.github.com>
This is a less-ambitious version of the program started in JuliaLang#60736 that
avoids the need for JuliaLang#61200, by restricting itself to world-age at
definition and side-stepping the issue of which names were `export`ed or
`public` at specific world ages.
A copy of my OP plus a summary of the new strategy:

InteractiveUtils provides a number of utilities for querying and
accessing the contents of modules. Because these modules can gain new
bindings, some callers of InteractiveUtils functions produce warnings

WARNING: Detected access to binding ... prior to its definition world.

One dramatic example occurs with the sequence

    using OptimizationProblems: OptimizationProblems
    using OptimizationProblems.ADNLPProblems
    using ADNLPModels: ADNLPModel

when running with Revise. ADNLPProblems loads new problems via Requires,
and starting with v3.13 Revise queries `InteractiveUtils.subtypes` to
cache dependent fieldtypes. This triggers the warning.

To fix this, add a `world` keyword argument to `names`/`unsorted_names`
to control the world age at which bindings are looked up. It is then
threaded through `varinfo`, `methodswith`, and `subtypes` in
InteractiveUtils so they operate on bindings in the correct world.

The default for the `world` kwarg preserves the current behavior:
`names`/`unsorted_names` default to `tls_world_age()` (matching the
prior C-side use of `jl_current_task->world_age`); the InteractiveUtils
entry points default to `get_world_counter()` so interactive tools see
the latest definitions by default.

Closes JuliaLang#60736
Closes JuliaLang#61200

Related: timholy/Revise.jl#993

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
**Best reviewed with "Hide whitespace"

When looking into JuliaLang#61292, I tried
to use `versioninfo` to get the JIT’s cpu target. I found out that even
though it shares parentheses with “ORCJIT”, the displayed cpu is always
just the host CPU.

Then I fell into the confusing and undocumented output of `versioninfo`
rabbit hole trying to improve it and this is the result of that. Once I
have a better idea about which changes are/aren't welcome, I’ll edit the
docstring to be more explicit about what each item is.

May be worth reviewing per-commit as I've tried to separate each change

This PR:
* Changes “GB” and “MB” to “GiB” and “MiB” when reporting memory
information.
* Moves the detected host cpu llvm target to be with the CPU instead of
in the LLVM section. I changed this because the previous way implied it
was the JIT target cpu. They usually match, but they may not if julia
was launched with the `-C` flag
* The build commit has been moved down into “Build Information”
* Refactors the “Build Information” section. It will now always be
shown, and the GC julia was built with was moved from the “Platform
Information” section.
* Adds the target cpu and triple that the base system image was built
with when `verbose=true`. ~This adds a new Sys.BASE_SYSIMG_TARGET
constant variable because calling `sysimage_target` from
InteractiveUtils returns “sysimage”. Hidden behind `verbose` flag~ Edit:
`sysimage_target` behaviour seems to have been fixed so the
implementation now uses it.
* LLVM section now reports the JIT target CPU where the host CPU use to
be. ~This expands/uses API that was added by @gbaraldi for GPUCompiler
in JuliaLang#49858.~ (edit: I found a way to do it using existing code)
* The triple on the OS line is now the JIT target triple (same
implementations worries as above), and the build machine triple
(MACHINE) is now reported in the verbose-only sysimage line in “Build
Information”. This is probably more relevant on macOS, where the triple
includes the kernel version, but I find it
[confusing](JuliaGPU/Metal.jl#179 (comment))
to show the build kernel version when reporting the current OS.

Current output (master):
```julia-repl
julia> versioninfo()
Julia Version 1.14.0-DEV.2169
Commit 61cb046 (2026-05-10 23:05 UTC)
Build Info:
  Official https://julialang.org release
Platform Info:
  OS: macOS (arm64-apple-darwin24.0.0)
  CPU: 12 × Apple M2 Max
  WORD_SIZE: 64
  LLVM: libLLVM-21.1.8 (ORCJIT, apple-m2)
  GC: Built with stock GC
Threads: 8 default, 1 interactive, 8 GC (on 8 virtual cores)
Environment:
  JULIA_NUM_THREADS = auto

julia> versioninfo(verbose=true)
Julia Version 1.14.0-DEV.2169
Commit 61cb046 (2026-05-10 23:05 UTC)
Build Info:
  Official https://julialang.org release
Platform Info:
  OS: macOS (arm64-apple-darwin24.0.0)
  uname: Darwin 25.5.0 Darwin Kernel Version 25.5.0: Mon Apr 27 20:39:09 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T6020 arm64 arm
  CPU: Apple M2 Max:
                 speed         user         nice          sys         idle          irq
       #1-12  2400 MHz     131350 s          0 s      70466 s    1842258 s          0 s
  Memory: 32.0 GB (304.4375 MB free)
  Uptime: 469303.0 sec
  Load Avg:  3.224609375  3.75244140625  3.3671875
  WORD_SIZE: 64
  LLVM: libLLVM-21.1.8 (ORCJIT, apple-m2)
  GC: Built with stock GC
Threads: 8 default, 1 interactive, 8 GC (on 8 virtual cores)
Environment:
```

This PR:
```julia-repl
julia> versioninfo()
Julia Version 1.14.0-DEV.2178
Build Info:
  Official https://julialang.org release
  Commit 09e1b83 (2026-05-11 13:03 UTC)
  GC: Built with stock GC
Platform Info:
  OS: macOS (arm64-apple-darwin25.5.0)
  CPU: 12 × Apple M2 Max (apple-m2)
  WORD_SIZE: 64
  LLVM: libLLVM-21.1.8 (ORCJIT, apple-m2)
Threads: 8 default, 1 interactive, 8 GC (on 8 virtual cores)
Environment:
  JULIA_NUM_THREADS = auto

julia> versioninfo(verbose=true)
Julia Version 1.14.0-DEV.2178
Build Info:
  Official https://julialang.org release
  Commit 09e1b83 (2026-05-11 13:03 UTC)
  GC: Built with stock GC
  Sysimage: generic;apple-m1,clone_all (arm64-apple-darwin24.0.0)
Platform Info:
  OS: macOS (arm64-apple-darwin25.5.0)
  uname: Darwin 25.5.0 Darwin Kernel Version 25.5.0: Mon Apr 27 20:39:09 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T6020 arm64 arm
  CPU: Apple M2 Max (apple-m2):
                 speed         user         nice          sys         idle          irq
       #1-12  2400 MHz     131335 s          0 s      70450 s    1842109 s          0 s
  Memory: 32.0 GiB (345.53125 MiB free)
  Uptime: 469288.0 sec
  Load Avg:  3.2978515625  3.7919921875  3.37451171875
  WORD_SIZE: 64
  LLVM: libLLVM-21.1.8 (ORCJIT, apple-m2)
Threads: 8 default, 1 interactive, 8 GC (on 8 virtual cores)
Environment:
```
)

Fixes JuliaLang#61805

It seems that the problem is that our `foldr_impl` is passing a
non-reversed iterator to `_xfadjoint`, and then doing the actual final
reduction on a reversed iterator. This is normally fine, but it
interacts incorrectly with `FlatteningRF` which does a sub-reduction
inside each reducing step:
```julia
@inline function (op::FlatteningRF)(acc, x)
    op′, itr′ = _xfadjoint(op.rf, x)
    return _foldl_impl(op′, acc, itr′)
end
```
and this sub-reduction is not reversed when it gets hit during `foldr`,
resulting in the weird reduction order shown in the linked issue:
```julia
julia> foldr(Iterators.flatten([["a","b"],["c","d"]])) do l, r
           @info "" l r
           l * r
       end
┌ Info: 
│   l = "d"
└   r = "c"
┌ Info: 
│   l = "a"
└   r = "dc"
┌ Info: 
│   l = "b"
└   r = "adc"
"badc"
```

This PR changes around the location of the `_reverse_iter` so that the
sub-reductions are also reversed:

```julia

julia> foldr(Iterators.flatten([["a","b"],["c","d"]])) do l, r
           @info "" l r
           l * r
       end
┌ Info: 
│   l = "c"
└   r = "d"
┌ Info: 
│   l = "b"
└   r = "cd"
┌ Info: 
│   l = "a"
└   r = "bcd"
"abcd"
```

---------

Co-authored-by: Andy Dienes <51664769+adienes@users.noreply.github.com>
Co-authored-by: Matt Bauman <mbauman@juliahub.com>
… has no methods (JuliaLang#61700)

inspired by new-user confusion I've seen on discourse, example
https://discourse.julialang.org/t/scope-of-parametric-types-and-associated-error-messages/48928

```julia
julia> struct Foo{T}
           x::T
           Foo(x) = new{typeof(x)}(x)
       end

# master
julia> Foo{Int}(1)
ERROR: MethodError: no method matching Foo{Int64}(::Int64)
The type `Foo{Int64}` exists, but no method is defined for this combination of argument types when trying to construct it.
Stacktrace:
 [1] top-level scope
   @ REPL[1]:1

# PR
julia> Foo{Int}(1)
ERROR: MethodError: no method matching Foo{Int64}(::Int64)
The type `Foo{Int64}` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
  Foo(::Any)
   @ Main REPL[1]:3

Stacktrace:
 [1] top-level scope
   @ REPL[1]:1
```

this might be a little controversial since we're technically lying to
the user (it is a method on a different type than the one they passed)
but given that it would condition it only on `isempty(methods(f))` it
seems useful to me
…lity (JuliaLang#61426)

```
julia> using BenchmarkTools

julia> @Btime [1.0 2; 3 4.0];
  75.248 ns (8 allocations: 336 bytes) # master
  9.593 ns (2 allocations: 112 bytes) # PR

julia> @Btime [1 2 3im; 4 5 6im;;;]
  104.256 ns (10 allocations: 720 bytes) # master
  14.236 ns (2 allocations: 176 bytes) # PR
```

replaces JuliaLang#52028 with suggestion
given in
JuliaLang#52028 (comment)
When a struct is redefined, we first check whether the new definition is
in fact identical to the old definition, and if so we just reuse the old
type. Revise leans pretty heavily on this behavior, as signature
extraction relies (in corner cases) on struct definition. As identified
in JuliaLang#61789, the equivalence check failed for self-referential `struct`s

```julia
struct R
    x
    next::R
end
```

The fix is fairly straightforward: before comparing equivalence with
`equiv_field_types`, ensure that type-substitution occurs throughout the
struct definition.

Fixes JuliaLang#61789

This pull request was written with the assistance of generative AI
(Claude Code, Opus 4.7).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…parseArrays-ce5909e-master

🤖 Bump SparseArrays stdlib 361d557 → ce5909e
…inearAlgebra-74a7cfa-master

🤖 Bump LinearAlgebra stdlib 3a7a009 → 74a7cfa
…ang#61769)

JuliaLang#61764 changed the code gen for
MMTk write barrier. The generated code will need to reference a global
variable from `libmmtk_julia.so`. Sometimes, we see a linker error:
```console
lld: error: undefined symbol: MMTK_SIDE_LOG_BIT_BASE_ADDRESS━━━ 0/84 Press `?` for help, `c` to cancel.
>>> referenced by array.jl:1053
>>>               text#0.o:(julia___init___0) in archive /home/yilin/Code/julia_workspace/julia/usr/share/julia/compiled/v1.14/LLVMLibUnwind_jll/jl_9aCDMN
>>> referenced by array.jl:0
>>>               text#0.o:(julia___init___0) in archive /home/yilin/Code/julia_workspace/julia/usr/share/julia/compiled/v1.14/LLVMLibUnwind_jll/jl_9aCDMN
>>> referenced by LLVMLibUnwind_jll.jl:26 (/home/yilin/Code/julia_workspace/julia/usr/share/julia/stdlib/v1.14/LLVMLibUnwind_jll/src/LLVMLibUnwind_jll.jl:26)
>>>               text#0.o:(julia___init___0) in archive /home/yilin/Code/julia_workspace/julia/usr/share/julia/compiled/v1.14/LLVMLibUnwind_jll/jl_9aCDMN
>>> referenced 3 more times
lld: error: undefined symbol
```

This PR moves the variable from the binding to the Julia side.
I was getting some very silly results where `CodeInstance` nodes were
`UnitRange{Int64}` and `Core.DebugInfo` was `Compiler.AnalysisResults`,
etc.

Bug found by claude. Probably introduced in JuliaLang#52854, when this field went
from `size_t` to `uint8_t`.

~~todo: tests~~ would need to implement a parser for heapsnapshot
files....
Remove a spurious line. I wonder why CI didn't detect that.
…nstructors (JuliaLang#61442)

The constructors documentation used `new` with implicit and explicit
type parameters in examples without clearly stating the underlying rule.
This adds a subsection explaining that `new` inherits the type
parameters from the curly braces on the inner constructor name, when
explicit parameters are needed, and what happens when the constructor
name has fewer parameters than the type requires. The existing
explanation in the "Outer-only constructors" section is also updated to
reference this rule.

The rules described here were determined empirically, as the existing
documentation did not specify them. If any details are inaccurate,
corrections are welcome.

---------

Co-authored-by: James Wrigley <JamesWrigley@users.noreply.github.com>
Co-authored-by: Dilum Aluthge <5619885+DilumAluthge@users.noreply.github.com>
Co-authored-by: Christian Guinard <28689358+christiangnrd@users.noreply.github.com>
Co-authored-by: Cody Tapscott <topolarity@tapscott.me>
The processor_{x86,arm,fallback}.cpp files that included these headers
were deleted in JuliaLang#61292, which replaced the hand-maintained CPU/feature
tables with the [cpufeatures](https://github.com/JuliaLang/cpufeatures)
library. No remaining code in the tree references features_x86.h,
features_aarch32.h, or features_aarch64.h, nor the JL_FEATURE_DEF macro
they defined.

JuliaLang#61292 listed these headers under Files to delete.
topolarity and others added 25 commits June 17, 2026 12:49
…1851)

On my machine this can make a _hefty_ difference:

```julia
julia> using REPL

julia> @time REPL.REPLCompletions.completions("Core.Compiler.", 14);
  0.001048 seconds (15.47 k allocations: 945.453 KiB) # PR
  3.838192 seconds (3.01 M allocations: 179.134 MiB, 2.97% gc time, 99.94% compilation time) # master
```
```julia
julia> using REPL

julia> @time REPL.REPLCompletions.completions("(1+1).", 6);
  0.000683 seconds (3.08 k allocations: 147.062 KiB, 47.45% compilation time) # PR
  3.807135 seconds (2.99 M allocations: 178.334 MiB, 2.98% gc time, 99.98% compilation time) # master
```

Originally investigated by Claude 🤖 when I noticed that
`Core.Compiler.<tab>` is _much_ slower than `Core.<tab>`.

Co-authored-by: Shuhei Kadowaki <aviatesk@gmail.com>
This `jl_value_t **` array is treated as an untracked region by our
static analyzer, so it is possible to copy an unrooted value into it and
then extract it later without the analyzer stopping you.

This function has the contract that if `p != NULL`, then `iparams ==
jl_svec_data(p)` and iparams is rooting storage, but otherwise iparams
is not rooting. This causes us to lose track of e.g. newly-allocated
normalized types from `normalize_unionall` which are stored only in
`iparams`, eventually leading to GC corruption.

Bug introduced in 3c2c5ce.
(likely exposed somewhat by 2614585, which tweaked when type
normalization allocates)

Diagnosed with heavy assistance from Claude 🤖. Resolves
JuliaLang#62140.
…iac` (JuliaLang#62128)

JuliaC has been the canonical home for these files for some time. It's
time we clean up these old copies.

As a bonus, this expands our platform / test support for `--trim` to
cover all of our CI platforms. Likely requires
JuliaLang#62121 for FreeBSD and we'll want
to update `julia-buildkite` to remove / filter the unneeded separate
`trimming` job.

This will need a follow-up PR to
https://github.com/JuliaCI/julia-buildkite/ to enable the new `trim`
test.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Resolve `IRShow` debuginfo helpers through the active `Compiler` module
instead of `Base.Compiler`. This keeps `IRCode` printing working when
`Compiler` is loaded as a stdlib and owns the `DebugInfoStream` values
being printed.

Following up JuliaLang#61979.

Co-authored-by: GPT-5.5 <noreply@openai.com>
Remove unused locals and mark intentionally unused callback arguments in
IRShow printing helpers.
Driven by changes I made with JuliaLang#62155. JETLS found them.

Co-authored-by: GPT-5.5 <noreply@openai.com>
This should improve both invalidations and `--trim` support.
…liaLang#62153)

Byte-precise `DebugInfo` (JuliaLang#61991) constructs `Core.DebugInfo` with a
`String` linetable, but only recent Julia's constructor accepts a
`String` there (older versions, e.g. v1.12, accept only
`Union{Nothing,DebugInfo}`). As a result `add_debuginfo!` raised a
`MethodError` on those versions, which made lowering itself impossible.

Detect whether a `String` linetable is supported and, when it is not,
degrade byte spans to line numbers and emit line-based `DebugInfo` (the
same shape as the `LineNumberNode` path), so lowering still produces a
valid `CodeInfo`. The byte-precise path on recent Julia is unaffected.

While JuliaLowering targets nightly and full compatibility is a
non-goal, it is desirable to retain minimal, lowering-only v1.12
compatibility: JETLS still targets v1.12 currently and relies on JL's
lowering (then runs inference on the resulting `CodeInfo`), without
going through `JuliaLowering.eval`. Remaining incompatibilities are
confined to the `eval` path (`Core.declare_const`,
`jl_begin_new_module`/ `jl_end_new_module`) and are intentionally left
unaddressed here.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
More quiet tests + 32-bit support
Split argument datatype lookup from common typename lookup and compute
reflection field counts structurally, so union types with mismatched
field counts are rejected while same-layout unions remain supported.

Fixes JuliaLang#62098

Co-authored-by: Keno Fischer <Keno@users.noreply.github.com>
Co-authored-by: OpenAI Codex <codex@openai.com>
Make several small passes.jl cleanups that clarify existing invariants
and help a code analyzer:

- Make `perform_lifting!` assert non-empty lifted leaves, return
`Union{LiftedValue, Nothing}` instead of `Any`, and dispatch final
statement-value resolution on `LiftedLeaves` vs `LiftedDefs`. This makes
explicit that definition lifting resolves through lifted phi-like values
and must not return raw boolean leaf values.
- Remove an unreachable `compact.result` reference from CFG
simplification
- Initialize conditionally assigned locals, mark intentionally unused
bindings with `_` names, and remove stale unused locals/comments.
Also fix-up `show` implementation for `WindowsRawSocket`, for `--trim`
compat in both cases.

This was improved in JuliaLang#62158 but not completely fixed.
This should be enough to enable the new, multi-platform `trim` tests.
…59663)

This commit includes several, interlocked changes:
1. It removes an undocumented SubString constructor which triage has
agreed can be considered internal and should be removed
* EDIT: After triage comments, the method is now deprecated, but not
removed.
2. It adds a new public, but unexported function `raw_substring`, which
creates a substring without checking for valid string indices. This new
function is used in place of the old removed method, internally in
Julia.
* EDIT: After triage comments, this was renamed from `unsafe_substring`
to `raw_substring`
3. It adds a new public, but unexported function `unannotate`, which
gives the underlying non-annotated string of an `AnnotatedString` or
`SubString{AnnotatedString}`. The reason for this change is that
currently, code outside Base (namely, in StyledStrings and
JuliaSyntaxHighlighting) relies on this operation. Its current
implementation reaches into both `AnnotatedString` and `SubString`
internals. Instead, I provide the function here in Base, and let the two
stdlibs use it. Also in general, I think it's a completely reasonable
basic function to have for annotated strings.

For reviewers: We can make these functions exported (I don't have a
strong opinion on that).

For this PR to land, it also requires compatible PRs in StyledStrings.jl
and JuliaSyntaxHighlighting.jl, which uses the removed constructor.

This depends on two PRs to be merged first:
1. JuliaLang/JuliaSyntaxHighlighting.jl#13
2. JuliaLang/StyledStrings.jl#125

Supersedes JuliaLang#59606
Supersedes JuliaLang#55458
Closes JuliaLang#59610
Closes JuliaLang#55247
Switch Windows executable and DLL link recipes to invoke $(LD) directly
and stage the MinGW CRT startup objects and import libraries from
CompilerSupportLibraries. This keeps Julia's Windows build on the
bundled runtime libraries instead of mixing in host MinGW CRT files.
The path.exists? femtolisp builtin has no callers anywhere in Julia: the
flisp front-end scripts and the bundled boot image never reference it,
and Julia stats the filesystem through libuv rather than flisp. Drop the
dead fl_path_exists function, its builtin-table entry, and the
now-unneeded <sys/types.h>/<sys/stat.h> includes.

This is motivated by avoiding POSIX `stat` in the codebase (everything
else already uses libuv), which on windows is provided by mingw shims
that have runtime-library-version-dependent ABI.

Co-authored-by: Keno Fischer <Keno@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…62179)

The Windows strptime matches %Z against the process timezone names via
the _tzname global. Newer mingw-w64 redirects the _tzname macro to a
different CRT symbol (__tzname / __imp___tzname); when the host
compiler's headers are newer than the bundled CompilerSupportLibraries
CRT import library (as after JuliaLang#62152) that symbol is not exported by the
bundled library and the link fails:

  src/support/strptime.c:625: undefined reference to `__imp___tzname'

For non-UCRT targets, #undef the macro and re-declare the classic
dllimport char *_tzname[2]. That references the __imp__tzname symbol,
which the bundled msvcrt import library still provides, so it links
regardless of the host header vintage and across both 32- and 64-bit
mingw-w64.

Co-authored-by: Keno Fischer <Keno@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Opaque closures created inside another closure could leave captured
locals from the enclosing closure as raw `BindingId` nodes in the
`new_opaque_closure` arguments. Linearization then failed because those
bindings were not native slots of the current lambda:
```julia-repl
julia> JuliaLowering.include_string(Main, """
       let y = [1]
           outer = Base.Experimental.@opaque () -> begin
               inner = Base.Experimental.@opaque n -> n in y
               inner(1)
           end
           outer()
       end
       """)
ERROR: LoweringError:
let y = [1]
    outer = Base.Experimental.@opaque () -> begin
        inner = Base.Experimental.@opaque n -> n in y

Detailed provenance:
  #₄/y @#= /Users/aviatesk/julia/julia/JuliaLowering/src/bindings.jl:123 =#
  └─ y
     └─ y
        └─ @ string:1

Stacktrace:
  [1] _renumber(ctx::JuliaLowering.LinearIRContext{…}, ssa_rewrites::Dict{…}, slot_rewrites::Dict{…}, label_table::Dict{…}, ex::JuliaSyntax.SyntaxTree{…})
    @ JuliaLowering ~/julia/julia/JuliaLowering/src/linear_ir.jl:1097
  [2] (::JuliaLowering.var"#_renumber##2#_renumber##3"{JuliaLowering.LinearIRContext{…}, Dict{…}, Dict{…}, Dict{…}})(e::JuliaSyntax.SyntaxTree{Dict{…}})
    @ JuliaLowering ~/julia/julia/JuliaLowering/src/linear_ir.jl:1120 [inlined]
  [3] mapchildren(f::JuliaLowering.var"#_renumber##2#_renumber##3"{…}, ctx::JuliaLowering.LinearIRContext{…}, ex::JuliaSyntax.SyntaxTree{…})
    @ JuliaSyntax ~/julia/julia/JuliaSyntax/src/porcelain/syntax_graph.jl:722
...
```

Convert opaque closure type bounds in the current closure-conversion
context and initialize capture arguments from the enclosing closure's
capture storage when needed. This preserves boxed storage for mutable
captures while allowing nested opaque closures to capture values from
their enclosing closure.

---------

Co-authored-by: GPT-5.5 <noreply@openai.com>
Co-authored-by: Em Chu <61633163+mlechu@users.noreply.github.com>
…a jobserver (JuliaLang#61958)

Fixes JuliaLang#58591

Parallel package precompilation previously let every worker
independently claim half the machine's cores for native-image codegen,
oversubscribing the CPU when many workers hit the imaging phase at once
while leaving cores idle during the long tail when only one big job
remained.

## Design

The orchestrating precompile process creates a named-semaphore token
pool sized to `Sys.EFFECTIVE_CPU_THREADS + 1` and shares it with worker
subprocesses via the `JULIA_IMAGE_JOBSERVER` environment variable. The
tokens form a single CPU budget that covers both the worker processes
and their imaging threads, rather than two budgets that multiply:

- The orchestrator holds one baseline token per CPU-active worker. Since
a worker's main thread sleeps while its imaging threads run, that
baseline doubles as the imaging phase's first codegen thread instead of
being counted twice.
- Each worker's imaging thread pool is elastic: shards are compiled from
a work queue, and the pool starts with the baseline thread plus whatever
tokens are free right then, keeps polling for tokens released by sibling
workers while it still has unclaimed shards, and returns each token to
the pool as soon as the thread holding it runs out of work. A worker
that entered the imaging phase on a busy machine expands into cores as
neighbors finish, and a draining worker feeds busy ones instead of
idling the rest of its allotment until the slowest shard completes.
- Setting `JULIA_IMAGE_THREADS` still pins a fixed per-worker thread
count and takes precedence over the jobserver.

Token acquisition can never hang precompilation: waiting is bounded by a
timeout and a cancellation check and degrades to bounded
oversubscription (the pre-jobserver behavior) if the pool is
unavailable, e.g. if tokens were leaked by a killed worker. Only one
jobserver can be active per process; a concurrent in-process precompile
session falls back to uncoordinated behavior rather than corrupting the
live pool.

## Results

Precompiling an environment with GLMakie and Plots from a cleared cache
went from 193s to 182s (~6% faster) on a 6-core M2 MacBook, with user
CPU time essentially unchanged — the speedup comes from better balance,
not extra work. Previously we both undersubscribed (idle cores during
the long tail) and, more egregiously, oversubscribed (when many packages
hit the imaging phase at the same time).


master d2936ef
```
julia> Base.Precompilation.precompilepkgs(timing=true, verbose=true)
Precompiling project...
   total │ include   (deps)   (comp) │ methods  img-gen     cache  ~pk-rss
...
  28.0 s │  18.29s    4.29s   12.54s │   36014    9.70s    44.0MB   1689MB  ✓ Plots
...
  88.3 s │  56.21s   14.33s   37.16s │  104623   32.14s   146.6MB   3302MB  ✓ Makie
  44.1 s │  26.89s    8.10s   16.95s │   36975   17.19s    49.8MB   2731MB  ✓ GLMakie
  316 dependencies successfully precompiled in 193 seconds. 41 already precompiled.
```

PR
```
   total │ include   (deps)   (comp) │ methods  img-gen     cache  ~pk-rss
...
  24.9 s │  17.30s    3.52s   12.38s │   36015    7.60s    44.0MB   1772MB  ✓ Plots
...
  80.0 s │  56.00s   14.22s   37.10s │  104626   23.95s   146.6MB   3254MB  ✓ Makie
  39.3 s │  26.78s    8.33s   16.74s │   36979   12.48s    49.8MB   2771MB  ✓ GLMakie
  316 dependencies successfully precompiled in 182 seconds. 41 already precompiled.
```


## Relation to JuliaLang#58591

The issue settled on "a jobserver like make". This implementation
targets the case thaultiple large packages hitting image generation at
once (e.g. GLMakie + DiffEq) — while leaving the already-saturated bulk
phase untouched. Because the imaging pool is elastic, a worker is no
longer stuck with whatever was free at spawn or phase-entry time: it
grows back up after a
neighbor finishes. Named semaphores give Windows support for free (the
same primitive mposable with the LLVM jobserver
(llvm/llvm-project#145131), which isn't in our bundled LLVM 21.

It is not yet wire-compatible with the GNU make jobserver protocol;
making it speak that protocol is a natural follow-up. This also
supersedes JuliaLang#60794, delivering the same all-cores tail benefit but
bounded so it can't reintroduce oversubscription.

Fixes JuliaLang#58591
Closes JuliaLang#60794

Co-authored-by: Claude <claude@users.noreply.github.com>
…nds (JuliaLang#62109)

See discussion in JuliaLang#62099. This moves towards the proposed rule and in
particular addressed the motivating example, but is not yet a principled
implementation of that rule.

---------

Co-authored-by: Keno Fischer <Keno@users.noreply.github.com>
Co-authored-by: OpenAI Codex <codex@openai.com>
…Lang#62187)

See the message length in
JuliaLang#62186

Limit the number of loaded package names shown in the precompilation
summary warning and append an "N more" suffix when the list is longer
than the cap. This keeps the message readable when many packages are
already loaded.

Co-authored-by: GitHub Copilot <copilot@users.noreply.github.com>
@Keno
Keno force-pushed the kf/subtype-forall-ub-split branch from e6ae6ae to c9df31c Compare June 22, 2026 22:21
A left-side (universally quantified) `where` variable with a trivial
lower bound, a union upper bound, and only covariant occurrences in the
body ranges over each arm of its upper bound independently, i.e. the
UnionAll distributes over the arms:

    (Tuple{T,T} where T<:Union{A,B}) ==
        Union{Tuple{T,T} where T<:A, Tuple{T,T} where T<:B}

(diagonality, if any, is preserved: each value of such a variable is
concrete and therefore lies entirely within a single arm).

Exploit this in `subtype_unionall` on the ∀ path by splitting the upper
bound before descending into the body: descend the ub's Union tree with
`pick_union_decision(e, 0)`, so each Union node registers as an ordinary
left-union decision and the enclosing ∀∃ loop enumerates all arms. This
makes judgments like

    (Tuple{T,T} where T<:Union{Float64,Int64}) <:
        Union{Tuple{Float64,Float64},Tuple{Int64,Int64}}

true, and fixes a previously `@test_broken` type equality. The gate is
checked by a new static walker: covariant here means reachable purely
through Tuple parameters, Union components, and Vararg element types;
any occurrence under a non-Tuple datatype parameter, in a Vararg length,
or inside an inner UnionAll disables the split. The ∃ side, lower
bounds, and intersection are unaffected.

`jl_obvious_subtype` was verified to return "unknown" (never
definitely-false) on the newly-true judgments, so it cannot mask the new
result in release builds nor trip the consistency assert in debug
builds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Keno
Keno force-pushed the kf/subtype-forall-ub-split branch from c9df31c to 3bb0611 Compare June 22, 2026 22:38
Keno pushed a commit that referenced this pull request Aug 13, 2026
…2491)

`CodeInstance.def` is Any so everything downstream of `mi` also got
inferred to Any. Should fix these invalidations seen when loading
CUDA.jl on 1.13:

```julia
inserting any(f::Function, a::StaticArraysCore.StaticArray; dims) @ StaticArrays ~/.julia/packages/StaticArrays/0cEwi/src/mapreduce.jl:308 invalidated:
   backedges: 1: superseding any(f::Function, a::AbstractArray; dims) @ Base reducedim.jl:993 with MethodInstance for any(::Compiler.var"#compileable_specialization##0#compileable_specialization##1", ::AbstractArray) (584 children)
```

That being said I couldn't actually test it because GPUCompiler
currently doesn't work on nightly, so it was confirmed by
`code_warntype` inspection.

Importantly, `mi` is now inferred as a `MethodInstance` so
`mi.sparam_vals` infers to `SimpleVector`.

Debugged with Claude Code
Keno pushed a commit that referenced this pull request Sep 10, 2026
Discovered (with help of Claude) that in 1.13, the first version where
my prior changes to stacktrace cycle printing are landing, has a flaw -
more than two frames in the cycle, and the brackets are printed on the
wrong frames, even being left unclosed:

```
Stacktrace:
     [1] error()
       @ Base .\error.jl:45
     [2] r(n::Int64)
       @ Main .\REPL[9]:1
     [3] (::var"#r##0#r##1"{Int64})(::Int64)
       @ Main .\REPL[9]:1
     [4] iterate
       @ .\generator.jl:48 [inlined]
     [5] _collect(c::UnitRange{Int64}, itr::Base.Generator{UnitRange{Int64}, var"#r##0#r##1"{Int64}}, ::Base.EltypeUnknown, isz::Base.HasShape{1})
       @ Base .\array.jl:848
 ┌   [6] collect_similar(cont::UnitRange{Int64}, itr::Base.Generator{UnitRange{Int64}, var"#r##0#r##1"{Int64}})
 │     @ Base .\array.jl:763
 ├   [7] map(f::Function, A::UnitRange{Int64})
 │     @ Base .\abstractarray.jl:3396
 ├   [8] r(n::Int64)
 │     @ Main .\REPL[9]:1
 ├   [9] top-level scope
 │     @ REPL[10]:1
```

This PR fixes this:

```
Stacktrace:
    [1] error()
 ┌  [2] r(n::Int64)
 ├  [3] (::var"#r_map##0#r_map##1"{Int64})(::Int64)
 ├  [4] iterate(::Base.Generator{...})
 ├  [5] _collect(c::UnitRange{Int64}, ...)
 ├  [6] collect_similar(cont::UnitRange{Int64}, ...)
 ├  [7] map(f::Function, A::UnitRange{Int64})
 ╰───── repeated 10 times
   [62] r(n::Int64)
```

Side-effect is that the presentation order of the nested example flips
(b/c all else equal, inner cycle now comes first instead of last), which
is why that test changed.

Please mark for 1.13 backport.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.