Skip to content

Backports for Julia 1.13-rc2 - #62313

Merged
KristofferC merged 16 commits into
release-1.13from
backports-release-1.13
Jul 20, 2026
Merged

Backports for Julia 1.13-rc2#62313
KristofferC merged 16 commits into
release-1.13from
backports-release-1.13

Conversation

@KristofferC

@KristofferC KristofferC commented Jul 9, 2026

Copy link
Copy Markdown
Member

Backported PRs:

Need manual backport:

Contains multiple commits, manual intervention needed:

Non-merged PRs with backport label:

topolarity and others added 16 commits July 18, 2026 21:23
…62043)

MSAN and TSAN require comprehensive build instrumentation, but ASAN can
in theory be used with Julia loaded dynamically as a library in an
application.

To enable this, this PR adds `JULIA_USE_RTLD_DEEPBIND` and
`JULIA_ASAN_COMPAT` environment variables + a rudimentary
`dlsym(...)`-based auto-detection for whether we are running under ASAN
or not.

The auto-detection should work automatically for most users, but the
environment variables exist to allow overriding if necessary.

Co-developed w/ Claude Opus 4.8, although essentially written by hand.

(cherry picked from commit acaea72)
This should fix the FreeBSD build regression from #62043.

This also enables one of the ASAN workarounds for FreeBSD (the
RTLD_DEEPBIND one). The `dlopen` and `siglongjmp` workarounds still need
implementation / testing before ASAN will fully work on FreeBSD - that's
easy work, but I don't have easy access to a FreeBSD machine so it's
left to future work for now.

(cherry picked from commit 329f9b7)
This should improve both invalidations and `--trim` support.

(cherry picked from commit 1d624e2)
Also fix-up `show` implementation for `WindowsRawSocket`, for `--trim`
compat in both cases.

This was improved in #62158 but not completely fixed.

(cherry picked from commit 6b06264)
#62212)

After backporting #55601 to 1.12
I saw PkgEval failures from JET. The robot minimized this to the
following (with a test that fails on master but passes here).

----------

:robot:

inference: keep `undef` refinement for `@isdefined` of `MustAlias` slots

`conditional_change` couples two jobs: narrowing a slot's type (the `⊑`
check) and refining its `undef` flag. For an `@isdefined slot`
Conditional
the then/else type is `widenslotwrapper(slot)`, so when the slot's value
is
tracked as a `MustAlias`, `widened ⋢ MustAlias` and the function hit
`return nothing` — silently dropping the `undef` refinement. A guarded
read
inside a loop was then left as possibly-undefined and merged
monotonically
across the back-edge, so it never recovered, marking the read as not
`:nothrow`.

This regressed in #55601, which removed eager `invalidate_slotwrapper`
so
slots retain `MustAlias` types across reassignment. It surfaced as a JET
false positive ("local variable `prev` may be undefined") on
`Base.join`,
and on master — where `MustAliasesLattice` is in the default lattice —
it
affects the native compiler's effect analysis directly.
Fix: for `@isdefined` Conditionals, keep `oldtyp` instead of bailing, so
the
`undef` refinement is still applied.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com>
(cherry picked from commit 7f3f499)
This PR documents the known bug #9498.

---------

Co-authored-by: Andy Dienes <51664769+adienes@users.noreply.github.com>
(cherry picked from commit 44b0b52)
…#62260)

This fixes a GPUCompiler test issue where we precompile code and make
sure it doesn't recompile. However, because precompilation doesn't
generate instrumented cache entries, and testing runs with code coverage
enabled, we can never reuse cache entries. "Fix" this by not
invalidating foreign CodeInstances for instrumentation, where that
doesn't make sense anyway: they never run as native code here.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit 8048a05)
When a package image is loaded, methods in a worklist-owned custom
`@MethodTable` used to be restored in place as internal typemap entries,
skipping `jl_method_table_activate` and coming back with a cleared
`dispatch_status` (no
`METHOD_SIG_LATEST_WHICH`/`METHOD_SIG_LATEST_ONLY`). This made
`queue_external_cis` drop foreign `CodeInstance`s cached on such methods
during downstream precompilation, and made on-load edge revalidation
treat calls to them as invalidated, so GPUCompiler-style compilation
caches never survived a package image when the overlay method and the
cached code live in the same package.

