Skip to content

Backports for 1.13.0-rc1#61318

Merged
KristofferC merged 41 commits into
release-1.13from
backports-release-1.13
Apr 15, 2026
Merged

Backports for 1.13.0-rc1#61318
KristofferC merged 41 commits into
release-1.13from
backports-release-1.13

Conversation

@KristofferC

@KristofferC KristofferC commented Mar 13, 2026

Copy link
Copy Markdown
Member

Backported PRs:

Need manual backport:

Non-merged PRs with backport label:

These `@assert`s are not directly problematic for `--trim` (thanks to an
overload / monkey patch it does), but they can cause false positives for
dynamic dispatches in JET and similar tools so it's probably best to
replace them with the 2-arg variant (which just needs to print a String,
rather than an `Expr` containing arbitrary Julia objects)

(cherry picked from commit d64c0da)
@KristofferC KristofferC added the release Release management and versioning. label Mar 13, 2026
JamesWrigley and others added 11 commits March 14, 2026 00:18
…tion (#61264)

When a const-prop frame encounters a cycle and gets poisoned,
`finishinfer!` marks the result as tombstoned and sets `cache_mode =
CACHE_MODE_NULL`, which prevents `promotecache!` from pushing the result
to the local inference cache.
Meanwhile, `constprop_cache_lookup` (added in #57545) skips tombstoned
entries entirely. This combination means the same const-prop is
re-attempted on every cycle iteration, causing inference to never
terminate when `aggressive_constant_propagation = true`.

Fix this by:
- In `const_prop_call`, explicitly pushing tombstoned results to the
inference cache after a successful but limited `typeinf`, and returning
`nothing` to fall back to the regular inference result.
- In `constprop_cache_lookup`, no longer skipping tombstoned entries so
they can be found on subsequent lookups, preventing re-attempts.
- In `const_prop_call` cache-hit path, checking `inf_result.tombstone`
and returning `nothing` (same as the existing cycle-hit handling).
- Removing the now-unnecessary tombstone check from
`OverlayCodeCache.get`, where the subsequent `overridden_by_const` and
`isdefined` checks already filter out such entries.

Note that #57545 added `tombstone && continue` to `cache_lookup` to
prevent `LimitedAccuracy` results from propagating to callers via
`const_prop_result`. This change removes that skip but achieves the same
protection by explicitly checking `inf_result.tombstone` in
`const_prop_call` and returning `nothing` instead of using the result.

Fixes #61257

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Rewriting the bundled zstd executables with install_name_tool
invalidates their existing signatures on macOS. Re-sign them ad
hoc after updating the rpath so the CLI remains launchable for Pkg
archive decompression.

Add a regression test that runs the bundled zstd executable
instead of only checking that libzstd can be loaded.

Fixes #61354 (but see discussion there as to whether it really does?)

Co-authored-by: Codex <codex@openai.com>

Co-authored-by: Codex <codex@openai.com>
(cherry picked from commit 3de619d)
@KristofferC
KristofferC requested a review from giordano as a code owner March 24, 2026 13:08
PatrickHaecker and others added 15 commits March 26, 2026 04:35
…tial hang (#61316)

Investigation of aviatesk/JETLS.jl#509 revealed that the root cause is a
hang in the subtyping algorithm. A minimal reproducer has been added to
`test/subtype.jl`, which does not terminate in a reasonable time on the
current master branch.

The fix was developed using Claude and Codex, with iterative
cross-review between the two to arrive at what I believe is the most
sound approach. However, I am not very familiar with the subtype
algorithm implementation, so there may be implementation issues I have
not caught. Note that the bulk of this patch was written by AI.

---

`subtype_ccheck` calls `local_forall_exists_subtype`, which leaks
right-side Union choices (via `pick_union_decision`) into the shared
`Runions` statestack. When many
Union-bounded type parameters share a common variable (e.g. 6 parameters
all bounded by `Union{Ref{F},Val{F}}`), each bounds check adds decisions
that `exists_subtype` must iterate over combinatorially, causing O(2^N)
iterations.

Make `subtype_ccheck` save and restore both the `Runions` state and
variable environment (`save_env`/`restore_env`).

When `pick_union_decision` added new entries during the check (detected
via `Runions.used` growth) and the check succeeded, a merge loop
(`ccheck_merge_env`) enumerates all successful right-side ∃ branches and
merges their variable constraints via `simple_meet` (lb) / `simple_join`
(ub). This yields the widest constraints valid across any successful
branch, instead of discarding all constraints. A separate
`ccheck_restore_metadata` step then overrides the metadata fields
(`occurs_inv`, `occurs_cov`,
`max_offset`, `innervars`) with the original values, since `merge_env`'s
metadata merging (max for occurs, min for max_offset) tightens
constraints — the opposite of what `subtype_ccheck` needs.

The merge loop uses `next_union_state_from` to iterate only the ccheck's
own ∃ decisions (starting from
`oldRunions.used`), avoiding interference with outer Union iteration.
Each branch runs a full ∀∃ check via
`local_forall_exists_subtype`. The `ccheck_merging` flag prevents
re-entrant merge loops from nested `subtype_ccheck` calls; the
`!e->intersection` guard avoids conflicts with `intersect_all`'s own
`merge_env` handling.

When no Union splitting occurred, the variable constraints are
deterministic and safe to keep; restoring them unconditionally
introduces false negatives that break the `obvious_subtype` invariant.
The `envout` array is preserved across the restore because inner
`subtype_unionall` calls may have already written inferred
type-parameter values that must survive.

Known limitation: detection via `Runions.used` growth is imperfect —
`local_forall_exists_subtype`'s exists-free path (path 1) internally
pushes/pops its own `Runions`, so Union choices made there do not
increase the outer `Runions.used`. Currently harmless because
exists-free inputs have no exists-variable constraints to corrupt, but
the detection mechanism has a structural gap.

Fixes aviatesk/JETLS.jl#509.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…61130)

fixes #60715

`tmerge_types_slow` would check `issimpleenoughtype(u)` after the
`PartialStruct`s were stripped from the types, but that path wasn't
getting hit as it would early return via `tmerge_fast_path`

Claude assisted the analysis but the proposed fix is mine
Remove duplicated entry in NEWS.md for Julia 1.13
…l_∀_∃_subtype` (#61503)

Following up on #61316, this commit moves the `env_unchange` check and
the reset of `Runion.more` to `local_forall_exists_subtype`, ensuring
that similar optimizations are applied more broadly, and preventing
unnecessary `save_env` cost.
Additionally, `env_unchanged` now also detects diagonality changes in
variables, which would fix
```julia
Tuple{NTuple{2,Int}, Int8} <: Tuple{<:Union{NTuple{2,T},Tuple{S,T}}, <:T} where {S,T>:Int}
```

(cherry picked from commit d0828a8)
Hopefully enough to fix JuliaC on `aarch64-linux`
(https://buildkite.com/julialang/julia-master/builds/56266#019d6367-fc8d-4806-a1f1-00ec67370c77/L1315-L1319):
```
Error #2: unresolved call from statement string(
  getproperty(
    libquadmath::Any,
    :path::Symbol
  )::Any
)::Any
 
Stacktrace:
[1] __init__()
@ CompilerSupportLibraries_jll ./CompilerSupportLibraries_jll/src/CompilerSupportLibraries_jll.jl:158
```

(cherry picked from commit ea7a52a)
Instances of foreign types are opaque to Julia and we can't traverse
them. Teach `get_nth_pointer` about this. This in turn prevents
`summarysize` from crashing when invoked on a foreign object.

Fixes oscar-system/GAP.jl#1366

(cherry picked from commit a992d84)
Co-authored-by: Andy Dienes <51664769+adienes@users.noreply.github.com>
(cherry picked from commit 211af65)
Add some targeted overloads for now to allow `--trim` to support these
essential functions.

(cherry picked from commit e9054e3)
This regressed in #61535 but
wasn't being tested until #61551,
which caused CI to fail only after merging both (sorry folks!)

JuliaC.jl is already fixed up by
JuliaLang/JuliaC.jl#131. This PR resolves the
Julia-side scripts. Also check in the `Manifest.toml` for JuliaC to
prevent breaking CI in the future, although it wouldn't have prevented
this regression.

Supersedes #61558.

(cherry picked from commit b9ba052)
Currently the subtyping algorithm uses `right` to mean `existential`,
rather than having structurally come from the right. This doesn't matter
as much, because right now, if the LHS and RHS and egal typevars, we
rename them. However, I'm working on a unionall representation change
that will make it important to distinguish which side of the expression
a var came from. As such, rename `right` to `existential`, freeing up
`right` to refer to having structurally come from the RHS.

(cherry picked from commit 85c0580)
Some packages add extensions to stdlibs, so we should not have
prohibited precompiling those. This broke Compat, which adds an
extension to LinearAlgebra.
(cherry picked from commit 12adf71)
… messages"

This reverts commit 81dbb72.
(cherry picked from commit cd0d942)
@KristofferC
KristofferC force-pushed the backports-release-1.13 branch from 6af6062 to dfd93da Compare April 14, 2026 11:46
IanButterworth and others added 14 commits April 14, 2026 13:51
…1272)

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit d7cc1c5)
Should prevent these invalidations seen when loading CurveFit.jl on
1.13:
```julia
inserting merge!(m::DataStructures.SortedDict{K, D, Ord}, others::AbstractDict{K, D}...) where {K, D, Ord<:Ordering} @ DataStructures ~/.julia/packages/DataStructures/qUuAY/src/sorted_dict.jl:473 invalidated:
   mt_backedges: 1: signature Tuple{typeof(merge!), Any, AbstractDict} triggered MethodInstance for Base.grow_to!(::AbstractDict{K, V}, ::Base.Generator{_A, Base.Compiler.IRShow.var"#30#31"} where _A, ::Any) where {K, V} (0 children)
                 2: signature Tuple{typeof(merge!), Any, AbstractDict} triggered MethodInstance for Base.grow_to!(::AbstractDict{K, V}, ::Base.Generator, ::Any) where {K, V} (0 children)
                 3: signature Tuple{typeof(merge!), Any, AbstractDict} triggered MethodInstance for Base.grow_to!(::AbstractDict{K, V}, ::Base.Generator, ::Any) where {K, V} (0 children)
                 4: signature Tuple{typeof(merge!), Any, AbstractDict} triggered MethodInstance for Base.grow_to!(::AbstractDict{K, V}, ::Base.Generator{I, Base.var"#Generator##0#Generator##1"{NonlinearSolveBase.Utils.var"#norm_op##2#norm_op##3"{typeof(+)}}} where I<:(Base.Iterators.Zip{Is} where Is<:Tuple{Base.AbstractBroadcasted, Base.AbstractBroadcasted}), ::Tuple{Tuple{Any, Any}, Tuple{Any, Any}}) where {K, V} (0 children)
                 5: signature Tuple{typeof(merge!), Any, AbstractDict} triggered MethodInstance for Base.grow_to!(::AbstractDict{K, V}, ::Base.Generator{_A, F} where {_A, F<:DifferentiationInterface.var"#_prepare_jacobian_aux##0#_prepare_jacobian_aux##1"}, ::Any) where {K, V} (0 children)
                 6: signature Tuple{typeof(merge!), Any, AbstractDict} triggered MethodInstance for Base.grow_to!(::AbstractDict{K, V}, ::Base.Generator{_A, F} where {_A, F<:DifferentiationInterface.var"#_prepare_jacobian_aux##12#_prepare_jacobian_aux##13"}, ::Any) where {K, V} (0 children)
                 7: signature Tuple{typeof(merge!), Any, AbstractDict} triggered MethodInstance for Base.grow_to!(::AbstractDict{K, V}, ::Base.Generator{I, Base.var"#Generator##0#Generator##1"{NonlinearSolveBase.Utils.var"#norm_op##2#norm_op##3"{typeof(+)}}} where I<:(Base.Iterators.Zip{Is} where Is<:Tuple{Base.AbstractBroadcasted, Vector{Float64}}), ::Tuple{Tuple{Any, Any}, Int64}) where {K, V} (0 children)
   backedges: 1: superseding merge!(d::AbstractDict, others::AbstractDict...) @ Base abstractdict.jl:224 with MethodInstance for merge!(::AbstractDict, ::AbstractDict) (1126 children)

inserting merge(d::OrderedCollections.OrderedDict, others::AbstractDict...) @ OrderedCollections ~/.julia/dev/OrderedCollections/src/ordered_dict.jl:488 invalidated:
   mt_backedges: <38 invalidations I deleted because they have enormous types>
   backedges: 1: superseding merge(d::AbstractDict, others::AbstractDict...) @ Base abstractdict.jl:359 with MethodInstance for merge(::AbstractDict, ::Base.Pairs{Symbol, Union{}, Nothing, @NamedTuple{}}) (21 children)
              2: superseding merge(d::AbstractDict, others::AbstractDict...) @ Base abstractdict.jl:359 with MethodInstance for merge(::AbstractDict, ::Base.Pairs{Symbol, _A, Nothing, A} where {_A, A<:NamedTuple}) (2489 children)
```

Partial alternative to #61329. I added `mergewith()` and `mergewith!()`
to be on the safe side.

(cherry picked from commit c3b914f)
…61502)

`@nospecializeinfer` widens the `MethodInstance` signature so that
inference does not specialize on `@nospecialize`'d arguments, but const
prop was not respecting this and `matching_cache_argtypes` would expand
a `Vararg` in the `MI`'s `cache_argtypes` to match the call-site arity,
producing `argtypes` of different lengths for different const-prop call
sites sharing the same `MI`. This caused an assertion failure in
`constprop_cache_lookup` when a second call with a different number of
varargs tried to look up a cached result.

Fix this by widening the `@nospecialize`'d arguments back to the
`cache_argtypes`, using the same `nospecialize` bitmask convention as
`jl_compilation_sig` in `src/gf.c`.
This ensures constprop argtypes always have the same shape as the `MI`'s
baseline argtypes, and avoids unnecessary specialization that
contradicts the intent of `@nospecializeinfer`.

Fixes aviatesk/JETLS.jl#618.

(cherry picked from commit 525c655)
`REPL.hascolor` and `Base.Terminals.hascolor` are two different generic
functions. The method for `::TextTerminal` is a method of the latter.

Detected by JET.

(cherry picked from commit 2aa3d62)
This change resolves some notable gaps in Preferences' tracking of
compile-time dependencies:

1. Preferences tracks only preferences keyed on the active
`__toplevel__` module, despite allowing you to query preferences keyed
on any module / package.
2. Preference changes are checked for by comparing a hash, without a
full value comparison.

This hash comparison was probably something of a premature optimization,
since it is done very rarely and it is impossible to compute the hash
before asking the pkgimage what preferences it depended upon anyway. It
has also led to a few serious bugs.

Resolve this by storing a serialized (TOML) representation of the
compile-time dependencies used in any Module and de-serializing this
upon loading instead.

Fixes JuliaPackaging/Preferences.jl#86. Fixes
#59344. Fixes
#59257.

---------

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit a25da50)
…ocas (#61229)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 8ce9654)
…1293)

The existing warning was brittle and wasn't shown when the package with
a different version loaded wasn't actually precompiled by this session.
i.e. `[21216c6a] + Preferences v1.5.2 [loaded: v1.5.1]` but v1.5.2 was
actually already precompiled on the system.

Developed with Claude.

---

Name the loaded packages and count their affected dependents in the
warning shown after Pkg.precompile. This helps users understand why a
subsequent `using` triggers re-precompilation: a transitive dependency
(e.g. Preferences) was loaded at one version but the manifest resolved a
different version, so caches built by Pkg.precompile
(ignore_loaded=true) are rejected at load time (ignore_loaded=false).

The message now shows which packages are loaded at a different version
and how many dependents will be affected, making the restart suggestion
more actionable.

```
(@v1.14) pkg> st
Status `~/.julia/environments/v1.14/Project.toml`
  [336ed68f] CSV v0.10.16
  [0ca39b1e] Chairmarks v1.3.1
  [a93c6f00] DataFrames v1.8.1
  [7876af07] Example v0.5.5
⌃ [21216c6a] Preferences v1.5.1
  [295af30f] Revise v3.13.2
Info Packages marked with ⌃ have new versions available and may be upgradable.

julia> using Preferences

(@v1.14) pkg> activate --temp
  Activating new project at `/var/folders/1z/jf841bdj73bdj3vk7kc7f_3w0000gn/T/jl_PgrklO`

(jl_PgrklO) pkg> add CSV
   Resolving package versions...
    Updating `/private/var/folders/1z/jf841bdj73bdj3vk7kc7f_3w0000gn/T/jl_PgrklO/Project.toml`
  [336ed68f] + CSV v0.10.16
    Updating `/private/var/folders/1z/jf841bdj73bdj3vk7kc7f_3w0000gn/T/jl_PgrklO/Manifest.toml`
  [336ed68f] + CSV v0.10.16
  [944b1d66] + CodecZlib v0.7.8
  [34da2185] + Compat v4.18.1
  [9a962f9c] + DataAPI v1.16.0
  [e2d170a0] + DataValueInterfaces v1.0.0
  [48062228] + FilePathsBase v0.9.24
  [842dd82b] + InlineStrings v1.4.5
  [82899510] + IteratorInterfaceExtensions v1.0.0
  [bac558e1] + OrderedCollections v1.8.1
  [69de0a69] + Parsers v2.8.3
  [2dfb63ee] + PooledArrays v1.4.3
  [aea7be01] + PrecompileTools v1.3.3
  [21216c6a] + Preferences v1.5.2 [loaded: v1.5.1]
  [91c51154] + SentinelArrays v1.4.9
  [3783bdb8] + TableTraits v1.0.1
  [bd369af6] + Tables v1.12.1
  [3bb67fe8] + TranscodingStreams v0.11.3
  [ea10d353] + WeakRefStrings v1.4.2
  [76eceee3] + WorkerUtilities v1.6.1
  [ade2ca70] + Dates v1.11.0
  [9fa8497b] + Future v1.11.0
  [8f399da3] + Libdl v1.11.0
  [a63ad114] + Mmap v1.11.0
  [de0858da] + Printf v1.11.0
  [9a3f8284] + Random v1.11.0
  [ea8e919c] + SHA v1.0.0
  [fa267f1f] + TOML v1.0.3
  [cf7118a7] + UUIDs v1.11.0
  [4ec0a83e] + Unicode v1.11.0
  [83775a58] + Zlib_jll v1.3.2+0
Precompiling CSV finished.
  1 dependency successfully precompiled in 9 seconds. 29 already precompiled.
  1 dependency precompiled but a different version is currently loaded (Preferences). Restart julia to access the new version. Otherwise, 5 dependents of this package may trigger further precompilation to work with the unexpected version.

```

---------

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 2998caa)
For reference the big win here is that this allows for loop unswitch to
trigger since it doesn't really understand our `and` sequence

- Replace AND-combined bounds check conditions with separate branches
annotated with branch weight metadata (2000:1) to mark the error path as
cold in LLVM codegen (`emit_bounds_check`, `emit_memoryref_direct`,
`emit_memoryref`)
- Change `checkbounds_indices` to use `&&` instead of `&` so each
dimension check becomes its own branch rather than being combined into a
single AND chain
- Change `throw_boundserror` to accept varargs for multi-index cases, so
the index tuple is only constructed inside the `@noinline` error path
rather than on the hot path

For a 2D array access `A[i, j]`, the net effect is:
- **Before**: one AND-combined branch + `alloca` for index tuple in the
entry block
- **After**: two separate cold-annotated branches per dimension, zero
tuple allocation on the hot path

Before:
```llvm
  %"new::Tuple" = alloca [2 x i64], align 8
  store i64 %"i::Int64", ptr %"new::Tuple", align 8
  store i64 %"j::Int64", ptr %0, align 8
  ...
  %.not = icmp ult i64 %0, %"x::Array.size.0"
  %1 = icmp ult i64 %2, %"x::Array.size.1"
  %combined = and i1 %.not, %1
  br i1 %combined, label %pass, label %fail
```

After:
```llvm
  %.not = icmp ult i64 %0, %"x::Array.size.sroa.0.0.copyload"
  br i1 %.not, label %L27, label %L29

L27:
  %.not10.not = icmp ult i64 %1, %"x::Array.size.sroa.2.0.copyload"
  br i1 %.not10.not, label %L32, label %L29

L29:
  call void @j_throw_boundserror_1(ptr %"x::Array", i64 %"i::Int64", i64 %"j::Int64")
  unreachable
```

- [x] Full `make -j8` build succeeds including sysimage precompilation
- [x] Verified LLVM IR output shows separate branches per dimension for
`A[i, j]`
- [x] Verified tuple `alloca` is eliminated from the hot path
- [x] Verified single-index `A[i]` still works correctly (no tuple
wrapping)
- [x] Verified `BoundsError` messages are unchanged at runtime
- [ ] CI tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
(cherry picked from commit 268f85e)
@KristofferC
KristofferC force-pushed the backports-release-1.13 branch from 363f98e to b451085 Compare April 15, 2026 07:32
@KristofferC

Copy link
Copy Markdown
Member Author

I'll take the chance to merge this now when CI is green.

@KristofferC
KristofferC merged commit 758f857 into release-1.13 Apr 15, 2026
7 checks passed
@KristofferC
KristofferC deleted the backports-release-1.13 branch April 15, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Release management and versioning.

Projects

None yet

Development

Successfully merging this pull request may close these issues.