Backports for Julia 1.13-rc2 - #62313
Merged
Merged
Conversation
…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)
#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)
…#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)
Shipping the [security patches that are in Debian](https://udd.debian.org/patches.cgi?src=libssh2&version=1.11.1-4) (cherry picked from commit dc5463d)
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)
(cherry picked from commit 3ed74ae)
👨 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
force-pushed
the
backports-release-1.13
branch
from
July 18, 2026 19:23
1017ae7 to
2e4428a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backported PRs:
muladdwithBoolarguments #61961dlopenworkarounds #62043jl_running_under_sanitizeron FreeBSD #62056showmethod forWindowsRawSocket#62158rm(...)on Windows #62165@isdefinedofMustAliasslots #62212Need manual backport:
iparamsininst_datatype_inner#62143Contains multiple commits, manual intervention needed:
@spawn#61826jl_find_dynamic_library_by_addron some distros #62121.superwhen canonicalizing types #62188@docexpansion #62236Non-merged PRs with backport label:
iterate(::Reverse{Union{}})#62304@nospecialize/@specializewhen given comma separated arguments to not silently do nothing useful #62293WITH_NVTXbuild option #62271TypeName.concrete_onlyflag to opt-in to inference barrier for abstract calls (prevents invalidations) #61504