Fix this by treating all custom method tables as external:
method tables owned by the worklist are now serialized
without their contents (their defs and dispatch caches are dropped), and
all of their still-valid methods are collected as extext methods, so
`jl_add_methods` / `jl_activate_methods` re-add and re-activate them on
load through the same batched, `allow_new_worlds`-gated path used for
methods extending tables owned by other modules. Activation restores the
dispatch fast-path bits.

x-ref: #58291, which added the on-load edge revalidation gated on these
dispatch bits, but only restored them for methods activated through the
external path.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 2adc817)
Co-authored-by: Codex <codex@openai.com>
…Union{}})` (#62304)

`Base.Iterators.Reverse` instructs downstream users to implement
`iterate(::Reverse{<:Foo})`, which requires this guard.

This shape for a Method extension is vulnerable (100% of the time) to
spurious invalidations unless the owner of the `Reverse` type knows to
add a magic `iterate(::Reverse{Union{}})` method to cover up the
intersection with another type's `iterate(::Reverse{<:Bar})` method.

Improves invalidations from OrderedCollections.jl.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 8561b1c)
👨 You could replicate the issue easily via:

```
julia> using REPL

julia> spec = REPL.History.FilterSpec(REPL.History.ConditionSet("test\\; test\\;"))
REPL.History.FilterSpec(String[], String[], Regex[r"\Qtest;\E"i, r"\Qtest;\E"i], ^[[B
Symbol[])

julia> REPL.History.matchregions(spec, "test test;")
ERROR: BoundsError: attempt to access 10-codeunit String at index [11]
Stacktrace:
 [1] _nextind_str(s::String, i::Int64)
   @ Base strings/string.jl:279 [inlined]
 [2] nextind(s::String, i::Int64)
   @ Base strings/string.jl:273 [inlined]
 [3] matchregions(spec::REPL.History.FilterSpec, candidate::String)
   @ REPL.History ~/.julia/juliaup/julia-nightly/Julia-1.14.app/Contents/Resources/julia/share/julia/stdlib/v1.14/REPL/src/History/resumablefiltering.jl:346
 [4] top-level scope
   @ REPL[2]:1
```

---

🤖

When highlighting matches in the history search UI, matchregions
merges adjacent match regions separated by a single space. If a match
ends at the last index of the candidate string and is followed by
another (duplicate or overlapping) region in the sorted list,
nextind was called past the end of the string, throwing a BoundsError
that killed the search display task. Skip the merge check when the
match extends to the end of the string.

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

Fixes #62341

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit b1f508a)
Fixes #62239 and JuliaPy/PythonCall.jl#735

Hopefully this can be back ported to 1.13, so [ChunkCodecs.jl python
integration tests can work
again](https://github.com/JuliaIO/ChunkCodecs.jl/blob/main/test/imagecodecs-compat.jl)

(cherry picked from commit 5a34ccd)
…62372)

`multiq_size` reads `tpheaps = heaps[tp]` before taking
`heaps_lock[tp]`, and then re-checks `length(tpheaps)` on that stale
local after acquiring the lock. When several threads concurrently
perform the first inserts into a pool (e.g. many default-pool tasks
spawning `:interactive` tasks at once), each of them sees the pre-growth
length, so each one in turn allocates a fresh heaps vector and publishes
it with `heaps[tp] = newheaps`, replacing the vector the previous thread
just published. Any task inserted into one of the replaced vectors in
that window is orphaned: it is unreachable from `heaps[tp]`, so no
thread can ever pop it, and anything waiting on it hangs. With `-t 8,4`
and a burst of cross-pool spawn/fetch pairs this reproduces within
seconds.

Re-read `heaps[tp]` under the lock so the re-check and the `copyto!`
operate on the current vector, making the grow-if-needed check correct
and the publication monotonic.

Fixes #62144
Co-authored-by: Claude <claude@users.noreply.github.com>

(cherry picked from commit 733410f)
👨 I had some trouble with some local daemon code and the robot MWEd it
to:

```julia
# flush() on a socket throws after the peer closes the connection, even
# though every byte was already written successfully (and even answered).
#
# flush(s::LibuvStream) issues a zero-length uv_write just to drain the
# write queue (base/stream.jl ~1167); that write fails once the peer end
# is gone, so a fully successful one-shot exchange
#     write(request) -> peer replies -> peer closes -> flush()
# errors whenever anything delays the flush past the peer's close (busy
# event loop, many concurrent tasks, ...).
#
# Reproduces on 1.10.11, 1.12.6, 1.13.0-rc1 (macOS).
using Sockets

function serve_once(path; reply::Bool)
    server = listen(path)
    peer_closed = Channel{Nothing}(1)
    task = @async begin
        c = accept(server)
        try
            readline(c)
            reply && write(c, "pong\n")
        finally
            close(c)                # peer closes right after handling
            close(server)
            put!(peer_closed, nothing)
        end
    end
    return task, peer_closed
end

# Variant 1: flush after the peer closed -> spurious EPIPE.
path = tempname()
server_task, peer_closed = serve_once(path; reply=false)
conn = connect(path)
write(conn, "ping\n")               # delivered: the server read it
take!(peer_closed)                   # wait until the peer has read and closed
try
    flush(conn)                     # IOError: write: broken pipe (EPIPE)
    println("variant 1: flush ok")
catch e
    println("variant 1: flush threw: ", sprint(showerror, e))
end
close(conn)
wait(server_task)
rm(path; force=true)

# Variant 2: the complete reply arrives before flush. Depending on whether
# Julia has processed the peer's EOF by then, flush throws either EPIPE or
# "stream is closed or unusable". Both come from the extra zero-length write.
path = tempname()
server_task, peer_closed = serve_once(path; reply=true)
conn = connect(path)
write(conn, "ping\n")
take!(peer_closed)
println("variant 2: got ", repr(readline(conn)), " — full round trip worked")
try
    flush(conn)                     # IOError: EPIPE or closed/unusable
    println("variant 2: flush ok")
catch e
    println("variant 2: flush threw: ", sprint(showerror, e))
end
close(conn)
wait(server_task)
rm(path; force=true)
```

Running this gives:

```
variant 1: flush threw: IOError: write: broken pipe (EPIPE)
variant 2: got "pong" — full round trip worked
variant 2: flush threw: IOError: stream is closed or unusable
```

with the patched `flush` here it gives:

```
variant 1: flush ok
variant 2: got "pong" — full round trip worked
variant 2: flush ok
```

----

🤖
`flush(::LibuvStream)` submits a zero-length write to wait for the write
queue to drain. If the peer closes the connection after a completed
exchange, that write fails with EPIPE (or `check_open` throws once the
peer's EOF has been processed), so `flush` threw even though every byte
had already been delivered. Swallow errors from this flush-write: the
queued writes report their failures to their own writers, so nothing is
lost.

Written with the assistance of generative AI.

Co-authored-by: OpenAI Codex <codex@openai.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: OpenAI Codex <codex@openai.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
(cherry picked from commit 0fe2ace)
…62301)

`jl_lookup_generic_` loads `mc->leafcache` before falling through to the
full argument tuple construction path. That path may allocate and
safepoint in arg_type_tuple, after which the previously loaded leafcache
pointer may no longer be valid: another thread can grow/replace the
method cache leafcache, and a GC may reclaim the old array while the
reader still holds only the stale C local. The fix is to reload
`mc->leafcache` after `arg_type_tuple`, before calling
`lookup_leafcache`.

See more discussion in this thread:
#62162 (comment)

Co-authored-by: GitHub Copilot <copilot@github.com>
(cherry picked from commit 6ac8c4f)
@KristofferC
KristofferC force-pushed the backports-release-1.13 branch from 1017ae7 to 2e4428a Compare July 18, 2026 19:23
@KristofferC
KristofferC merged commit 3e4019c into release-1.13 Jul 20, 2026
7 of 8 checks passed
@KristofferC
KristofferC deleted the backports-release-1.13 branch July 20, 2026 10:58
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.

8 participants