From 8d5205e7343c144a6ed36524d238be7991a897e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Wed, 24 Sep 2025 22:57:06 +0200 Subject: [PATCH 01/38] Set world bounds on `CodeInfo` created for `OpaqueClosure(::IRCode)` (#59631) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting world bounds on the created `CodeInfo` allows us to interpret opaque closures faster. Taking the following example: ```julia julia> f(x, y) = x + y f (generic function with 1 method) julia> ir = Base.code_ircode_by_type(Tuple{typeof(f), Int, Int})[1][1] 1 1 ─ %1 = intrinsic Base.add_int(_2, _3)::Int64 │╻ + └── return %1 ││ julia> ir.argtypes[1] = Tuple{} Tuple{} julia> oc = Core.OpaqueClosure(ir; do_compile=true) (::Int64, ::Int64)->◌::Int64 ``` this is what we emitted before ```julia julia> @code_typed oc(1, 2) Pair{Core.CodeInfo, Any}(CodeInfo( @ REPL[8]:1 within `f` ┌ @ int.jl:87 within `+` 1 ─│ %1 = dynamic Base.add_int(none@_2, none@_3)::Int64 └──│ return %1 └ ), Int64) julia> using BenchmarkTools; @btime $oc(1, 2) 39.765 ns (0 allocations: 0 bytes) ``` and now: ```julia julia> @code_typed oc(1, 2) Pair{Core.CodeInfo, Any}(CodeInfo( @ REPL[93]:1 within `f` ┌ @ int.jl:87 within `+` 1 ─│ %1 = intrinsic Base.add_int(none@_2, none@_3)::Int64 └──│ return %1 └ ), Int64) julia> using BenchmarkTools; @btime $oc(1, 2) 2.678 ns (0 allocations: 0 bytes) ``` The overhead notably adds more and more with every statement, which for ~20 statements led to > 1 µs of overhead, and multiple allocations. This overhead is observed on 1.12+ only (1.11 evaluates as fast as with this change), which may have been surfaced by the partitioned bindings feature. (cherry picked from commit a5576b4ddb61ce10eefba752a21ef806c4d6fd93) --- Compiler/src/opaque_closure.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Compiler/src/opaque_closure.jl b/Compiler/src/opaque_closure.jl index d0a375c2a54b5..21f2640037893 100644 --- a/Compiler/src/opaque_closure.jl +++ b/Compiler/src/opaque_closure.jl @@ -48,6 +48,8 @@ function Core.OpaqueClosure(ir::IRCode, @nospecialize env...; end src.slotflags = fill(zero(UInt8), nargtypes) src.slottypes = copy(ir.argtypes) + src.min_world = ir.valid_worlds.min_world + src.max_world = ir.valid_worlds.max_world src.isva = isva src.nargs = UInt(nargtypes) src = ir_to_codeinf!(src, ir) From 3c33f509a2b5536da33b53b29a757e0b8edbe455 Mon Sep 17 00:00:00 2001 From: Lionel Zoubritzky Date: Thu, 25 Sep 2025 23:41:14 +0200 Subject: [PATCH 02/38] Fix gcdx and lcm with mixed signed/unsigned arguments (#59628) Add `gcdx(a::Signed, b::Unsigned)` and `gcdx(a::Unsigned, b::Signed)` methods to fix #58025: ```julia julia> gcdx(UInt16(100), Int8(-101)) # pr (0x0001, 0xffff, 0xffff) julia> gcdx(UInt16(100), Int8(-101)) # master, incorrect result (0x0005, 0xf855, 0x0003) ``` Also add the equivalent methods for `lcm` to fix the systematic `InexactError` when one argument is a negative `Signed` and the other is any `Unsigned`: ```julia julia> lcm(UInt16(100), Int8(-101)) # pr 0x2774 julia> lcm(UInt16(100), Int8(-101)) # master, error ERROR: InexactError: trunc(UInt16, -101) Stacktrace: [1] throw_inexacterror(func::Symbol, to::Type, val::Int8) @ Core ./boot.jl:866 [2] check_sign_bit @ ./boot.jl:872 [inlined] [3] toUInt16 @ ./boot.jl:958 [inlined] [4] UInt16 @ ./boot.jl:1011 [inlined] [5] convert @ ./number.jl:7 [inlined] [6] _promote @ ./promotion.jl:379 [inlined] [7] promote @ ./promotion.jl:404 [inlined] [8] lcm(a::UInt16, b::Int8) @ Base ./intfuncs.jl:152 [9] top-level scope @ REPL[62]:1 ``` Inspired by https://github.com/JuliaLang/julia/pull/59487#issuecomment-3258209203. The difference is that the solution proposed in this PR keeps the current correct result type for inputs such as `(::Int16, ::UInt8)`. (cherry picked from commit 4f1e471d54e93a79aee246bba100032ab3995a8c) --- base/intfuncs.jl | 12 ++++++++++++ test/intfuncs.jl | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 3f26d2597f9d9..7b665caad6b17 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -148,6 +148,8 @@ gcd(a::Rational) = checked_abs(a.num) // a.den lcm(a::Union{Integer,Rational}) = gcd(a) gcd(a::Unsigned, b::Signed) = gcd(promote(a, abs(b))...) gcd(a::Signed, b::Unsigned) = gcd(promote(abs(a), b)...) +lcm(a::Unsigned, b::Signed) = lcm(promote(a, abs(b))...) +lcm(a::Signed, b::Unsigned) = lcm(promote(abs(a), b)...) gcd(a::Real, b::Real) = gcd(promote(a,b)...) lcm(a::Real, b::Real) = lcm(promote(a,b)...) gcd(a::Real, b::Real, c::Real...) = gcd(a, gcd(b, c...)) @@ -252,6 +254,16 @@ function gcdx(a::Real, b::Real, cs::Real...) d′, x, ys... = gcdx(d, cs...) return d′, i*x, j*x, ys... end +function gcdx(a::Signed, b::Unsigned) + R = promote_type(typeof(a), typeof(b)) + _a = a % signed(R) # handle the case a == typemin(typeof(a)) if R != typeof(a) + d, u, v = gcdx(promote(abs(_a), b)...) + d, flipsign(u, a), v +end +function gcdx(a::Unsigned, b::Signed) + d, v, u = gcdx(b, a) + d, u, v +end # multiplicative inverse of n mod m, error if none diff --git a/test/intfuncs.jl b/test/intfuncs.jl index adcfee2a050dd..5c5c7510d9da8 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -216,6 +216,35 @@ end x, y = Int8(-12), UInt(100) d, u, v = gcdx(x, y) @test x*u + y*v == d + +end + +# issue #58025 +@testset "Mixed signed/unsigned types" begin + cases = [ # adapted from https://github.com/JuliaLang/julia/pull/59487#issuecomment-3258209203 + (UInt16(100), Int8(-101)), + (Int8(-50), UInt16(75)), + (UInt32(12), Int16(-18)), + (Int64(-24), UInt8(36)), + (UInt8(15), Int16(-25)), + (Int32(-42), UInt64(56)), + (UInt128(1000), Int32(-1500)), + (UInt64(0), Int32(-5)), + (Int16(-7), UInt8(0)), + (Int8(-14), UInt8(13)), + ] + for (a, b) in cases + g1 = gcd(a, b) + g2, s, t = gcdx(a, b) + @test g1 === g2 + @test s*a + t*b == g2 + @test g2 >= 0 + @test lcm(a, b) === convert(typeof(g1), lcm(widen(a), widen(b))) + end + + @test gcdx(Int16(-32768), Int8(-128)) === (Int16(128), Int16(0), Int16(-1)) + @test gcdx(Int8(-128), UInt16(256)) === (0x0080, 0xffff, 0x0000) + @test_broken gcd(Int8(-128), UInt16(256)) === 0x0080 end @testset "gcd/lcm/gcdx for custom types" begin From 734aee5b712bbfc07b0d7a18df211b1f6f23e03d Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 1 Oct 2025 04:21:41 +0900 Subject: [PATCH 03/38] effects: improve nothrow modeling for `Core._svec_len` (#59706) --- Compiler/src/tfuncs.jl | 12 ++++++++---- Compiler/test/effects.jl | 2 ++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Compiler/src/tfuncs.jl b/Compiler/src/tfuncs.jl index 33c866c401811..5070e4ba16d00 100644 --- a/Compiler/src/tfuncs.jl +++ b/Compiler/src/tfuncs.jl @@ -586,15 +586,19 @@ add_tfunc(nfields, 1, 1, nfields_tfunc, 1) add_tfunc(Core._expr, 1, INT_INF, @nospecs((𝕃::AbstractLattice, args...)->Expr), 100) add_tfunc(svec, 0, INT_INF, @nospecs((𝕃::AbstractLattice, args...)->SimpleVector), 20) -@nospecs function _svec_len_tfunc(𝕃::AbstractLattice, s) +@nospecs function _svec_len_tfunc(::AbstractLattice, s) if isa(s, Const) && isa(s.val, SimpleVector) return Const(length(s.val)) end return Int end add_tfunc(Core._svec_len, 1, 1, _svec_len_tfunc, 1) +@nospecs function _svec_len_nothrow(𝕃::AbstractLattice, s) + ⊑ = partialorder(𝕃) + return s ⊑ SimpleVector +end -@nospecs function _svec_ref_tfunc(𝕃::AbstractLattice, s, i) +@nospecs function _svec_ref_tfunc(::AbstractLattice, s, i) if isa(s, Const) && isa(i, Const) s, i = s.val, i.val if isa(s, SimpleVector) && isa(i, Int) @@ -604,7 +608,7 @@ add_tfunc(Core._svec_len, 1, 1, _svec_len_tfunc, 1) return Any end add_tfunc(Core._svec_ref, 2, 2, _svec_ref_tfunc, 1) -@nospecs function typevar_tfunc(𝕃::AbstractLattice, n, lb_arg, ub_arg) +@nospecs function typevar_tfunc(::AbstractLattice, n, lb_arg, ub_arg) lb = Union{} ub = Any ub_certain = lb_certain = true @@ -2363,7 +2367,7 @@ function _builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f::Builtin), argt return compilerbarrier_nothrow(argtypes[1], nothing) elseif f === Core._svec_len na == 1 || return false - return _svec_len_tfunc(𝕃, argtypes[1]) isa Const + return _svec_len_nothrow(𝕃, argtypes[1]) elseif f === Core._svec_ref na == 2 || return false return _svec_ref_tfunc(𝕃, argtypes[1], argtypes[2]) isa Const diff --git a/Compiler/test/effects.jl b/Compiler/test/effects.jl index 79d16602a8d92..2b6895d5f9da0 100644 --- a/Compiler/test/effects.jl +++ b/Compiler/test/effects.jl @@ -1471,3 +1471,5 @@ let effects = Base.infer_effects((Core.SimpleVector,Int); optimize=false) do sve @test !Compiler.is_nothrow(effects) @test Compiler.is_terminates(effects) end + +@test Compiler.is_nothrow(Base.infer_effects(length, (Core.SimpleVector,))) From b26c91cb3d8c9f7cfa11e1ba39c97f99ef1aad88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20H=C3=A9not?= <38465572+OlivierHnt@users.noreply.github.com> Date: Sun, 27 Jul 2025 13:27:14 +0200 Subject: [PATCH 04/38] Fix rounding when converting Rational to BigFloat (#59063) --- base/mpfr.jl | 8 +++++++- test/mpfr.jl | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 2175fb56cc526..cd73506c9e96f 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -391,12 +391,18 @@ BigFloat(x::Union{Float16,Float32}, r::MPFRRoundingMode=rounding_raw(BigFloat); BigFloat(Float64(x), r; precision=precision) function BigFloat(x::Rational, r::MPFRRoundingMode=rounding_raw(BigFloat); precision::Integer=_precision_with_base_2(BigFloat)) + r_den = _opposite_round(r) setprecision(BigFloat, precision) do setrounding_raw(BigFloat, r) do - BigFloat(numerator(x))::BigFloat / BigFloat(denominator(x))::BigFloat + BigFloat(numerator(x))::BigFloat / BigFloat(denominator(x), r_den)::BigFloat end end end +function _opposite_round(r::MPFRRoundingMode) + r == MPFRRoundUp && return MPFRRoundDown + r == MPFRRoundDown && return MPFRRoundUp + return r +end function tryparse(::Type{BigFloat}, s::AbstractString; base::Integer=0, precision::Integer=_precision_with_base_2(BigFloat), rounding::MPFRRoundingMode=rounding_raw(BigFloat)) !isempty(s) && isspace(s[end]) && return tryparse(BigFloat, rstrip(s), base = base) diff --git a/test/mpfr.jl b/test/mpfr.jl index 3bb8768b280d5..627d49935795d 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -35,6 +35,9 @@ import Base.MPFR @test typeof(BigFloat(1//1)) == BigFloat @test typeof(BigFloat(one(Rational{BigInt}))) == BigFloat + rat = 1 // (big(2)^300 + 1) + @test BigFloat(rat, RoundDown) < rat < BigFloat(rat, RoundUp) + @test BigFloat(-rat, RoundUp) < -rat < BigFloat(-rat, RoundDown) # BigFloat constructor respects global precision when not specified let prec = precision(BigFloat) < 16 ? 256 : precision(BigFloat) ÷ 2 From 58ebaaffb33ac9e962438608d1748548dfb95098 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 29 Sep 2025 11:17:14 -0400 Subject: [PATCH 05/38] [Compiler] fix stdout/stderr to point to Core (#59672) Followup to #58425 Fixes #56645 --- Compiler/src/Compiler.jl | 8 +++++--- Compiler/test/validation.jl | 9 +++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Compiler/src/Compiler.jl b/Compiler/src/Compiler.jl index 080cdc6f7ee0e..01f2dbe33b674 100644 --- a/Compiler/src/Compiler.jl +++ b/Compiler/src/Compiler.jl @@ -41,9 +41,8 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc MethodTable, MethodCache, PartialOpaque, SimpleVector, TypeofVararg, _apply_iterate, apply_type, compilerbarrier, donotdelete, memoryref_isassigned, memoryrefget, memoryrefnew, memoryrefoffset, memoryrefset!, print, println, show, svec, - typename, unsafe_write, write + typename, unsafe_write, write, stdout, stderr -using Base using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer, PARTITION_KIND_GLOBAL, PARTITION_KIND_UNDEF_CONST, PARTITION_KIND_BACKDATED_CONST, PARTITION_KIND_DECLARED, PARTITION_FLAG_DEPWARN, @@ -64,7 +63,10 @@ using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospeciali partition_restriction, quoted, rename_unionall, rewrap_unionall, specialize_method, structdiff, tls_world_age, unconstrain_vararg_length, unionlen, uniontype_layout, uniontypes, unsafe_convert, unwrap_unionall, unwrapva, vect, widen_diagonal, - _uncompressed_ir, maybe_add_binding_backedge! + _uncompressed_ir, maybe_add_binding_backedge!, + devnull, devnull as stdin + +using Base using Base.Order import Base: ==, _topmod, append!, convert, copy, copy!, findall, first, get, get!, diff --git a/Compiler/test/validation.jl b/Compiler/test/validation.jl index f01ff85e4321c..766f887c05860 100644 --- a/Compiler/test/validation.jl +++ b/Compiler/test/validation.jl @@ -4,6 +4,15 @@ using Test, Core.IR include("setup_Compiler.jl") +@testset "stdio validation" begin + for s in (:stdout, :stderr, :print, :println, :write) + @test getglobal(Compiler, s) === getglobal(Core, s) + @test isconst(Compiler, s) + end + @test Compiler.stdin === devnull + @test isconst(Compiler, :stdin) +end + function f22938(a, b, x...) nothing nothing From 70139ad5e1146de7fe818137c175bc6942de6be7 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 2 Oct 2025 16:26:05 +0900 Subject: [PATCH 06/38] do not specialze for `abstract_eval_nonlinearized_foreigncall_name` (#59722) Following up JuliaLang/julia#58872. Since `e` is potentially arbitrary runtime value, we should not specialize on it. --- Compiler/src/abstractinterpretation.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index 3c4de73b4c36a..69ec3c27de903 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -3444,9 +3444,13 @@ function refine_partial_type(@nospecialize t) return t end -abstract_eval_nonlinearized_foreigncall_name(interp::AbstractInterpreter, e, sstate::StatementState, sv::IRInterpretationState) = nothing +abstract_eval_nonlinearized_foreigncall_name( + ::AbstractInterpreter, @nospecialize(e), ::StatementState, ::IRInterpretationState + ) = nothing -function abstract_eval_nonlinearized_foreigncall_name(interp::AbstractInterpreter, e, sstate::StatementState, sv::AbsIntState) +function abstract_eval_nonlinearized_foreigncall_name( + interp::AbstractInterpreter, @nospecialize(e), sstate::StatementState, sv::InferenceState + ) if isexpr(e, :call) n = length(e.args) argtypes = Vector{Any}(undef, n) From 37256d2256b5444c7152f6e184c8545ffb04474a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 2 Oct 2025 03:27:11 -0400 Subject: [PATCH 07/38] Compiler: fix inferred nothrow effects for add_ptr and sub_ptr (#59720) Previously, add_ptr and sub_ptr intrinsics were incorrectly inferred as potentially throwing because they fell through to the general primitive type check, which was incorrect after #53687 changed them. This adds explicit handling in intrinsic_exct to return Union{} (nothrow) when given correct argument types (Ptr and UInt), similar to #57398. Fixes #57557 Written by Claude --- Compiler/src/tfuncs.jl | 7 +++++++ Compiler/test/effects.jl | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/Compiler/src/tfuncs.jl b/Compiler/src/tfuncs.jl index 5070e4ba16d00..fcead89d0a151 100644 --- a/Compiler/src/tfuncs.jl +++ b/Compiler/src/tfuncs.jl @@ -2978,6 +2978,13 @@ function intrinsic_exct(𝕃::AbstractLattice, f::IntrinsicFunction, argtypes::V return Union{} end + if f === Intrinsics.add_ptr || f === Intrinsics.sub_ptr + if !(argtypes[1] ⊑ Ptr && argtypes[2] ⊑ UInt) + return TypeError + end + return Union{} + end + # The remaining intrinsics are math/bits/comparison intrinsics. # All the non-floating point intrinsics work on primitive values of the same type. isshift = f === shl_int || f === lshr_int || f === ashr_int diff --git a/Compiler/test/effects.jl b/Compiler/test/effects.jl index 2b6895d5f9da0..e06d885fcefd7 100644 --- a/Compiler/test/effects.jl +++ b/Compiler/test/effects.jl @@ -1438,6 +1438,11 @@ let effects = Base.infer_effects(Core.Intrinsics.pointerset, Tuple{Vararg{Any}}) @test Compiler.is_consistent(effects) @test !Compiler.is_effect_free(effects) end +@test Compiler.intrinsic_nothrow(Core.Intrinsics.add_ptr, Any[Ptr{Int}, UInt]) +@test Compiler.intrinsic_nothrow(Core.Intrinsics.sub_ptr, Any[Ptr{Int}, UInt]) +@test !Compiler.intrinsic_nothrow(Core.Intrinsics.add_ptr, Any[UInt, UInt]) +@test !Compiler.intrinsic_nothrow(Core.Intrinsics.sub_ptr, Any[UInt, UInt]) +@test Compiler.is_nothrow(Base.infer_effects(+, Tuple{Ptr{UInt8}, UInt})) # effects modeling for atomic intrinsics # these functions especially need to be marked !effect_free since they imply synchronization for atomicfunc = Any[ From c715ddb56c375d8b5d713c1718ee16557acce376 Mon Sep 17 00:00:00 2001 From: Andy Dienes <51664769+adienes@users.noreply.github.com> Date: Tue, 30 Sep 2025 18:43:08 -0400 Subject: [PATCH 08/38] fix `powermod` correctness on unsigned power of half typemax (#59680) the comment claims: > When the concrete type of p is signed and has the lowest value, > `p != 0 && p == -p` is equivalent to `p == typemin(typeof(p))` this is true, but fails to consider that `p == -p` is also true when `p = div(typemax(UInt), 2) + 1`, and that `p` is not necessarily signed leading to incorrect results on inputs like ``` julia> powermod(0x03, 0x80, 0x07) 0x01 ``` which should be `0x02` (cherry picked from commit bb3be0dc56f7c0f2cb645318d728280285d81933) --- base/intfuncs.jl | 22 ++++++++++++---------- test/intfuncs.jl | 3 +++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 7b665caad6b17..d660c6d0a8ecc 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -489,18 +489,20 @@ julia> powermod(5, 3, 19) function powermod(x::Integer, p::Integer, m::T) where T<:Integer p == 0 && return mod(one(m),m) # When the concrete type of p is signed and has the lowest value, - # `p != 0 && p == -p` is equivalent to `p == typemin(typeof(p))` for 2's complement representation. + # `p < 0 && p == -p` is equivalent to `p == typemin(typeof(p))` for 2's complement representation. # but will work for integer types like `BigInt` that don't have `typemin` defined # It needs special handling otherwise will cause overflow problem. - if p == -p - imod = invmod(x, m) - rhalf = powermod(imod, -(p÷2), m) - r::T = mod(widemul(rhalf, rhalf), m) - isodd(p) && (r = mod(widemul(r, imod), m)) - #else odd - return r - elseif p < 0 - return powermod(invmod(x, m), -p, m) + if p < 0 + if p == -p + imod = invmod(x, m) + rhalf = powermod(imod, -(p÷2), m) + r::T = mod(widemul(rhalf, rhalf), m) + isodd(p) && (r = mod(widemul(r, imod), m)) + #else odd + return r + else + return powermod(invmod(x, m), -p, m) + end end (m == 1 || m == -1) && return zero(m) b = oftype(m,mod(x,m)) # this also checks for divide by zero diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 5c5c7510d9da8..dc800a3a00b08 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -352,6 +352,9 @@ end @test powermod(2, big(3), -5) == -2 @inferred powermod(2, -2, -5) @inferred powermod(big(2), -2, UInt(5)) + + @test powermod(-3, 0x80, 7) === 2 + @test powermod(0x03, 0x80, 0x07) === 0x02 end @testset "nextpow/prevpow" begin From 2c4d5cfb227b5dc0e5f4e761d0f76da15d40ad6d Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 2 Oct 2025 04:38:12 +0200 Subject: [PATCH 09/38] retain size for zero length multidimensional arrays in mmap (#59719) Fixes https://github.com/JuliaLang/julia/issues/44679 Co-authored-by: KristofferC (cherry picked from commit 65ef689c6e08b240be70c3e42efd544125014be6) --- stdlib/Mmap/src/Mmap.jl | 2 +- stdlib/Mmap/test/runtests.jl | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/stdlib/Mmap/src/Mmap.jl b/stdlib/Mmap/src/Mmap.jl index c527123d51bb8..fb6bb72026588 100644 --- a/stdlib/Mmap/src/Mmap.jl +++ b/stdlib/Mmap/src/Mmap.jl @@ -199,7 +199,7 @@ function mmap(io::IO, overflow && throw(ArgumentError("requested size prod($((len, dims...))) too large, would overflow typeof(size(T)) == $(typeof(len))")) end len >= 0 || throw(ArgumentError("requested size must be ≥ 0, got $len")) - len == 0 && return Array{T}(undef, ntuple(x->0,Val(N))) + len == 0 && return Array{T}(undef, dims) len < typemax(Int) - PAGESIZE || throw(ArgumentError("requested size must be < $(typemax(Int)-PAGESIZE), got $len")) offset >= 0 || throw(ArgumentError("requested offset must be ≥ 0, got $offset")) diff --git a/stdlib/Mmap/test/runtests.jl b/stdlib/Mmap/test/runtests.jl index 0c34c49d400a7..d129c0ee94295 100644 --- a/stdlib/Mmap/test/runtests.jl +++ b/stdlib/Mmap/test/runtests.jl @@ -11,12 +11,13 @@ GC.gc(); GC.gc() GC.gc(); GC.gc() @test mmap(file, Array{UInt8,3}, (1,1,11)) == reshape(t,(1,1,11)) GC.gc(); GC.gc() -@test mmap(file, Array{UInt8,3}, (11,0,1)) == Array{UInt8}(undef, (0,0,0)) +@test size(mmap(file, Array{UInt8,3}, (11,0,1))) == (11,0,1) @test mmap(file, Vector{UInt8}, (11,)) == t GC.gc(); GC.gc() @test mmap(file, Array{UInt8,2}, (1,11)) == t' GC.gc(); GC.gc() -@test mmap(file, Array{UInt8,2}, (0,12)) == Array{UInt8}(undef, (0,0)) +@test size(mmap(file, Array{UInt8,2}, (0,12))) == (0,12) +@test size(mmap(file, Matrix{Float32}, (10,0))) == (10,0) m = mmap(file, Array{UInt8,3}, (1,2,1)) @test m == reshape(b"He",(1,2,1)) finalize(m); m=nothing; GC.gc() From 9e7bb28cbeb1f514b1fd26c773e8820f22b492cf Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 2 Oct 2025 13:54:21 -0400 Subject: [PATCH 10/38] improve compile-ability of method errors (#59709) This was correct prior to changing closures to use Core.Typeof, but was not corrected when that PR was merged. This doesn't seem to quite fix issue #56861, since we used to have a precompile workload for this, and now REPL is a separate package. (cherry picked from commit 9c8886c741e850693855b61f1ab32e467f04e2d9) --- base/errorshow.jl | 16 ++++++++-------- base/experimental.jl | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index e771597d7b1e0..602e6c55cb388 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -259,7 +259,7 @@ function showerror(io::IO, ex::MethodError) is_arg_types = !isa(ex.args, Tuple) arg_types = is_arg_types ? ex.args : typesof(ex.args...) arg_types_param::SimpleVector = (unwrap_unionall(arg_types)::DataType).parameters - san_arg_types_param = Any[rewrap_unionall(a, arg_types) for a in arg_types_param] + san_arg_types_param = Any[rewrap_unionall(arg_types_param[i], arg_types) for i in 1:length(arg_types_param)] f = ex.f meth = methods_including_ambiguous(f, arg_types) if isa(meth, MethodList) && length(meth) > 1 @@ -399,10 +399,10 @@ function showerror_ambiguous(io::IO, meths, f, args::Type) sigfix = typeintersect(m.sig, sigfix) end if isa(unwrap_unionall(sigfix), DataType) && sigfix <: Tuple - let sigfix=sigfix - if all(m->morespecific(sigfix, m.sig), meths) + let sigfix=Core.Box(sigfix) + if all(m->morespecific(sigfix.contents, m.sig), meths) print(io, "\nPossible fix, define\n ") - show_tuple_as_call(io, :function, sigfix) + show_tuple_as_call(io, :function, sigfix.contents) else print(io, "To resolve the ambiguity, try making one of the methods more specific, or ") print(io, "adding a new method more specific than any of the existing applicable methods.") @@ -487,10 +487,10 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs=[]) # If isvarargtype then it checks whether the rest of the input arguments matches # the varargtype if Base.isvarargtype(sig[i]) - sigstr = (unwrapva(unwrap_unionall(sig[i])), "...") + sigstr = Core.svec(unwrapva(unwrap_unionall(sig[i])), "...") j = length(t_i) else - sigstr = (sig[i],) + sigstr = Core.svec(sig[i],) j = i end # Checks if the type of arg 1:i of the input intersects with the current method @@ -536,9 +536,9 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs=[]) for (k, sigtype) in enumerate(sig[length(t_i)+1:end]) sigtype = isvarargtype(sigtype) ? unwrap_unionall(sigtype) : sigtype if Base.isvarargtype(sigtype) - sigstr = (unwrapva(sigtype::Core.TypeofVararg), "...") + sigstr = Core.svec(unwrapva(sigtype::Core.TypeofVararg), "...") else - sigstr = (sigtype,) + sigstr = Core.svec(sigtype,) end if !((min(length(t_i), length(sig)) == 0) && k==1) print(iob, ", ") diff --git a/base/experimental.jl b/base/experimental.jl index efaf4bd33a820..8500d8c3ea91a 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -315,6 +315,7 @@ the handler for that type. This interface is experimental and subject to change or removal without notice. """ function show_error_hints(io, ex, args...) + @nospecialize hinters = get(_hint_handlers, typeof(ex), nothing) isnothing(hinters) && return for handler in hinters From f0d980987321de75140b5fc1dd067e9e008fb54d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 3 Oct 2025 11:22:29 -0400 Subject: [PATCH 11/38] iobuffer: fix overflow OOB read/write bugs (#59728) maxsize is usually typemax, so need to be careful to not do comparisons after adding to it. Substracting from it should normally be perfectly fine. At worst we should compute a negative amount of space remaining. (cherry picked from commit 85687b522a6e73836b6ae95e65a8dc1723427d32) --- base/iobuffer.jl | 10 ++++++---- base/stream.jl | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/base/iobuffer.jl b/base/iobuffer.jl index ca2757201aae8..fee699287705b 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -604,7 +604,8 @@ end end # The fast path here usually checks there is already room, then does nothing. # When append is true, new data is added after io.size, not io.ptr - existing_space = min(lastindex(io.data), io.maxsize + get_offset(io)) - (io.append ? io.size : io.ptr - 1) + start_offset = io.append ? io.size : io.ptr - 1 + existing_space = min(lastindex(io.data) - start_offset, io.maxsize - (start_offset - get_offset(io))) if existing_space < nshort % Int # Outline this function to make it more likely that ensureroom inlines itself return ensureroom_slowpath(io, nshort, existing_space) @@ -824,12 +825,13 @@ function unsafe_write(to::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt) append = to.append ptr = append ? size+1 : to.ptr data = to.data - to_write = min(nb, (min(Int(length(data))::Int, to.maxsize + get_offset(to)) - ptr + 1) % UInt) % Int + start_offset = ptr - 1 + to_write = max(0, min(nb, (min(Int(length(data))::Int - start_offset, to.maxsize - (start_offset - get_offset(to)))) % UInt) % Int) # Dispatch based on the type of data, to possibly allow using memcpy _unsafe_write(data, p, ptr, to_write % UInt) # Update to.size only if the ptr has advanced to higher than # the previous size. Otherwise, we just overwrote existing data - to.size = max(size, ptr + to_write - 1) + to.size = max(size, start_offset + to_write) # If to.append, we only update size, not ptr. if !append to.ptr = ptr + to_write @@ -867,7 +869,7 @@ end ptr = (to.append ? to.size+1 : to.ptr) # We have just ensured there is room for 1 byte, EXCEPT if we were to exceed # maxsize. So, we just need to check that here. - if ptr > to.maxsize + get_offset(to) + if ptr - get_offset(to) > to.maxsize return 0 else to.data[ptr] = a diff --git a/base/stream.jl b/base/stream.jl index 6103d4ff1bb31..836fe8391302e 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -617,7 +617,8 @@ end function alloc_request(buffer::IOBuffer, recommended_size::UInt) ensureroom(buffer, recommended_size) ptr = buffer.append ? buffer.size + 1 : buffer.ptr - nb = min(length(buffer.data), buffer.maxsize + get_offset(buffer)) - ptr + 1 + start_offset = ptr - 1 + nb = max(0, min(length(buffer.data) - start_offset, buffer.maxsize - (start_offset - get_offset(buffer)))) return (Ptr{Cvoid}(pointer(buffer.data, ptr)), nb) end From 1d71dea9f70c24f8c806c68328a20c73a45dad34 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sat, 4 Oct 2025 09:55:25 +0200 Subject: [PATCH 12/38] fix getting field docstrings for parametric structs (#59725) (cherry picked from commit 47908c8be8f1fe4cab106966dcbad8e1692efc3e) --- stdlib/REPL/src/docview.jl | 3 ++- stdlib/REPL/test/docview.jl | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 5e0a30abb4137..52153a12f606f 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -640,7 +640,7 @@ function _repl(x, brief::Bool=true, mod::Module=Main, internal_accesses::Union{N docs = esc(:(@doc $x)) docs = if isfield(x) quote - if isa($(esc(x.args[1])), DataType) + if $(esc(x.args[1])) isa Type fielddoc($(esc(x.args[1])), $(esc(x.args[2]))) else $docs @@ -684,6 +684,7 @@ function fielddoc(binding::Binding, field::Symbol) end # As with the additional `doc` methods, this converts an object to a `Binding` first. +fielddoc(obj::UnionAll, field::Symbol) = fielddoc(Base.unwrap_unionall(obj), field) fielddoc(object, field::Symbol) = fielddoc(aliasof(object, typeof(object)), field) diff --git a/stdlib/REPL/test/docview.jl b/stdlib/REPL/test/docview.jl index 8c92d9b3b1d95..0ac6f88d12ed6 100644 --- a/stdlib/REPL/test/docview.jl +++ b/stdlib/REPL/test/docview.jl @@ -111,6 +111,24 @@ end @test endswith(get_help_standard("Int.not_a_field"), "`$Int` has no fields.\n") end +@testset "Parametric struct field help (#59524)" begin + "NonParametricStruct docstring" + struct NonParametricStruct + "field_x docstring" + field_x::Float64 + end + + "ParametricStruct docstring" + struct ParametricStruct{T<:Real} + "field_y docstring" + field_y::T + end + + @test occursin("field_x docstring", get_help_standard("NonParametricStruct.field_x")) + @test occursin("field_y docstring", get_help_standard("ParametricStruct.field_y")) + @test endswith(get_help_standard("ParametricStruct.not_a_field"), "ParametricStruct` has field `field_y`.\n") +end + module InternalWarningsTests module A From b5bed042473e5974baf77e7bbdd67ff16c104b2c Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Sat, 4 Oct 2025 16:00:14 -0700 Subject: [PATCH 13/38] Improve floating-point Euclidean division for Float16 and Float32 (#49637) (cherry picked from commit cb7d44629cb76aa52edad0f3d92d340782c0f412) --- base/div.jl | 6 ++++++ test/numbers.jl | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/base/div.jl b/base/div.jl index 3fec8d2f5cdf3..0ec852ff045c7 100644 --- a/base/div.jl +++ b/base/div.jl @@ -385,3 +385,9 @@ end # NOTE: C89 fmod() and x87 FPREM implicitly provide truncating float division, # so it is used here as the basis of float div(). div(x::T, y::T, r::RoundingMode) where {T<:AbstractFloat} = convert(T, round((x - rem(x, y, r)) / y)) + +# Vincent Lefèvre: "The Euclidean Division Implemented with a Floating-Point Division and a Floor" +# https://inria.hal.science/inria-00070403 +# Theorem 1 implies that the following are exact if eps(x/y) <= 1 +div(x::Float32, y::Float32, r::RoundingMode) = Float32(round(Float64(x) / Float64(y), r)) +div(x::Float16, y::Float16, r::RoundingMode) = Float16(round(Float32(x) / Float32(y), r)) diff --git a/test/numbers.jl b/test/numbers.jl index dc4f2cb613d77..0d8a8b1d867c0 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -1739,6 +1739,27 @@ end @test cld(-1.1, 0.1) == div(-1.1, 0.1, RoundUp) == ceil(big(-1.1)/big(0.1)) == -11.0 @test fld(-1.1, 0.1) == div(-1.1, 0.1, RoundDown) == floor(big(-1.1)/big(0.1)) == -12.0 end + @testset "issue #49450" begin + @test div(514, Float16(0.75)) === Float16(685) + @test fld(514, Float16(0.75)) === Float16(685) + @test cld(515, Float16(0.75)) === Float16(687) + + @test cld(1, Float16(0.000999)) === Float16(1001) + @test cld(2, Float16(0.001999)) === Float16(1001) + @test cld(3, Float16(0.002934)) === Float16(1023) + @test cld(4, Float16(0.003998)) === Float16(1001) + @test fld(5, Float16(0.004925)) === Float16(1015) + + @test div(4_194_307, Float32(0.75)) === Float32(5_592_409) + @test fld(4_194_307, Float32(0.75)) === Float32(5_592_409) + @test cld(4_194_308, Float32(0.75)) === Float32(5_592_411) + + @test fld(5, Float32(6.556511e-7)) === Float32(7_626_007) + @test fld(10, Float32(1.3113022e-6)) === Float32(7_626_007) + @test fld(11, Float32(1.4305115e-6)) === Float32(7_689_557) + @test cld(16, Float32(2.8014183e-6)) === Float32(5_711_393) + @test cld(17, Float32(2.2053719e-6)) === Float32(7_708_451) + end end @testset "return types" begin for T in (Int8,Int16,Int32,Int64,Int128, UInt8,UInt16,UInt32,UInt64,UInt128) From 6c28ba08d1350468e75788a59bf85d37f4b46532 Mon Sep 17 00:00:00 2001 From: Andy Dienes <51664769+adienes@users.noreply.github.com> Date: Sun, 5 Oct 2025 17:30:06 -0400 Subject: [PATCH 14/38] [NFC] add tests for typemin power of float (#59745) with backports, as suggested in https://github.com/JuliaLang/julia/issues/57463#issuecomment-2671090597. closes https://github.com/JuliaLang/julia/issues/57463 (cherry picked from commit 30c34ef51891e37a40e18fd584759cd2ee273ebf) --- test/math.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/math.jl b/test/math.jl index 9a8b3a16d8fb3..6ea0ce25f9a73 100644 --- a/test/math.jl +++ b/test/math.jl @@ -390,6 +390,15 @@ end @test isnan(exp(reinterpret(Float64, 0x7ffbb14880000000))) end +@testset "issue #57463" begin + for T in (Int16, Int32, Int64, Int128) + @test iszero(1.1^typemin(T)) + @test iszero(0.9^typemax(T)) + @test isinf(1.1^typemax(T)) + @test isinf(0.9^typemin(T)) + end +end + @testset "test abstractarray trig functions" begin TAA = rand(2,2) TAA = (TAA + TAA')/2. From 54a39e0cc784e01de974111097d34d4c428b3306 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 6 Oct 2025 17:08:38 -0400 Subject: [PATCH 15/38] [REPL] Do not corrupt input when using numbered_prompt! mode (#59730) Fix the unbounded possible corruption of the user input by reverting to a non-scoped begin instead of let. ```julia julia> ex = Base.remove_linenums!(:(using A; using B; using C)); REPL.Numbered.get_usings!([], ex) 2-element Vector{Any}: :(using A) :(using C) julia> ex quote using B end ``` (cherry picked from commit 05c07e18c3ef2226bc81cb8f22cd1dc2df1defc3) --- stdlib/REPL/src/REPL.jl | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index b0abb6e78f997..f82068c372b3b 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1845,25 +1845,10 @@ function repl_eval_counter(hp) end function out_transform(@nospecialize(x), n::Ref{Int}) - return Expr(:toplevel, get_usings!([], x)..., quote - let __temp_val_a72df459 = $x - $capture_result($n, __temp_val_a72df459) - __temp_val_a72df459 - end - end) -end - -function get_usings!(usings, ex) - ex isa Expr || return usings - # get all `using` and `import` statements which are at the top level - for (i, arg) in enumerate(ex.args) - if Base.isexpr(arg, :toplevel) - get_usings!(usings, arg) - elseif Base.isexpr(arg, [:using, :import]) - push!(usings, popat!(ex.args, i)) - end - end - return usings + return Expr(:block, # avoid line numbers or scope that would leak into the output and change the meaning of x + :(local __temp_val_a72df459 = $x), + Expr(:call, capture_result, n, :__temp_val_a72df459), + :__temp_val_a72df459) end function create_global_out!(mod) From 8c05c90e8617f4a6c5ab620eb5ffc2b45715defb Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 6 Oct 2025 17:15:54 -0400 Subject: [PATCH 16/38] array: avoid overallocation when inserting at beginning of empty arrays (#59717) Fixes a minor nuisance where length 0 would grow to 8 and length 7 would also grow to 8. Instead growing by a flat 3x even initially. Fixes #58640 (cherry picked from commit e16dfe2da4031dd3f67f444cd6ac849e55e75105) --- base/array.jl | 6 +++--- test/arrayops.jl | 6 ++++++ test/core.jl | 3 --- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/base/array.jl b/base/array.jl index f134ad2bc9ea5..7fe5ff626f448 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1053,14 +1053,14 @@ end # Specifically we are wasting ~10% of memory for small arrays # by not picking memory sizes that max out a GC pool function overallocation(maxsize) - maxsize < 8 && return 8; - # compute maxsize = maxsize + 4*maxsize^(7/8) + maxsize/8 + # compute maxsize = maxsize + 3*maxsize^(7/8) + maxsize/8 # for small n, we grow faster than O(n) # for large n, we grow at O(n/8) # and as we reach O(memory) for memory>>1MB, # this means we end by adding about 10% of memory each time + # most commonly, this will take steps of 0-3-9-34 or 1-4-16-66 or 2-8-33 exp2 = sizeof(maxsize) * 8 - Core.Intrinsics.ctlz_int(maxsize) - maxsize += (1 << div(exp2 * 7, 8)) * 4 + div(maxsize, 8) + maxsize += (1 << div(exp2 * 7, 8)) * 3 + div(maxsize, 8) return maxsize end diff --git a/test/arrayops.jl b/test/arrayops.jl index 836bbb1daec9a..d511988dd7a80 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -519,6 +519,12 @@ end v = empty!(collect(1:100)) pushfirst!(v, 1) @test length(v.ref.mem) == 100 + + # test that insert! at position 1 doesn't allocate for empty arrays with capacity (issue #58640) + v = empty!(Vector{Int}(undef, 5)) + insert!(v, 1, 10) + @test v == [10] + @test length(v.ref.mem) == 5 end @testset "popat!(::Vector, i, [default])" begin diff --git a/test/core.jl b/test/core.jl index 5c4ba7c9722b2..478499ae192db 100644 --- a/test/core.jl +++ b/test/core.jl @@ -6830,9 +6830,6 @@ Base._growat!(A, 4, 1) Base._growat!(A, 2, 3) @test getindex(A, 1) === 0x01 @test getindex(A, 2) === missing -@test getindex(A, 3) === missing -@test getindex(A, 4) === missing -@test getindex(A, 5) === missing @test getindex(A, 6) === 0x03 @test getindex(A, 7) === missing @test getindex(A, 8) === missing From ae6d2871d1c8e4dca453661d9d47d9d0e2616b61 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 7 Oct 2025 09:11:05 -0400 Subject: [PATCH 17/38] Revert "Restrict COFF to a single thread when symbol count is high (#50874)" (#59736) This reverts commit eb4416b16b8a865376da5c76451a4d60516e2c4a. As of LLVM 16, we automatically emit: ``` .drectve `-exclude-symbols:""` ``` which mitigates this issue where it is supported by the linker (GCC 11+ and LLD 14+ are tested working) PackageCompiler on Windows now ships GCC 14 (https://github.com/JuliaLang/PackageCompiler.jl/pull/1012), so we should no longer need this workaround that can make a 15-minute sysimage compilation take an hour+ (cherry picked from commit 1cba9c292c6bad490cca49cd52e333632a3fab91) --- src/aotcompile.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index c0a0bd2d14460..6009bd435534c 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1083,7 +1083,6 @@ static FunctionInfo getFunctionWeight(const Function &F) } struct ModuleInfo { - Triple triple; size_t globals; size_t funcs; size_t bbs; @@ -1094,7 +1093,6 @@ struct ModuleInfo { ModuleInfo compute_module_info(Module &M) { ModuleInfo info; - info.triple = Triple(M.getTargetTriple()); info.globals = 0; info.funcs = 0; info.bbs = 0; @@ -1895,12 +1893,6 @@ static unsigned compute_image_thread_count(const ModuleInfo &info) { #endif if (jl_is_timing_passes) // LLVM isn't thread safe when timing the passes https://github.com/llvm/llvm-project/issues/44417 return 1; - // COFF has limits on external symbols (even hidden) up to 65536. We reserve the last few - // for any of our other symbols that we insert during compilation. - if (info.triple.isOSBinFormatCOFF() && info.globals > 64000) { - LLVM_DEBUG(dbgs() << "COFF is restricted to a single thread for large images\n"); - return 1; - } // This is not overridable because empty modules do occasionally appear, but they'll be very small and thus exit early to // known easy behavior. Plus they really don't warrant multiple threads if (info.weight < 1000) { From fb4e89069777df2c071f8f731d95dddf7ee97924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Tue, 7 Oct 2025 17:38:19 +0200 Subject: [PATCH 18/38] Avoid method instance normalization for opaque closure methods (#59772) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #59222. This issue was caused by a mismatch with `jl_new_opaque_closure_from_code_info_in_world` filling in the unnormalized method instance, and `new_opaque_closure` getting its `spec_ptr` from the normalized one via `jl_compile_method_internal`. For example, `g(xs...)` specialized as `g(:a, :b)` resulted in a different method instance than the corresponding normalized one: ```julia Tuple{Tuple{typeof(Main.g)}, Symbol, Symbol} # unnormalized Tuple{Tuple{typeof(Main.g)}, Symbol, Vararg{Symbol}} # normalized ``` Here I chose to align on using the unnormalized one from the `CodeInfo` at `OpaqueClosure` construction (the fix being a single-line change in that case), but we could also choose the normalized one if that is deemed preferable, so long as we use the same when storing the inferred code and when retrieving the `spec_ptr`. --- 🤖 Generated with some assistance from Claude Code. (cherry picked from commit e8667fb089fda5ec09bff8d127826c9750a113d6) --- src/gf.c | 2 +- test/opaque_closure.jl | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/gf.c b/src/gf.c index b13830c1e1ba9..47db5474701ac 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3683,7 +3683,7 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_tupletype_t *ti, jl_s jl_method_instance_t *jl_normalize_to_compilable_mi(jl_method_instance_t *mi JL_PROPAGATES_ROOT) { jl_method_t *def = mi->def.method; - if (!jl_is_method(def) || !jl_is_datatype(mi->specTypes)) + if (!jl_is_method(def) || !jl_is_datatype(mi->specTypes) || def->is_for_opaque_closure) return mi; jl_value_t *compilationsig = jl_normalize_to_compilable_sig((jl_datatype_t*)mi->specTypes, mi->sparam_vals, def, 1); if (compilationsig == jl_nothing || jl_egal(compilationsig, mi->specTypes)) diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index 0dc2ed95b8872..2de5193ec4f35 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -297,6 +297,20 @@ let src = code_typed((Int,Int)) do x, y... @test oc(1,2) === (1,(2,)) @test_throws MethodError oc(1,2,3) end + + # with manually constructed IRCode, without round-trip to CodeInfo + f59222(xs...) = length(xs) + ir = Base.code_ircode_by_type(Tuple{typeof(f59222), Symbol, Symbol})[1][1] + ir.argtypes[1] = Tuple{} + let oc = OpaqueClosure(ir; isva=true) + @test oc(:a, :b) == 2 + end + ir = Base.code_ircode_by_type(Tuple{typeof(f59222), Symbol, Vararg{Symbol}})[1][1] + ir.argtypes[1] = Tuple{} + let oc = OpaqueClosure(ir; isva=true) + @test oc(:a) == 1 + @test oc(:a, :b, :c) == 3 + end end # Check for correct handling in case of broken return type. From 9de8e4649bb4dd2711fd228a31ef831c875353a5 Mon Sep 17 00:00:00 2001 From: Em Chu <61633163+mlechu@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:46:42 +0200 Subject: [PATCH 19/38] Allow globalref on lhs of op= (#59762) Fix https://github.com/JuliaLang/julia/issues/59008. `globalref`s are OK to assign to. This check isn't actually necessary since we check the lhs again when re-expanding the assignment, and most of the forms allowed through aren't valid with a non-dotted `op=`, but deleting it makes some error messages worse. (cherry picked from commit 5ddcea77fc468110f15b464b82b649042450ce64) --- src/julia-syntax.scm | 2 +- test/syntax.jl | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index e9ba0bb251082..f10d24acf7d81 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1895,7 +1895,7 @@ ,(expand-update-operator op op= (car e) rhs T)))) (else (if (and (pair? lhs) (eq? op= '=) - (not (memq (car lhs) '(|.| tuple vcat ncat typed_hcat typed_vcat typed_ncat)))) + (not (memq (car lhs) '(|.| globalref tuple vcat ncat typed_hcat typed_vcat typed_ncat)))) (error (string "invalid assignment location \"" (deparse lhs) "\""))) (expand-update-operator- op op= lhs rhs declT)))) diff --git a/test/syntax.jl b/test/syntax.jl index be01279b82359..833ecf232ed44 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2529,6 +2529,15 @@ end @test ncalls_in_lowered(:((.+)(a, b .- (.^)(c, 2))), GlobalRef(Base, :BroadcastFunction)) == 0 end +module M59008 # dotop with global LHS in macro +using Test +global a = 1 +macro counter() + :(a += 1) +end +@test @counter() === 2 === a +end + # issue #37656 @test :(if true 'a' else 1 end) == Expr(:if, true, quote 'a' end, quote 1 end) From 6adc7f3c4c0f32943271f72f00500aa464935c6a Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 9 Oct 2025 09:15:16 +0200 Subject: [PATCH 20/38] fix prefix IntrusiveLinkedList with Base in a REPL test util (#59782) (cherry picked from commit 9534147332939ec839f2f07d05acc76c198637ff) --- stdlib/REPL/test/repl.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 62cb6bbed8cb0..0ca3788d2b460 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -32,7 +32,7 @@ function kill_timer(delay) # **DON'T COPY ME.** # The correct way to handle timeouts is to close the handle: # e.g. `close(stdout_read); close(stdin_write)` - test_task.queue === nothing || Base.list_deletefirst!(test_task.queue::IntrusiveLinkedList{Task}, test_task) + test_task.queue === nothing || Base.list_deletefirst!(test_task.queue::Base.IntrusiveLinkedList{Task}, test_task) schedule(test_task, "hard kill repl test"; error=true) print(stderr, "WARNING: attempting hard kill of repl test after exceeding timeout\n") end From ba3b0932f1faaf12ccf00a6034aaf0e2bb54e3fb Mon Sep 17 00:00:00 2001 From: Sam Schweigel <33556084+xal-0@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:48:11 -0700 Subject: [PATCH 21/38] [REPL] Fix keyword arguments completions with do block (#59123) In `_complete_methods`, desugar the `:do` Expr into a call with a lambda in the first argument. Fixes #58833. (cherry picked from commit 50c895614653e56f30a7995265b93398f89f7da4) --- stdlib/REPL/src/REPLCompletions.jl | 9 +++++++++ stdlib/REPL/test/replcompletions.jl | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 03b4b4fec98f1..8c39231153fcc 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -698,6 +698,15 @@ code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState)) MAX_METHOD_COMPLETIONS::Int = 40 function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool) isempty(ex_org.args) && return 2, nothing, [], Set{Symbol}() + # Desugar do block call into call with lambda + if ex_org.head === :do && length(ex_org.args) >= 2 + ex_call = ex_org.args[1] + ex_args = [x for x in ex_call.args if !(x isa Expr && x.head === :parameters)] + ex_params = findfirst(x -> x isa Expr && x.head === :parameters, ex_call.args) + new_args = [ex_args[1], ex_org.args[end], ex_args[2:end]...] + ex_params !== nothing && push!(new_args, ex_call.args[ex_params]) + ex_org = Expr(:call, new_args...) + end funct = repl_eval_ex(ex_org.args[1], context_module) funct === nothing && return 2, nothing, [], Set{Symbol}() funct = CC.widenconst(funct) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index b3b141425e1a4..736f6fb405a41 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -139,6 +139,7 @@ let ex = kwtest4(a::SubString; x23, _something) = pass kwtest5(a::Int, b, x...; somekwarg, somekotherkwarg) = pass kwtest5(a::Char, b; xyz) = pass + kwtest6(f::Function, arg1; somekwarg) = pass const named = (; len2=3) const fmsoebelkv = (; len2=3) @@ -198,6 +199,8 @@ test_scomplete(s) = map_completion_text(@inferred(shell_completions(s, lastinde test_complete_pos(s) = map_completion_text(@inferred(completions(replace(s, '|' => ""), findfirst('|', s)-1))) test_complete_context(s, m=@__MODULE__; shift::Bool=true) = map_completion_text(@inferred(completions(s,lastindex(s), m, shift))) +test_complete_context_pos(s, m=@__MODULE__; shift::Bool=true) = + map_completion_text(@inferred(completions(replace(s, '|' => ""), findfirst('|', s)-1, m, shift))) test_complete_foo(s; shift::Bool=true) = test_complete_context(s, Main.CompletionFoo; shift) test_complete_noshift(s) = map_completion_text(@inferred(completions(s, lastindex(s), Main, false))) @@ -2720,3 +2723,10 @@ let s = "foo58296(findfi" @test "findfirst" in c @test r == 10:15 end + +# #58833 - Autocompletion of keyword arguments with do-blocks is broken +let s = "kwtest6(123; som|) do x; x + 3 end" + c, r = test_complete_context_pos(s, Main.CompletionFoo) + @test "somekwarg=" in c + @test r == 14:16 +end From 028c977b1c99e09c502d6fbd743d1c182795e311 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 19 Sep 2025 09:44:55 -0400 Subject: [PATCH 22/38] specialize show of `:` to make `reshape` trimmable (#59598) I also noticed that the standard Base.show_type_name appears to be trimmable so we don't need the override. We might still want it for code size, but probably not a big deal. (cherry picked from commit 12f7bb52e5714c577189665a05606e3764f333cf) --- base/essentials.jl | 4 ++++ contrib/juliac/juliac-trim-base.jl | 1 - test/trimming/trimmability.jl | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/base/essentials.jl b/base/essentials.jl index 412629411e36a..66400d88fd1ab 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -1009,6 +1009,10 @@ struct Colon <: Function end const (:) = Colon() +function show(io::IO, ::Colon) + show_type_name(io, Colon.name) + print(io, "()") +end """ Val(c) diff --git a/contrib/juliac/juliac-trim-base.jl b/contrib/juliac/juliac-trim-base.jl index 244a643a1a76f..6300633eef8fe 100644 --- a/contrib/juliac/juliac-trim-base.jl +++ b/contrib/juliac/juliac-trim-base.jl @@ -77,7 +77,6 @@ end print(io, T.var.name) end end - show_type_name(io::IO, tn::Core.TypeName) = print(io, tn.name) mapreduce(f::F, op::F2, A::AbstractArrayOrBroadcasted; dims=:, init=_InitialValue()) where {F, F2} = _mapreduce_dim(f, op, init, A, dims) diff --git a/test/trimming/trimmability.jl b/test/trimming/trimmability.jl index acba8244534ef..403f4f6c3a798 100644 --- a/test/trimming/trimmability.jl +++ b/test/trimming/trimmability.jl @@ -50,5 +50,7 @@ function @main(args::Vector{String})::Cint catch end + Base.donotdelete(reshape([1,2,3],:,1,1)) + return 0 end From ffaef872d31002e5988362e995ae40caf3b1e523 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 1 Oct 2025 07:45:44 +0200 Subject: [PATCH 23/38] lowering: increment world age after toplevel expressions (#59703) When a macro expands to `:toplevel` expression, it seems reasonable (as would be if the expression were not wrapped in `:toplevel`) that any contained struct definitions be available thereafter. For example, this applies to `@enum`, which countrly causes world age erros when attempting to define a method on an enum within the same expression. Fix this by having lowering insert and explicit `latestworld` after toplevel. Fixes #59429 Written by Claude (cherry picked from commit 0c8768ff321c83245475503ca8a2fe2a7917352b) --- src/julia-syntax.scm | 1 + test/worlds.jl | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index f10d24acf7d81..aad8105b34308 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -5032,6 +5032,7 @@ f(x) = yt(x) (check-top-level e) (let ((val (make-ssavalue))) (emit `(= ,val ,e)) + (emit `(latestworld)) (if tail (emit-return tail val)) val)) diff --git a/test/worlds.jl b/test/worlds.jl index 4a2836164e2c6..2bd1bcab353c6 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -589,3 +589,21 @@ module C57316; import ..X57316.Y57316 as Z, .Z.Y57316 as W; end @test !isdefined(B57316, :X57316) @test !isdefined(C57316, :X57316) @test !isdefined(C57316, :Y57316) + +# issue #59429 - world age semantics with toplevel in macros +module M59429 +using Test +macro new_enum(T::Symbol, args...) + esc(quote + @enum $T $(args...) + function Base.hash(x::$T, h::UInt) + rand(UInt) + end + end) +end + +@new_enum Foo59429 bar59429 baz59429 + +# Test that the hash function works without world age issues +@test hash(bar59429, UInt(0)) isa UInt +end From 630774dfe458135257eb29767108aafd91ba4fd0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 2 Oct 2025 13:57:11 -0400 Subject: [PATCH 24/38] fix performance of ProductIterator for non-concrete iterators (#59711) This uses inferred element types to make the iterator state type-stable, enabling inlining, which can enable the runtime to see the real types. This is essentially the transform that we want inference to infer and codegen to implement, except made explicit so that we can rely on it actually happening. Fix #56607 ``` julia> @btime f() 574.500 ns (2 allocations: 272 bytes) # non-const op 253.878 ns (2 allocations: 272 bytes) # const op ``` (cherry picked from commit 498d982fd5324672d02880d06e89b05692d4285d) --- base/iterators.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/base/iterators.jl b/base/iterators.jl index f7ae8138f6856..47e365f5c7a86 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -16,7 +16,7 @@ using .Base: (:), |, +, -, *, !==, !, ==, !=, <=, <, >, >=, =>, missing, any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex, tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape, LazyString, - afoldl + afoldl, @default_eltype using Core using Core: @doc @@ -1140,6 +1140,8 @@ end next === nothing && return nothing restnext = _piterate(rest...) restnext === nothing && return nothing + VS = @default_eltype(iter1) + next = Pair{VS, typeof(next[2])}(next[1], next[2]) return (next, restnext...) end @inline function iterate(P::ProductIterator) @@ -1152,8 +1154,8 @@ end @inline _piterate1(::Tuple{}, ::Tuple{}) = nothing @inline function _piterate1(iters, states) iter1 = first(iters) - next = iterate(iter1, first(states)[2]) - restnext = tail(states) + state1, restnext... = states + next = iterate(iter1, state1[2]) if next === nothing isdone(iter1) === true && return nothing restnext = _piterate1(tail(iters), restnext) @@ -1161,6 +1163,7 @@ end next = iterate(iter1) next === nothing && return nothing end + next = Pair{fieldtype(typeof(state1), 1), typeof(next[2])}(next[1], next[2]) return (next, restnext...) end @inline function iterate(P::ProductIterator, states) From b8469627dc21cd6bc5248ec98da59151faac32fb Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Sun, 5 Oct 2025 18:23:14 -0700 Subject: [PATCH 25/38] [libblastrampoline] Bump to v5.15.0 (#59748) (cherry picked from commit b76c189614499464fc03af6417c9c28ffea89908) --- deps/blastrampoline.version | 6 +- deps/checksums/blastrampoline | 76 +++++++++++------------ stdlib/libblastrampoline_jll/Project.toml | 2 +- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/deps/blastrampoline.version b/deps/blastrampoline.version index bb711cfcd67ec..4f9dc0aff0741 100644 --- a/deps/blastrampoline.version +++ b/deps/blastrampoline.version @@ -4,6 +4,6 @@ BLASTRAMPOLINE_JLL_NAME := libblastrampoline ## source build -BLASTRAMPOLINE_VER := 5.13.1 -BLASTRAMPOLINE_BRANCH=v5.13.1 -BLASTRAMPOLINE_SHA1=f26278e83ddc9035ae7695da597f1a5b26a4c62b +BLASTRAMPOLINE_VER := 5.15.0 +BLASTRAMPOLINE_BRANCH=v5.15.0 +BLASTRAMPOLINE_SHA1=072b5f67895bec0b92f8c83194567c1c48e9833d diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index eef1653cf353a..d4c42a32024f9 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -1,38 +1,38 @@ -blastrampoline-f26278e83ddc9035ae7695da597f1a5b26a4c62b.tar.gz/md5/855b7723a6e9eb8885876eb675d48329 -blastrampoline-f26278e83ddc9035ae7695da597f1a5b26a4c62b.tar.gz/sha512/29cbd060c8f5eb17ef486d0a10ee4b221eeceec3a2ab0f9f98f60880f3d19a2247d93ac0dc0d32ec568ef876acd30f6c0642aaf704757580c2e17884e425607f -libblastrampoline.v5.13.1+1.aarch64-apple-darwin.tar.gz/md5/d8dc0f092f86b379b2fb9da97382be70 -libblastrampoline.v5.13.1+1.aarch64-apple-darwin.tar.gz/sha512/d9fc0439565afaabe53f56f64c20aeddb846c991dafeafdef6c2369bd7a359c1a6b49cdf8d63eaae2730a336509854b5c306e630eb520445712efc4e41c0263e -libblastrampoline.v5.13.1+1.aarch64-linux-gnu.tar.gz/md5/c181e51a6ca4cde0da3d036d561e24dc -libblastrampoline.v5.13.1+1.aarch64-linux-gnu.tar.gz/sha512/fe4a86bb4c94ef86c2307adad528bb58d0508a33c194c64190fffe7902f5b915592567d9e0cc35414633c5ab9067def2fa20cf669a2f4309265744180a5ec51a -libblastrampoline.v5.13.1+1.aarch64-linux-musl.tar.gz/md5/6f9eb8d73a0e61f3a2b97dba7105086e -libblastrampoline.v5.13.1+1.aarch64-linux-musl.tar.gz/sha512/9c3db080155729a91b5dd47df91d3852539aefc331d4dc51167fccaf3b01e601b36911ec259c53e211fe192c108e839a1f14b837009fa4f7d88ed82d658f80ff -libblastrampoline.v5.13.1+1.aarch64-unknown-freebsd.tar.gz/md5/68f65db9da9938929d510eea3540335b -libblastrampoline.v5.13.1+1.aarch64-unknown-freebsd.tar.gz/sha512/2fc7b375a751f3bb201504e0417828602fe014a2c8626137779c09ca7264ac6d39d44db0d1d32e0dc506284f56b49e23791922b0cc1237021473fb505fbf06bd -libblastrampoline.v5.13.1+1.armv6l-linux-gnueabihf.tar.gz/md5/a377fa4e5751fbeb3c42f319cb6341de -libblastrampoline.v5.13.1+1.armv6l-linux-gnueabihf.tar.gz/sha512/9ddb1e2f4daab45d65b66dafc00df6ca7f788cb919cd6699c4aa0deca3e99a86d9ced10c3741610a6e480093d483e8a02c1d9165f91a7179632c1e2ae1abcfb7 -libblastrampoline.v5.13.1+1.armv6l-linux-musleabihf.tar.gz/md5/42c841baa05f80f17ea1b1d4f3405bef -libblastrampoline.v5.13.1+1.armv6l-linux-musleabihf.tar.gz/sha512/0c3ed42bd48f8f1ee9b1dc18faa7afa6e2fb27cffc59b9a420e29b5e6cdf8fb3bde36b82f3086075f8f7f329614aeb91ca5f173b1683e30e9530076f341ea2e0 -libblastrampoline.v5.13.1+1.armv7l-linux-gnueabihf.tar.gz/md5/61e515ec1223c99705175a26e6fbaf87 -libblastrampoline.v5.13.1+1.armv7l-linux-gnueabihf.tar.gz/sha512/92260dcc563ece74719f21921a7cb51266884ed01b50c97fa997b4a98737e900ec9eaa8012d2c4c67ab479c4080bd1cf2708612eaaaddbba28e4f9147f3708ea -libblastrampoline.v5.13.1+1.armv7l-linux-musleabihf.tar.gz/md5/d45816d705dd46572d85105567bc060e -libblastrampoline.v5.13.1+1.armv7l-linux-musleabihf.tar.gz/sha512/45cba07050b818cd85c67acdfc29515e1fe416eb4e0c219171f2c0c026f7412903c3a9367d48258259a16e89f36c1e8f9fa054e455759720f1c6c5e8e27be476 -libblastrampoline.v5.13.1+1.i686-linux-gnu.tar.gz/md5/c8d3fd5f314353133934396361857c92 -libblastrampoline.v5.13.1+1.i686-linux-gnu.tar.gz/sha512/a949b3c0655ad9d6f8d53fd8a3f0b4ab504046e49a373039defc94e832b7faf90c77520f3912c4d6db8b0829951d85b4fc2a4021b3d8bb2c399d1ad04ce77ab0 -libblastrampoline.v5.13.1+1.i686-linux-musl.tar.gz/md5/a7bbd2233366d180ce8aa61fd3568c11 -libblastrampoline.v5.13.1+1.i686-linux-musl.tar.gz/sha512/e78cbef5b3bcfa93a86e14eebf0d704a94ac7b1f5c7030706d1f4a960de888c42e3daddb65395c7102e08dfd444efbfb40273e58a5f1de199d44ad55fd3ae658 -libblastrampoline.v5.13.1+1.i686-w64-mingw32.tar.gz/md5/35bf0e9d253d3bbd188c1feba5635e1d -libblastrampoline.v5.13.1+1.i686-w64-mingw32.tar.gz/sha512/6b28e83265b6bd7337ccf7784c74f0490fcf5089bb9c21e2333dace8801f94f09b46ef683d2bd4f2c885b64a26ec4e8755bab21a6e81098e287c15de27d66293 -libblastrampoline.v5.13.1+1.powerpc64le-linux-gnu.tar.gz/md5/8be947c20f7d35ec22247f9a11ccce43 -libblastrampoline.v5.13.1+1.powerpc64le-linux-gnu.tar.gz/sha512/56f4246f96d2f49b03f5e5f3b996660a48d50b3784f89df7cd1dc52bab6efea0c120a65015040041a51d18fc6f361f89486f77317d771fbf588a1ba7565d77a2 -libblastrampoline.v5.13.1+1.riscv64-linux-gnu.tar.gz/md5/85e1f70a3235097158b4884a58a58154 -libblastrampoline.v5.13.1+1.riscv64-linux-gnu.tar.gz/sha512/11f1f5c2a409dbdab11d6bc968610b5700e9b0cb95094e348fe43ddca5586eda47bda1c382fb1f4b5a15aa741a6fc2b31f58f9b08bfe46631b5471e864bc009b -libblastrampoline.v5.13.1+1.x86_64-apple-darwin.tar.gz/md5/c6756ca8b6778ce2a4a440f63355c32e -libblastrampoline.v5.13.1+1.x86_64-apple-darwin.tar.gz/sha512/895d9bba75a9a0861809dca48b3dae7b5ffc5d866a518729ffd52f70fa1742a41a4b8b4e03bb354cba12d9ad11a33f3f112fa69a30ab3f945a9dede0d59d92b3 -libblastrampoline.v5.13.1+1.x86_64-linux-gnu.tar.gz/md5/1326a406aa98b6045f7459d7fb237894 -libblastrampoline.v5.13.1+1.x86_64-linux-gnu.tar.gz/sha512/4965baa1de5532425ea57b8100e369cf44b55963340cd144c0359f845560f27a1bea1597e4c72ec541917f71aaff8a4863f47d01a095c2e761a68212bfb08d1e -libblastrampoline.v5.13.1+1.x86_64-linux-musl.tar.gz/md5/5103983b7fecc7b87f495cd3b6c4d7a5 -libblastrampoline.v5.13.1+1.x86_64-linux-musl.tar.gz/sha512/f3243d84a0a0a191abad9e3850c37be78892eb5905b63b47bfb3e5a4148e0dae672ee72d311c5c764ad0fffe57d39c10dfd2086466efd76b5030118941d36a00 -libblastrampoline.v5.13.1+1.x86_64-unknown-freebsd.tar.gz/md5/a001ecd07b5178ce724a4f78996dc43e -libblastrampoline.v5.13.1+1.x86_64-unknown-freebsd.tar.gz/sha512/508866d54a9a49df2ef7eaa5d807173016c6dfaec59c4c89d5b37cd3faa7384302d2d4d39aca1975d79a948414657b7ec048a3ebdf6bf5c938037aa89303013a -libblastrampoline.v5.13.1+1.x86_64-w64-mingw32.tar.gz/md5/b7c51c8e1a11accc5c5097042da646d9 -libblastrampoline.v5.13.1+1.x86_64-w64-mingw32.tar.gz/sha512/24bc32f0dabc886898ca0e5fd2b3d7250fc67f7bd962ec2aae8c946f405826967a8b8623e524bf22c201bc7e6c55d16c4a5fb2a2250cbbefaf73e383fc080aba +blastrampoline-072b5f67895bec0b92f8c83194567c1c48e9833d.tar.gz/md5/aa445967f00e19fef56c7072c72a6832 +blastrampoline-072b5f67895bec0b92f8c83194567c1c48e9833d.tar.gz/sha512/3185a39b553c9cb9f6171755462f52f1d164ec3d7ad83274cb4557e6cf5d6cef3bd7447f85c27ebcb3b1c76da0a547f978099bd554688bc769dd8ded654a2ad2 +libblastrampoline.v5.15.0+0.aarch64-apple-darwin.tar.gz/md5/d816e41ff36472e67d9d815f8db5b75d +libblastrampoline.v5.15.0+0.aarch64-apple-darwin.tar.gz/sha512/b4c844f6f7f4d917629fb27dcaabb70f46f362b2fc0b4686df56c39e46251328ee323d01e09609988ebfa76d123eac033641fae14f4a48bfbe4b20b77ef745a9 +libblastrampoline.v5.15.0+0.aarch64-linux-gnu.tar.gz/md5/375e826ab5bdaf34b71c61eafc9deac8 +libblastrampoline.v5.15.0+0.aarch64-linux-gnu.tar.gz/sha512/2f8503aea3454136800f51fd3edf3b26efd1bab194d33fc9a302b00cff00b237462bd2a9409016656ac8e9904244004f4725d3388f29da4512b962f018af0609 +libblastrampoline.v5.15.0+0.aarch64-linux-musl.tar.gz/md5/f1c90626a26885bac905ad6ed9c26b46 +libblastrampoline.v5.15.0+0.aarch64-linux-musl.tar.gz/sha512/0cd900176f4b5e628b54fdfb4f9504fb6b352f92b97a1b3cb102343c94ffddb37b564a5d682d36631af3b03f6b4a49ad7e33f473f8450d95f74e3f986f39b780 +libblastrampoline.v5.15.0+0.aarch64-unknown-freebsd.tar.gz/md5/b9025566d8ba21fa10f55065bd9e3795 +libblastrampoline.v5.15.0+0.aarch64-unknown-freebsd.tar.gz/sha512/97ef1dd8116c4fc89cfa35d8f8674885e3b425d30cd45ab727ae98cf5f9b2b481352217bf2a01e6d3e0f5a95700c1771c15db4dcd73312c6d3286895c04c6f6b +libblastrampoline.v5.15.0+0.armv6l-linux-gnueabihf.tar.gz/md5/24e043853904f395436db5045add7e2c +libblastrampoline.v5.15.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/8948720cc3cf46ec0c7489ea90e8defd24eaebaaf0c9a98a0de13bdfffdd732311f1e1608078573a30e3170ad543eb0caf31a500255827482af526957bccacbb +libblastrampoline.v5.15.0+0.armv6l-linux-musleabihf.tar.gz/md5/3cbbb1c09d1731e9dd29dd9350110aae +libblastrampoline.v5.15.0+0.armv6l-linux-musleabihf.tar.gz/sha512/f1806cb4a1b59a78027291ca01481f2d6acf74340c3bc9534af93c3874aa6774dd4c6b5cf7b1bf98b028602d74b9301b0250fad9d2c2e0e02188c604f63e1559 +libblastrampoline.v5.15.0+0.armv7l-linux-gnueabihf.tar.gz/md5/da36360de121353b213c488158c264ed +libblastrampoline.v5.15.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/25883ac30da9a14816e295545a7f8135c20adc3e2e3ab7b374a8796bcfef31b57ab1bc8f4788aef53e2f02d2b795ee89181f143b30132a8af531f019a0219a72 +libblastrampoline.v5.15.0+0.armv7l-linux-musleabihf.tar.gz/md5/7062e23f7549d790cb2c6ff5ef96aff6 +libblastrampoline.v5.15.0+0.armv7l-linux-musleabihf.tar.gz/sha512/06f3da585d720801c840ded52cb29b25364e892b1c1b895ee49a37bde721063757da0f7da4336c9dd518b6269f3c47b2b109d6d37d0a578d805fffe814c239f1 +libblastrampoline.v5.15.0+0.i686-linux-gnu.tar.gz/md5/fe7d22677ec883261429c0201210e35c +libblastrampoline.v5.15.0+0.i686-linux-gnu.tar.gz/sha512/b99713ae9a00bffdca3001a1a8a84b99e1b3194a755d1a52eb7a73cd8d0571adfb8746b182cb62b3a3e8f7584ae49ece89d503af032b0485b3e6e832bd17de83 +libblastrampoline.v5.15.0+0.i686-linux-musl.tar.gz/md5/3a54aba695352019a507a44d49d4b73a +libblastrampoline.v5.15.0+0.i686-linux-musl.tar.gz/sha512/9a3baeb75551ec14b58eb370730a50bd0023aa63610d47e492d08dd4d54ff8afca4b8c9b34501ba5eaf2dab02922b88cfeb6ea9383b58783337999eb966f2f0e +libblastrampoline.v5.15.0+0.i686-w64-mingw32.tar.gz/md5/f8424745959e7bce3a804ccee41b30bf +libblastrampoline.v5.15.0+0.i686-w64-mingw32.tar.gz/sha512/1b0e877098e666a0ffa7d057f60a1063d710d598ee8a53094815237d98119f5101c4e643fce7190ce22ce3787249920ab7dc6ff2dc1e3088cd95779b19e17bf7 +libblastrampoline.v5.15.0+0.powerpc64le-linux-gnu.tar.gz/md5/927bb0e1a4b5e3ceb225bd8c0f75ec77 +libblastrampoline.v5.15.0+0.powerpc64le-linux-gnu.tar.gz/sha512/a414a84fb45d2666dfd72cd9d6e013481e95c8d6d7cae40939bef779c14fe50d0a8473ddeb7d6060fcc8c4531cd34bd06ff6bbc7a44a6b106b8d0e2c8d62bdbd +libblastrampoline.v5.15.0+0.riscv64-linux-gnu.tar.gz/md5/3369a45ed2b52482ca2a83ddb515e08b +libblastrampoline.v5.15.0+0.riscv64-linux-gnu.tar.gz/sha512/68ceb3dea1ebb1689892f05b6c7ab5a088b5249cab5a70bba8164d6290b74634deaa9aa3cfa5c84de836313ac0569542280a48623f95b3744746b2f8984c1d37 +libblastrampoline.v5.15.0+0.x86_64-apple-darwin.tar.gz/md5/07e63e6ef01f2bde3f80b724170991fb +libblastrampoline.v5.15.0+0.x86_64-apple-darwin.tar.gz/sha512/361689ede0771148e353a4b911fef76839754ddf8f735a60d597b8c523569e206b5daede3885e3ec1302e9e4ca5488b6ea203bd3ed384ccd607934d813b27ba7 +libblastrampoline.v5.15.0+0.x86_64-linux-gnu.tar.gz/md5/e7bcbd90bb3f7d4f197269b3f64cfc4c +libblastrampoline.v5.15.0+0.x86_64-linux-gnu.tar.gz/sha512/5a55c9b10460bf9472c18fc381cf941d10443b9a089c7e774b4e9456c11a100dd61788c9c5139f77504f0b0bce7e4567b1e002386c402695c968632280b4d316 +libblastrampoline.v5.15.0+0.x86_64-linux-musl.tar.gz/md5/bb8bacb2748137adfb0aa83834bc72d0 +libblastrampoline.v5.15.0+0.x86_64-linux-musl.tar.gz/sha512/5d683868e377d2aee34fb2699f8c3733714d2c7cf0ac7ddb1d35a78460015d784b9e00f8d30e8bc6ee570a47de6baaa7f7efd90790588cc2953cd19897c56a92 +libblastrampoline.v5.15.0+0.x86_64-unknown-freebsd.tar.gz/md5/60e6833b6e7656db680032c0d1b5d5b7 +libblastrampoline.v5.15.0+0.x86_64-unknown-freebsd.tar.gz/sha512/ca155fcc0b66889fe608291738e5e4c8506d28c42e03a948e4e56d441949be1d1dcbd201ba0f2536c862da3d8fdf7c7ee77a9b0b715dc1b9893767510ff0ffb9 +libblastrampoline.v5.15.0+0.x86_64-w64-mingw32.tar.gz/md5/950ec7ca747e8f13cfb7bd1967af5db6 +libblastrampoline.v5.15.0+0.x86_64-w64-mingw32.tar.gz/sha512/6dbcde726cf3b1e2ecc91913b3ff954881706381fcd97907173fd8c21aff6708a9f6998f510356c7f284f826ee67264509831752399efe7163a4baaeb071c3c8 diff --git a/stdlib/libblastrampoline_jll/Project.toml b/stdlib/libblastrampoline_jll/Project.toml index 08e75ea061293..1356d2d6f27af 100644 --- a/stdlib/libblastrampoline_jll/Project.toml +++ b/stdlib/libblastrampoline_jll/Project.toml @@ -1,6 +1,6 @@ name = "libblastrampoline_jll" uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.13.1+1" +version = "5.15.0+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 3eb52dfbbe7a25bf0d1b62632d4eee52acf4769d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 6 Oct 2025 17:08:13 -0400 Subject: [PATCH 26/38] [InteractiveUtils] specify code_warntype vararg length (#59726) Avoids a stack overflow when mistyping arguments, allowing for a better MethodError. Issue introduced by #51109 Fixes #57597 (cherry picked from commit 6e697c39a3d1a0e3493f99d70d9b636e135f6473) --- stdlib/InteractiveUtils/src/codeview.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/InteractiveUtils/src/codeview.jl b/stdlib/InteractiveUtils/src/codeview.jl index 1aa83a19285ff..a93a00dd4f0a6 100644 --- a/stdlib/InteractiveUtils/src/codeview.jl +++ b/stdlib/InteractiveUtils/src/codeview.jl @@ -176,7 +176,8 @@ function code_warntype(io::IO, @nospecialize(f), @nospecialize(tt=Base.default_t end nothing end -code_warntype(args...; kwargs...) = (@nospecialize; code_warntype(stdout, args...; kwargs...)) +code_warntype(f; kwargs...) = (@nospecialize; code_warntype(stdout, f; kwargs...)) +code_warntype(f, argtypes; kwargs...) = (@nospecialize; code_warntype(stdout, f, argtypes; kwargs...)) using Base: CodegenParams From 1e2f8a51414b3ed1d41ba8ccb79116a0cae9f18d Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 8 Oct 2025 11:38:01 -0400 Subject: [PATCH 27/38] Align interpreter and codegen error behavior of setglobal! and friends (#59766) Currently this is an ErrorException in the runtime/interpreter, but a TypeError in codegen. This is not permitted - which error is thrown is semantically observable and codegen is not permitted to change it. Worse, inference is also inconsistent about whether this is TypeError or ErrorException, so this could actually lead to type confusion and crashes. Fix all that by having the runtime also emit a TypeError here. However, in order to not lose the binding name in the error message, adjust the TypeError context field to permit a binding. (cherry picked from commit 17e0df567b259c769f951d176aa44da90fe46120) --- Compiler/src/abstractinterpretation.jl | 11 ++++++----- Compiler/test/inference.jl | 2 +- base/boot.jl | 2 +- base/errorshow.jl | 3 +++ src/codegen.cpp | 3 +-- src/datatype.c | 13 ++++++------- src/genericmemory.c | 2 +- src/julia.h | 6 +++++- src/julia_internal.h | 3 ++- src/module.c | 18 ++++++++---------- src/rtutils.c | 10 ++++++++++ test/core.jl | 5 ++++- 12 files changed, 48 insertions(+), 30 deletions(-) diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index 69ec3c27de903..10cbca8c0c3d8 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -2488,7 +2488,7 @@ function abstract_eval_setglobal!(interp::AbstractInterpreter, sv::AbsIntState, (rt, exct) = global_assignment_rt_exct(interp, sv, saw_latestworld, gr, v) return CallMeta(rt, exct, Effects(setglobal!_effects, nothrow=exct===Bottom), GlobalAccessInfo(convert(Core.Binding, gr))) end - return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + return CallMeta(Union{}, Union{TypeError, ErrorException}, EFFECTS_THROWS, NoCallInfo()) end ⊑ = partialorder(typeinf_lattice(interp)) if !(hasintersect(widenconst(M), Module) && hasintersect(widenconst(s), Symbol)) @@ -3732,7 +3732,7 @@ end function global_assignment_rt_exct(interp::AbstractInterpreter, sv::AbsIntState, saw_latestworld::Bool, g::GlobalRef, @nospecialize(newty)) if saw_latestworld - return Pair{Any,Any}(newty, ErrorException) + return Pair{Any,Any}(newty, Union{TypeError, ErrorException}) end newty′ = RefValue{Any}(newty) (valid_worlds, ret) = scan_partitions(interp, g, sv.world) do interp::AbstractInterpreter, ::Core.Binding, partition::Core.BindingPartition @@ -3747,15 +3747,16 @@ function global_assignment_binding_rt_exct(interp::AbstractInterpreter, partitio if is_some_guard(kind) return Pair{Any,Any}(newty, ErrorException) elseif is_some_const_binding(kind) || is_some_imported(kind) - return Pair{Any,Any}(Bottom, ErrorException) + # N.B.: Backdating should not improve inference in an earlier world + return Pair{Any,Any}(kind == PARTITION_KIND_BACKDATED_CONST ? newty : Bottom, ErrorException) end ty = kind == PARTITION_KIND_DECLARED ? Any : partition_restriction(partition) wnewty = widenconst(newty) if !hasintersect(wnewty, ty) - return Pair{Any,Any}(Bottom, ErrorException) + return Pair{Any,Any}(Bottom, TypeError) elseif !(wnewty <: ty) retty = tmeet(typeinf_lattice(interp), newty, ty) - return Pair{Any,Any}(retty, ErrorException) + return Pair{Any,Any}(retty, TypeError) end return Pair{Any,Any}(newty, Bottom) end diff --git a/Compiler/test/inference.jl b/Compiler/test/inference.jl index a4294e2e45f6b..6b1b964805149 100644 --- a/Compiler/test/inference.jl +++ b/Compiler/test/inference.jl @@ -6235,7 +6235,7 @@ g57292(xs::String...) = getfield(("abc",), 1, :not_atomic, xs...) global invalid_setglobal!_exct_modeling::Int @test Base.infer_exception_type((Float64,)) do x setglobal!(@__MODULE__, :invalid_setglobal!_exct_modeling, x) -end == ErrorException +end == TypeError # Issue #58257 - Hang in inference during BindingPartition resolution module A58257 diff --git a/base/boot.jl b/base/boot.jl index 32975e96af583..e1af7383fbb81 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -410,7 +410,7 @@ struct TypeError <: Exception # `context` optionally adds extra detail, e.g. the name of the type parameter # that got a bad value. func::Symbol - context::Union{AbstractString,Symbol} + context::Union{AbstractString,GlobalRef,Symbol} expected::Type got TypeError(func, context, @nospecialize(expected::Type), @nospecialize(got)) = diff --git a/base/errorshow.jl b/base/errorshow.jl index 602e6c55cb388..d8d94d6f1fb94 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -91,6 +91,9 @@ function showerror(io::IO, ex::TypeError) end if ex.context == "" ctx = "in $(ex.func)" + elseif isa(ex.context, Core.GlobalRef) + gr = ex.context + ctx = "in $(ex.func) of global binding `$(gr.mod).$(gr.name)`" elseif ex.func === :var"keyword argument" ctx = "in keyword argument $(ex.context)" else diff --git a/src/codegen.cpp b/src/codegen.cpp index 3189171e9b30a..8f0644c0cff7f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3251,8 +3251,7 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s if (ty != nullptr) { const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!"; if (!ismodifyglobal) { - // TODO: use typeassert in jl_check_binding_assign_value too - emit_typecheck(ctx, rval, ty, "typeassert"); + emit_typecheck(ctx, rval, ty, fname.c_str()); rval = update_julia_type(ctx, rval, ty); if (rval.typ == jl_bottom_type) return jl_cgval_t(); diff --git a/src/datatype.c b/src/datatype.c index 5c771ed2a003a..4cf2c6d780bbd 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -1981,11 +1981,11 @@ jl_value_t *swap_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_value_ } } -inline jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, int isatomic, jl_module_t *mod, jl_sym_t *name) +inline jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, int isatomic, jl_binding_t *b, jl_module_t *mod, jl_sym_t *name) { jl_value_t *r = isatomic ? jl_atomic_load(p) : jl_atomic_load_relaxed(p); if (__unlikely(r == NULL)) { - if (mod && name) + if (b) jl_undefined_var_error(name, (jl_value_t*)mod); jl_throw(jl_undefref_exception); } @@ -1996,11 +1996,10 @@ inline jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_valu args[1] = rhs; jl_value_t *y = jl_apply_generic(op, args, 2); args[1] = y; - if (!jl_isa(y, ty)) { - if (mod && name) - jl_errorf("cannot assign an incompatible value to the global %s.%s.", jl_symbol_name(mod->name), jl_symbol_name(name)); + if (b) + jl_check_binding_assign_value(b, mod, name, y, "modifyglobal!"); + else if (!jl_isa(y, ty)) jl_type_error(jl_is_genericmemory(parent) ? "memoryrefmodify!" : "modifyfield!", ty, y); - } if (isatomic ? jl_atomic_cmpswap(p, &r, y) : jl_atomic_cmpswap_release(p, &r, y)) { jl_gc_wb(parent, y); break; @@ -2114,7 +2113,7 @@ jl_value_t *modify_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_valu jl_value_t *ty = jl_field_type_concrete(st, i); char *p = (char*)v + offs; if (jl_field_isptr(st, i)) { - return modify_value(ty, (_Atomic(jl_value_t*)*)p, v, op, rhs, isatomic, NULL, NULL); + return modify_value(ty, (_Atomic(jl_value_t*)*)p, v, op, rhs, isatomic, NULL, NULL, NULL); } else { uint8_t *psel = jl_is_uniontype(ty) ? (uint8_t*)&p[jl_field_size(st, i) - 1] : NULL; diff --git a/src/genericmemory.c b/src/genericmemory.c index e35ed5e5ac5b3..32adc1add3d06 100644 --- a/src/genericmemory.c +++ b/src/genericmemory.c @@ -543,7 +543,7 @@ JL_DLLEXPORT jl_value_t *jl_memoryrefmodify(jl_genericmemoryref_t m, jl_value_t char *data = (char*)m.ptr_or_offset; if (layout->flags.arrayelem_isboxed) { assert(data - (char*)m.mem->ptr < sizeof(jl_value_t*) * m.mem->length); - return modify_value(eltype, (_Atomic(jl_value_t*)*)data, owner, op, rhs, isatomic, NULL, NULL); + return modify_value(eltype, (_Atomic(jl_value_t*)*)data, owner, op, rhs, isatomic, NULL, NULL, NULL); } size_t fsz = layout->size; uint8_t *psel = NULL; diff --git a/src/julia.h b/src/julia.h index e8322119299a3..7cda8fd456f74 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2121,7 +2121,7 @@ JL_DLLEXPORT jl_value_t *jl_get_module_binding_or_nothing(jl_module_t *m, jl_sym // get binding for reading JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); -JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var); +JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var) JL_GLOBALLY_ROOTED; JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var); // get binding for assignment JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module_t *m, jl_sym_t *s); @@ -2197,6 +2197,10 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error_rt(const char *fname, const char *context, jl_value_t *ty JL_MAYBE_UNROOTED, jl_value_t *got JL_MAYBE_UNROOTED); +JL_DLLEXPORT void JL_NORETURN jl_type_error_global(const char *fname, + jl_module_t *mod, jl_sym_t *sym, + jl_value_t *ty JL_MAYBE_UNROOTED, + jl_value_t *got JL_MAYBE_UNROOTED); JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var, jl_value_t *scope JL_MAYBE_UNROOTED); JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_datatype_t *t, jl_sym_t *var); JL_DLLEXPORT void JL_NORETURN jl_argument_error(char *str); diff --git a/src/julia_internal.h b/src/julia_internal.h index 9473f6be4b898..2203ac99afbe0 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -851,7 +851,7 @@ int set_nth_fieldonce(jl_datatype_t *st, jl_value_t *v, size_t i, jl_value_t *rh jl_value_t *swap_bits(jl_value_t *ty, char *v, uint8_t *psel, jl_value_t *parent, jl_value_t *rhs, enum atomic_kind isatomic); jl_value_t *replace_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *expected, jl_value_t *rhs, int isatomic, jl_module_t *mod, jl_sym_t *name); jl_value_t *replace_bits(jl_value_t *ty, char *p, uint8_t *psel, jl_value_t *parent, jl_value_t *expected, jl_value_t *rhs, enum atomic_kind isatomic); -jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, int isatomic, jl_module_t *mod, jl_sym_t *name); +jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, int isatomic, jl_binding_t *b, jl_module_t *mod, jl_sym_t *name); jl_value_t *modify_bits(jl_value_t *ty, char *p, uint8_t *psel, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, enum atomic_kind isatomic); int setonce_bits(jl_datatype_t *rty, char *p, jl_value_t *owner, jl_value_t *rhs, enum atomic_kind isatomic); jl_expr_t *jl_exprn(jl_sym_t *head, size_t n); @@ -865,6 +865,7 @@ JL_DLLEXPORT int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree); int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t *val); +jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED, const char *msg); void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty); void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type); JL_DLLEXPORT void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, int strong); diff --git a/src/module.c b/src/module.c index b5d3e785bc96c..60d2652ddfaaa 100644 --- a/src/module.c +++ b/src/module.c @@ -1877,7 +1877,7 @@ void jl_binding_deprecation_warning(jl_binding_t *b) // For a generally writable binding (checked using jl_check_binding_currently_writable in this world age), check whether // we can actually write the value `rhs` to it. -jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED) +jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED, const char *msg) { JL_GC_PUSH1(&rhs); // callee-rooted jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); @@ -1885,10 +1885,8 @@ jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl assert(kind == PARTITION_KIND_DECLARED || kind == PARTITION_KIND_GLOBAL); jl_value_t *old_ty = kind == PARTITION_KIND_DECLARED ? (jl_value_t*)jl_any_type : bpart->restriction; JL_GC_PROMISE_ROOTED(old_ty); - if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty) { - if (!jl_isa(rhs, old_ty)) - jl_errorf("cannot assign an incompatible value to the global %s.%s.", - jl_symbol_name(mod->name), jl_symbol_name(var)); + if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty && !jl_isa(rhs, old_ty)) { + jl_type_error_global(msg, mod, var, old_ty, rhs); } JL_GC_POP(); return old_ty; @@ -1896,7 +1894,7 @@ jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs) { - if (jl_check_binding_assign_value(b, mod, var, rhs) != NULL) { + if (jl_check_binding_assign_value(b, mod, var, rhs, "setglobal!") != NULL) { jl_atomic_store_release(&b->value, rhs); jl_gc_wb(b, rhs); } @@ -1904,7 +1902,7 @@ JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_module_t *mod, jl_sy JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs) { - jl_check_binding_assign_value(b, mod, var, rhs); + jl_check_binding_assign_value(b, mod, var, rhs, "swapglobal!"); jl_value_t *old = jl_atomic_exchange(&b->value, rhs); jl_gc_wb(b, rhs); if (__unlikely(old == NULL)) @@ -1914,7 +1912,7 @@ JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_s JL_DLLEXPORT jl_value_t *jl_checked_replace(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *expected, jl_value_t *rhs) { - jl_value_t *ty = jl_check_binding_assign_value(b, mod, var, rhs); + jl_value_t *ty = jl_check_binding_assign_value(b, mod, var, rhs, "replaceglobal!"); return replace_value(ty, &b->value, (jl_value_t*)b, expected, rhs, 1, mod, var); } @@ -1928,12 +1926,12 @@ JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl jl_symbol_name(mod->name), jl_symbol_name(var)); jl_value_t *ty = bpart->restriction; JL_GC_PROMISE_ROOTED(ty); - return modify_value(ty, &b->value, (jl_value_t*)b, op, rhs, 1, mod, var); + return modify_value(ty, &b->value, (jl_value_t*)b, op, rhs, 1, b, mod, var); } JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs ) { - jl_check_binding_assign_value(b, mod, var, rhs); + jl_check_binding_assign_value(b, mod, var, rhs, "setglobalonce!"); jl_value_t *old = NULL; if (jl_atomic_cmpswap(&b->value, &old, rhs)) jl_gc_wb(b, rhs); diff --git a/src/rtutils.c b/src/rtutils.c index 2976e56b4195d..6d4a375017bf2 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -121,6 +121,16 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error_rt(const char *fname, const char *co jl_throw(ex); } +JL_DLLEXPORT void JL_NORETURN jl_type_error_global(const char *fname, jl_module_t *mod, jl_sym_t *sym, + jl_value_t *expected JL_MAYBE_UNROOTED, + jl_value_t *got JL_MAYBE_UNROOTED) +{ + jl_value_t *gr = jl_module_globalref(mod, sym); + jl_value_t *ex = jl_new_struct(jl_typeerror_type, jl_symbol(fname), gr, expected, got); + jl_throw(ex); +} + + // with function name or description only JL_DLLEXPORT void JL_NORETURN jl_type_error(const char *fname, jl_value_t *expected JL_MAYBE_UNROOTED, diff --git a/test/core.jl b/test/core.jl index 478499ae192db..7552d1d014a6d 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8131,7 +8131,10 @@ end setglobal!(m, :x, 2, :release) @test m.x === 2 @test_throws ConcurrencyViolationError setglobal!(m, :x, 3, :not_atomic) - @test_throws ErrorException setglobal!(m, :x, 4., :release) + @test_throws TypeError setglobal!(m, :x, 4., :release) + + f_set_bad_type(m) = setglobal!(m, :x, 4., :release) + @test_throws TypeError f_set_bad_type(m) m.x = 1 @test m.x === 1 From 9e05e9500792b094e1f49550952bbdd2f7a720ee Mon Sep 17 00:00:00 2001 From: Lionel Zoubritzky Date: Wed, 1 Oct 2025 17:06:32 +0200 Subject: [PATCH 28/38] Change delayed delete mechanism to prevent cross-drive mv failure for in-use DLL (#59635) On Windows, during precompilation of package `A`, a DLL file is generated and may replace the existing one. If `A` is already loaded in the julia session however, the corresponding soon-to-be-obsolete DLL cannot be removed while julia is running. Currently, this problem is solved by moving the old DLL in a special "julia_delayed_deletes" folder, which will be cleaned in a later julia session by `Pkg.gc()`. However, moving an in-use DLL is only permitted on the same drive, and the "julia_delayed_deletes" is located in the `tempdir`, a.k.a. on a fixed drive, say `C:`. Thus, if the `DEPOT_PATH` points to a ".julia" in another drive, say `D:`, any precompilation occuring on an already loaded package will fail. This is what happens in #59589, actually resulting in an infinite loop that bloats up both memory and disk. @staticfloat had actually predicted that such an issue could occur in https://github.com/JuliaLang/julia/pull/53456#discussion_r1501883636. This PR fixes #59589 by changing the "delayed deleting" mechanism: instead of moving the old DLLs to a fixed folder, they are renamed in their initial folder and recorded in a list stored at a fixed location. Upon `Pkg.gc()`, the listed obsolete files will be deleted (https://github.com/JuliaLang/Pkg.jl/pull/4392). It also prevents any similar infinite loops by cutting the `rm -> mv -> rm` cycle into `rm -> rename`. ~I also removed the private argument `allow_delayed_delete` from `rm`, which is only called in by [Pkg](https://github.com/JuliaLang/Pkg.jl/blob/7344e2656475261a83a6bd37d9d4cc1e7dcf5f0d/src/API.jl#L1127) but does not appear to be useful.~ EDIT: current state is https://github.com/JuliaLang/julia/pull/59635#issuecomment-3328054688 --------- Co-authored-by: Jameson Nash Co-authored-by: Elliot Saba (cherry picked from commit 3e2a4edd53ceb9f1fc3dc05c4448c5e1d4ccb5c8) --- Compiler/test/special_loading.jl | 3 +- base/file.jl | 59 ++++++++++++++++++++++++++------ test/compileall.jl | 4 ++- test/core.jl | 9 ++--- test/loading.jl | 51 ++++++++++----------------- test/precompile.jl | 10 ++---- test/precompile_utils.jl | 18 +++------- test/relocatedepot.jl | 13 +++---- test/tempdepot.jl | 43 +++++++++++++++++++++++ 9 files changed, 132 insertions(+), 78 deletions(-) create mode 100755 test/tempdepot.jl diff --git a/Compiler/test/special_loading.jl b/Compiler/test/special_loading.jl index ba012446dc61f..6b7865e12f58e 100644 --- a/Compiler/test/special_loading.jl +++ b/Compiler/test/special_loading.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -mktempdir() do dir +include(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "test", "tempdepot.jl")) +mkdepottempdir() do dir withenv("JULIA_DEPOT_PATH" => dir * (Sys.iswindows() ? ";" : ":"), "JULIA_LOAD_PATH" => nothing) do cd(joinpath(@__DIR__, "CompilerLoadingTest")) do @test success(pipeline(`$(Base.julia_cmd()[1]) --startup-file=no --project=. compiler_loading_test.jl`; stdout, stderr)) diff --git a/base/file.jl b/base/file.jl index 66e8114aba4ba..ce9bc76256fd3 100644 --- a/base/file.jl +++ b/base/file.jl @@ -253,9 +253,9 @@ function mkpath(path::AbstractString; mode::Integer = 0o777) return path end -# Files that were requested to be deleted but can't be by the current process -# i.e. loaded DLLs on Windows -delayed_delete_dir() = joinpath(tempdir(), "julia_delayed_deletes") +# Files that were requested to be deleted but can't be by the current process, +# i.e. loaded DLLs on Windows, are listed in the directory below +delayed_delete_ref() = joinpath(tempdir(), "julia_delayed_deletes_ref") """ rm(path::AbstractString; force::Bool=false, recursive::Bool=false) @@ -288,13 +288,7 @@ function rm(path::AbstractString; force::Bool=false, recursive::Bool=false, allo force && err.code==Base.UV_ENOENT && return @static if Sys.iswindows() if allow_delayed_delete && err.code==Base.UV_EACCES && endswith(path, ".dll") - # Loaded DLLs cannot be deleted on Windows, even with posix delete mode - # but they can be moved. So move out to allow the dir to be deleted. - # Pkg.gc() cleans up this dir when possible - dir = mkpath(delayed_delete_dir()) - temp_path = tempname(dir, cleanup = false, suffix = string("_", basename(path))) - @debug "Could not delete DLL most likely because it is loaded, moving to tempdir" path temp_path - mv(path, temp_path) + delayed_delete_dll(path) return end end @@ -330,6 +324,22 @@ function rm(path::AbstractString; force::Bool=false, recursive::Bool=false, allo end +# Loaded DLLs cannot be deleted on Windows, even with posix delete mode but they can be renamed. +# delayed_delete_dll(path) does so temporarily, until later cleanup by Pkg.gc(). +function delayed_delete_dll(path) + # in-use DLL must be kept on the same drive + temp_path = tempname(abspath(dirname(path)); cleanup=false, suffix=string("_", basename(path))) + @debug "Could not delete DLL most likely because it is loaded, moving to a temporary path" path temp_path + mkpath(delayed_delete_ref()) + io = last(mktemp(delayed_delete_ref(); cleanup=false)) + try + print(io, temp_path) # record the temporary path for Pkg.gc() + finally + close(io) + end + rename(path, temp_path) # do not call mv which could recursively call rm(path) +end + # The following use Unix command line facilities function checkfor_mv_cp_cptree(src::AbstractString, dst::AbstractString, txt::AbstractString; force::Bool=false) @@ -678,8 +688,35 @@ end # deprecated internal function used by some packages temp_cleanup_purge(; force=false) = force ? temp_cleanup_purge_all() : @lock TEMP_CLEANUP_LOCK temp_cleanup_purge_prelocked(false) +function temp_cleanup_postprocess(cleanup_dirs) + if !isempty(cleanup_dirs) + rmcmd = """ + cleanuplist = readlines(stdin) # This loop won't start running until stdin is closed, which is supposed to be sequenced after the process exits + sleep(1) # Wait for the operating system to hopefully be ready, since the OS implementation is probably incorrect, given the history of buggy work-arounds like this that have existed for ages in dotNet and libuv + for path in cleanuplist + try + rm(path, force=true, recursive=true) + catch ex + @warn "Failed to clean up temporary path \$(repr(path))\n\$ex" _group=:file + end + end + """ + cmd = Cmd(Base.cmd_gen(((Base.julia_cmd(),), ("--startup-file=no",), ("-e",), (rmcmd,))); ignorestatus = true, detach = true) + pw = Base.PipeEndpoint() + run(cmd, pw, devnull, stderr; wait=false) + join(pw, cleanup_dirs, "\n") + Base.dup(Base._fd(pw)) # intentionally leak a reference, until the process exits + close(pw) + end +end + +function temp_cleanup_atexit() + temp_cleanup_purge_all() + @lock TEMP_CLEANUP_LOCK temp_cleanup_postprocess(keys(TEMP_CLEANUP)) +end + function __postinit__() - Base.atexit(temp_cleanup_purge_all) + Base.atexit(temp_cleanup_atexit) end const temp_prefix = "jl_" diff --git a/test/compileall.jl b/test/compileall.jl index 1987bda7f04df..726cadbba68c8 100644 --- a/test/compileall.jl +++ b/test/compileall.jl @@ -2,8 +2,10 @@ # We make it a separate test target here, so that it can run in parallel # with the rest of the tests. +include("tempdepot.jl") + function precompile_test_harness(@nospecialize(f)) - load_path = mktempdir() + load_path = mkdepottempdir() try pushfirst!(LOAD_PATH, load_path) pushfirst!(DEPOT_PATH, load_path) diff --git a/test/core.jl b/test/core.jl index 7552d1d014a6d..d18f7fe8136b6 100644 --- a/test/core.jl +++ b/test/core.jl @@ -9,6 +9,8 @@ const Bottom = Union{} # For curmod_* include("testenv.jl") +include("tempdepot.jl") + ## tests that `const` field declarations # sanity tests that our built-in types are marked correctly for const fields @@ -8422,7 +8424,7 @@ end # precompilation let load_path = mktempdir() - depot_path = mktempdir() + depot_path = mkdepottempdir() try pushfirst!(LOAD_PATH, load_path) pushfirst!(DEPOT_PATH, depot_path) @@ -8464,11 +8466,6 @@ let load_path = mktempdir() filter!((≠)(load_path), LOAD_PATH) filter!((≠)(depot_path), DEPOT_PATH) rm(load_path, recursive=true, force=true) - try - rm(depot_path, force=true, recursive=true) - catch err - @show err - end end end diff --git a/test/loading.jl b/test/loading.jl index d12cd2769ef1d..c92bbf52f16c1 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -34,6 +34,7 @@ end @test @nested_LINE_expansion2() == ((@__LINE__() - 5, @__LINE__() - 9), @__LINE__()) original_depot_path = copy(Base.DEPOT_PATH) +include("tempdepot.jl") include("precompile_utils.jl") loaded_files = String[] @@ -226,7 +227,6 @@ end end end - ## functional testing of package identification, location & loading ## saved_load_path = copy(LOAD_PATH) @@ -236,8 +236,9 @@ watcher_counter = Ref(0) push!(Base.active_project_callbacks, () -> watcher_counter[] += 1) push!(Base.active_project_callbacks, () -> error("broken")) +const testdefaultdepot = mkdepottempdir() push!(empty!(LOAD_PATH), joinpath(@__DIR__, "project")) -append!(empty!(DEPOT_PATH), [mktempdir(), joinpath(@__DIR__, "depot")]) +append!(empty!(DEPOT_PATH), [testdefaultdepot, joinpath(@__DIR__, "depot")]) @test watcher_counter[] == 0 @test_logs (:error, r"active project callback .* failed") Base.set_active_project(nothing) @test watcher_counter[] == 1 @@ -461,7 +462,7 @@ function make_env(flat, root, roots, graph, paths, dummies) ) end -const depots = [mktempdir() for _ = 1:3] +const depots = [mkdepottempdir() for _ = 1:3] const envs = Dict{String,Any}() append!(empty!(DEPOT_PATH), depots) @@ -755,13 +756,6 @@ end for env in keys(envs) rm(env, force=true, recursive=true) end -for depot in depots - try - rm(depot, force=true, recursive=true) - catch err - @show err - end -end append!(empty!(LOAD_PATH), saved_load_path) append!(empty!(DEPOT_PATH), saved_depot_path) @@ -1043,9 +1037,10 @@ end _pkgversion == pkgversion(parent) || error("unexpected extension \$ext version: \$_pkgversion") end """ - depot_path = mktempdir() - try - proj = joinpath(@__DIR__, "project", "Extensions", "HasDepWithExtensions.jl") + depot_path = mkdepottempdir() + proj = joinpath(@__DIR__, "project", "Extensions", "HasDepWithExtensions.jl") + + begin function gen_extension_cmd(compile, distr=false) load_distr = distr ? "using Distributed; addprocs(1)" : "" @@ -1155,7 +1150,7 @@ end # Extension-to-extension dependencies - mktempdir() do depot # Parallel pre-compilation + mkdepottempdir() do depot # Parallel pre-compilation code = """ Base.disable_parallel_precompile = false using ExtToExtDependency @@ -1171,7 +1166,7 @@ end ) @test occursin("Hello ext-to-ext!", String(read(cmd))) end - mktempdir() do depot # Serial pre-compilation + mkdepottempdir() do depot # Serial pre-compilation code = """ Base.disable_parallel_precompile = true using ExtToExtDependency @@ -1188,7 +1183,7 @@ end @test occursin("Hello ext-to-ext!", String(read(cmd))) end - mktempdir() do depot # Parallel pre-compilation + mkdepottempdir() do depot # Parallel pre-compilation code = """ Base.disable_parallel_precompile = false using CrossPackageExtToExtDependency @@ -1204,7 +1199,7 @@ end ) @test occursin("Hello x-package ext-to-ext!", String(read(cmd))) end - mktempdir() do depot # Serial pre-compilation + mkdepottempdir() do depot # Serial pre-compilation code = """ Base.disable_parallel_precompile = true using CrossPackageExtToExtDependency @@ -1224,7 +1219,7 @@ end # Extensions for "parent" dependencies # (i.e. an `ExtAB` where A depends on / loads B, but B provides the extension) - mktempdir() do depot # Parallel pre-compilation + mkdepottempdir() do depot # Parallel pre-compilation code = """ Base.disable_parallel_precompile = false using Parent @@ -1239,7 +1234,7 @@ end ) @test occursin("Hello parent!", String(read(cmd))) end - mktempdir() do depot # Serial pre-compilation + mkdepottempdir() do depot # Serial pre-compilation code = """ Base.disable_parallel_precompile = true using Parent @@ -1254,13 +1249,6 @@ end ) @test occursin("Hello parent!", String(read(cmd))) end - - finally - try - rm(depot_path, force=true, recursive=true) - catch err - @show err - end end end @@ -1361,7 +1349,7 @@ end end @testset "relocatable upgrades #51989" begin - mktempdir() do depot + mkdepottempdir() do depot # realpath is needed because Pkg is used for one of the precompile paths below, and Pkg calls realpath on the # project path so the cache file slug will be different if the tempdir is given as a symlink # (which it often is on MacOS) which would break the test. @@ -1435,7 +1423,7 @@ end end @testset "code coverage disabled during precompilation" begin - mktempdir() do depot + mkdepottempdir() do depot cov_test_dir = joinpath(@__DIR__, "project", "deps", "CovTest.jl") cov_cache_dir = joinpath(depot, "compiled", "v$(VERSION.major).$(VERSION.minor)", "CovTest") function rm_cov_files() @@ -1479,7 +1467,7 @@ end end @testset "command-line flags" begin - mktempdir() do depot_path mktempdir() do dir + mkdepottempdir() do depot_path mktempdir() do dir # generate a Parent.jl and Child.jl package, with Parent depending on Child open(joinpath(dir, "Child.jl"), "w") do io println(io, """ @@ -1562,7 +1550,7 @@ end end @testset "including non-existent file throws proper error #52462" begin - mktempdir() do depot + mkdepottempdir() do depot project_path = joinpath(depot, "project") mkpath(project_path) @@ -1704,7 +1692,7 @@ end end @testset "require_stdlib loading duplication" begin - depot_path = mktempdir() + depot_path = mkdepottempdir() oldBase64 = nothing try push!(empty!(DEPOT_PATH), depot_path) @@ -1728,6 +1716,5 @@ end finally oldBase64 === nothing || Base.register_root_module(oldBase64) copy!(DEPOT_PATH, original_depot_path) - rm(depot_path, force=true, recursive=true) end end diff --git a/test/precompile.jl b/test/precompile.jl index 13af539eed2ab..e40410cddc465 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -4,6 +4,7 @@ using Test, Distributed, Random, Logging, Libdl using REPL # testing the doc lookup function should be outside of the scope of this file, but is currently tested here include("precompile_utils.jl") +include("tempdepot.jl") Foo_module = :Foo4b3a94a1a081a8cb foo_incl_dep = :foo4b3a94a1a081a8cb @@ -1524,11 +1525,11 @@ end test_workers = addprocs(1) push!(test_workers, myid()) save_cwd = pwd() - temp_path = mktempdir() + temp_path = mkdepottempdir() try cd(temp_path) load_path = mktempdir(temp_path) - load_cache_path = mktempdir(temp_path) + load_cache_path = mkdepottempdir(temp_path) ModuleA = :Issue19960A ModuleB = :Issue19960B @@ -1576,11 +1577,6 @@ end end finally cd(save_cwd) - try - rm(temp_path, recursive=true) - catch err - @show err - end pop!(test_workers) # remove myid rmprocs(test_workers) end diff --git a/test/precompile_utils.jl b/test/precompile_utils.jl index 55eba353f2ada..c9a7c98d262e0 100644 --- a/test/precompile_utils.jl +++ b/test/precompile_utils.jl @@ -1,28 +1,18 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +include("tempdepot.jl") + function precompile_test_harness(@nospecialize(f), testset::String) @testset "$testset" precompile_test_harness(f, true) end function precompile_test_harness(@nospecialize(f), separate::Bool=true) - load_path = mktempdir() - load_cache_path = separate ? mktempdir() : load_path + load_path = mkdepottempdir() + load_cache_path = separate ? mkdepottempdir() : load_path try pushfirst!(LOAD_PATH, load_path) pushfirst!(DEPOT_PATH, load_cache_path) f(load_path) finally - try - rm(load_path, force=true, recursive=true) - catch err - @show err - end - if separate - try - rm(load_cache_path, force=true, recursive=true) - catch err - @show err - end - end filter!((≠)(load_path), LOAD_PATH) separate && filter!((≠)(load_cache_path), DEPOT_PATH) end diff --git a/test/relocatedepot.jl b/test/relocatedepot.jl index 2ef6dec90dbc1..e8758365e3ff4 100644 --- a/test/relocatedepot.jl +++ b/test/relocatedepot.jl @@ -4,6 +4,7 @@ using Test include("testenv.jl") +include("tempdepot.jl") function test_harness(@nospecialize(fn); empty_load_path=true, empty_depot_path=true) @@ -32,7 +33,7 @@ if !test_relocated_depot # insert @depot only once for first match test_harness() do - mktempdir() do dir + mkdepottempdir() do dir pushfirst!(DEPOT_PATH, dir) if Sys.iswindows() # dirs start with a drive letter instead of a path separator @@ -46,7 +47,7 @@ if !test_relocated_depot # 55340 empty!(DEPOT_PATH) - mktempdir() do dir + mkdepottempdir() do dir jlrc = joinpath(dir, "julia-rc2") jl = joinpath(dir, "julia") mkdir(jl) @@ -61,7 +62,7 @@ if !test_relocated_depot # deal with and without trailing path separators test_harness() do - mktempdir() do dir + mkdepottempdir() do dir pushfirst!(DEPOT_PATH, dir) path = joinpath(dir, "foo") if isdirpath(DEPOT_PATH[1]) @@ -176,7 +177,7 @@ if !test_relocated_depot # add them as include_dependency()s to a new pkg Foo, which will be precompiled into depot3. # After loading the include_dependency()s of Foo should refer to depot1 depot2 each. test_harness() do - mktempdir() do depot1 + mkdepottempdir() do depot1 # precompile Example in depot1 example1_root = joinpath(depot1, "Example1") mkpath(joinpath(example1_root, "src")) @@ -196,7 +197,7 @@ if !test_relocated_depot end pushfirst!(LOAD_PATH, depot1); pushfirst!(DEPOT_PATH, depot1) pkg = Base.identify_package("Example1"); Base.require(pkg) - mktempdir() do depot2 + mkdepottempdir() do depot2 # precompile Example in depot2 example2_root = joinpath(depot2, "Example2") mkpath(joinpath(example2_root, "src")) @@ -216,7 +217,7 @@ if !test_relocated_depot end pushfirst!(LOAD_PATH, depot2); pushfirst!(DEPOT_PATH, depot2) pkg = Base.identify_package("Example2"); Base.require(pkg) - mktempdir() do depot3 + mkdepottempdir() do depot3 # precompile Foo in depot3 open(joinpath(depot3, "Module52161.jl"), write=true) do io println(io, """ diff --git a/test/tempdepot.jl b/test/tempdepot.jl new file mode 100755 index 0000000000000..18fad0cf15346 --- /dev/null +++ b/test/tempdepot.jl @@ -0,0 +1,43 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# This includes the `mkdepottempdir` and `rmdepot` functions, used to +# respectively create and remove temporary depots to use in tests. +# `mktempdir` and `rm` cannot be used because, on Windows, the DLLs generated by +# precompilation in the depots cannot be removed by the program that uses them. +# This file can be included multiple times in the same module if necessary, +# which can happen with unisolated test runs. + +if Sys.iswindows() && !@isdefined(DEPOTS_TOREMOVE) + const DEPOTS_TOREMOVE = String[] + atexit(() -> Base.Filesystem.temp_cleanup_postprocess(DEPOTS_TOREMOVE)) +end + +function rmdepot(depot) + try + @static if Sys.iswindows() # on Windows, delay the rm + push!(DEPOTS_TOREMOVE, depot) + else # on the other systems, do it immediately + rm(depot, force=true, recursive=true) + end + catch err + @show err + end +end + +function mkdepottempdir(f::Function, parent=tempdir(); prefix="jltestdepot_") + tmpdir = mktempdir(parent; prefix, cleanup=false) + try + f(tmpdir) + finally + rmdepot(tmpdir) + end +end +function mkdepottempdir(parent=tempdir(); prefix="jltestdepot_", cleanup=true) + @static if Sys.iswindows() + tmpdir = mktempdir(parent; prefix, cleanup=false) + cleanup && push!(DEPOTS_TOREMOVE, tmpdir) + tmpdir + else + mktempdir(parent; prefix, cleanup) + end +end From f3446e9838e7a6a31b03824d9351a8eddc166755 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Fri, 19 Sep 2025 09:07:24 -0400 Subject: [PATCH 29/38] `CoreLogging`: prevent some `Annotated*`-related instability (#59467) Add some concrete `typeassert`s to increase the resistance of the sysimage to invalidation by helping inference within the method body. (cherry picked from commit bb51d567c3529bac33e683cad40673e9c3228234) --- base/logging/ConsoleLogger.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/logging/ConsoleLogger.jl b/base/logging/ConsoleLogger.jl index 8766d0ae56331..b6dc01f4f326c 100644 --- a/base/logging/ConsoleLogger.jl +++ b/base/logging/ConsoleLogger.jl @@ -124,8 +124,8 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module # split into lines. This is specialised to improve type inference, # and reduce the risk of resulting method invalidations. message = string(message) - msglines = if Base._isannotated(message) && !isempty(Base.annotations(message)) - message = Base.AnnotatedString(String(message), Base.annotations(message)) + msglines = if Base._isannotated(message) && !isempty(Base.annotations(message)::Vector{Base.RegionAnnotation}) + message = Base.AnnotatedString(String(message)::String, Base.annotations(message)::Vector{Base.RegionAnnotation}) @NamedTuple{indent::Int, msg::Union{SubString{Base.AnnotatedString{String}}, SubString{String}}}[ (indent=0, msg=l) for l in split(chomp(message), '\n')] else From c186014ccf7dfcb8209b45a7c0a2cf6280cefbcd Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 24 Sep 2025 13:09:41 -0300 Subject: [PATCH 30/38] Print task backtraces on sigquit (#59389) (cherry picked from commit 06352854007e7c3d69df01833877809f78832781) --- src/julia_internal.h | 1 + src/signal-handling.c | 3 +++ src/stackwalk.c | 18 +++++++++++++----- test/cmdlineargs.jl | 26 ++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/julia_internal.h b/src/julia_internal.h index 2203ac99afbe0..c9e1b0e204df6 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1501,6 +1501,7 @@ JL_DLLEXPORT void jl_raise_debugger(void) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_gdblookup(void* ip) JL_NOTSAFEPOINT; void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT; void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_data) JL_NOTSAFEPOINT; +JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT; #ifdef _OS_WINDOWS_ JL_DLLEXPORT void jl_refresh_dbg_module_list(void); #endif diff --git a/src/signal-handling.c b/src/signal-handling.c index 6e19028ca7940..b03f0c1a430cd 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -636,6 +636,9 @@ void jl_critical_error(int sig, int si_code, bt_context_t *context, jl_task_t *c jl_safe_printf("\n[%d] signal %d (%d): %s\n", getpid(), sig, si_code, strsignal(sig)); else jl_safe_printf("\n[%d] signal %d: %s\n", getpid(), sig, strsignal(sig)); + if (sig == SIGQUIT) { + jl_print_task_backtraces(0); + } } jl_safe_printf("in expression starting at %s:%d\n", jl_filename, jl_lineno); if (context && ct) { diff --git a/src/stackwalk.c b/src/stackwalk.c index 1f3c8d690c8ce..50566b46ff45a 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -1383,11 +1383,19 @@ JL_DLLEXPORT void jlbacktrace(void) JL_NOTSAFEPOINT // Print backtrace for specified task to jl_safe_printf stderr JL_DLLEXPORT void jlbacktracet(jl_task_t *t) JL_NOTSAFEPOINT { - jl_task_t *ct = jl_current_task; - jl_ptls_t ptls = ct->ptls; - ptls->bt_size = 0; - jl_bt_element_t *bt_data = ptls->bt_data; - jl_record_backtrace_result_t r = jl_record_backtrace(t, bt_data, JL_MAX_BT_SIZE, 0); + jl_bt_element_t *bt_data; + jl_task_t *ct = jl_get_current_task(); + size_t max_bt_size; + if (ct && ct->ptls != NULL) { + jl_ptls_t ptls = ct->ptls; + ptls->bt_size = 0; + bt_data = ptls->bt_data; + max_bt_size = JL_MAX_BT_SIZE; + } else { + max_bt_size = 1024; //8kb of stack should be safe + bt_data = (jl_bt_element_t *)alloca(max_bt_size * sizeof(jl_bt_element_t)); + } + jl_record_backtrace_result_t r = jl_record_backtrace(t, bt_data, max_bt_size, 0); size_t bt_size = r.bt_size; size_t i; for (i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 09cb4c1d6bf9a..75e338ea5d518 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -60,6 +60,32 @@ let @test format_filename("%a%%b") == "a%b" end +if Sys.isunix() + @testset "SIGQUIT prints task backtraces" begin + script = """ + mutable struct RLimit + cur::Int64 + max::Int64 + end + const RLIMIT_CORE = 4 # from /usr/include/sys/resource.h + ccall(:setrlimit, Cint, (Cint, Ref{RLimit}), RLIMIT_CORE, Ref(RLimit(0, 0))) + write(stdout, "r") + wait() + """ + exename = `$(Base.julia_cmd()) --startup-file=no --color=no` + errp = PipeBuffer() + # disable coredumps for this process + p = open(pipeline(`$exename -e $script`, stderr=errp), "r") + @test read(p, UInt8) == UInt8('r') + Base.kill(p, Base.SIGQUIT) + wait(p) + err_s = readchomp(errp) + @test Base.process_signaled(p) && p.termsignal == Base.SIGQUIT + @test occursin("==== Thread ", err_s) + @test occursin("==== Done", err_s) + end +end + @testset "julia_cmd" begin julia_basic = Base.julia_cmd() function get_julia_cmd(arg) From e0a44d74e3be4f4d476250cf2e3a18f797efba3e Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sat, 11 Oct 2025 22:31:10 -0400 Subject: [PATCH 31/38] [1.12] Backport (improve sysimage.mk more completely (#57758)) (#59793) Add back support for -debug builds and define ability to use shared library builds more places in the file. Fix #57675 (cherry picked from commit 51da5402f86c98113c78ddeced4e96527fc09a21) Fixes https://github.com/JuliaLang/julia/issues/57675#issuecomment-3386213653 and https://github.com/JuliaLang/julia/pull/57758#issuecomment-3386223349 Co-authored-by: Jameson Nash --- Makefile | 8 +------- sysimage.mk | 52 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index d5742f2f4ab70..f58fda5ec8558 100644 --- a/Makefile +++ b/Makefile @@ -110,13 +110,7 @@ julia-src-release julia-src-debug : julia-src-% : julia-deps julia_flisp.boot.in julia-cli-release julia-cli-debug: julia-cli-% : julia-deps @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/cli $* -julia-sysimg-ji : $(TOP_LEVEL_PKG_LINK_TARGETS) julia-stdlib julia-base julia-cli-$(JULIA_BUILD_MODE) julia-src-$(JULIA_BUILD_MODE) | $(build_private_libdir) - @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) -f sysimage.mk sysimg-ji JULIA_EXECUTABLE='$(JULIA_EXECUTABLE)' - -julia-sysimg-bc : $(TOP_LEVEL_PKG_LINK_TARGETS) julia-stdlib julia-base julia-cli-$(JULIA_BUILD_MODE) julia-src-$(JULIA_BUILD_MODE) | $(build_private_libdir) - @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) -f sysimage.mk sysimg-bc JULIA_EXECUTABLE='$(JULIA_EXECUTABLE)' - -julia-sysimg-release julia-sysimg-debug : julia-sysimg-% : julia-sysimg-ji julia-src-% +julia-sysimg-release julia-sysimg-debug : julia-sysimg-% : julia-src-% $(TOP_LEVEL_PKG_LINK_TARGETS) julia-stdlib julia-base julia-cli-% julia-src-% | $(build_private_libdir) @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) -f sysimage.mk sysimg-$* julia-debug julia-release : julia-% : julia-sysimg-% julia-src-% julia-symlink julia-libccalltest \ diff --git a/sysimage.mk b/sysimage.mk index 5faffc6b76990..310dbde37580a 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -7,7 +7,7 @@ include $(JULIAHOME)/stdlib/stdlib.mk default: sysimg-$(JULIA_BUILD_MODE) # contains either "debug" or "release" all: sysimg-release sysimg-debug basecompiler-ji: $(build_private_libdir)/basecompiler.ji -sysimg-ji: $(build_private_libdir)/sys.ji +sysimg-ji: $(build_private_libdir)/sysbase.ji sysimg-bc: $(build_private_libdir)/sys-bc.a sysimg-release: $(build_private_libdir)/sys.$(SHLIB_EXT) sysimg-debug: $(build_private_libdir)/sys-debug.$(SHLIB_EXT) @@ -69,28 +69,45 @@ RELDATADIR := $(call rel_path,$(JULIAHOME)/base,$(build_datarootdir))/ # <-- mak $(build_private_libdir)/basecompiler.ji: $(COMPILER_SRCS) @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ - JULIA_NUM_THREADS=1 $(call spawn,$(JULIA_EXECUTABLE)) -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp \ - --startup-file=no --warn-overwrite=yes --depwarn=error -g$(BOOTSTRAP_DEBUG_LEVEL) -O1 Base_compiler.jl --buildroot $(RELBUILDROOT) --dataroot $(RELDATADIR)) - @mv $@.tmp $@ - -$(build_private_libdir)/basecompiler-o.a $(build_private_libdir)/basecompiler-bc.a: $(build_private_libdir)/basecompiler-%.a : $(COMPILER_SRCS) - @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ - JULIA_NUM_THREADS=1 $(call spawn,$(JULIA_EXECUTABLE)) -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-$* $(call cygpath_w,$@).tmp \ + JULIA_NUM_THREADS=1 $(call spawn,$(JULIA_EXECUTABLE)) $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp \ --startup-file=no --warn-overwrite=yes -g$(BOOTSTRAP_DEBUG_LEVEL) -O1 Base_compiler.jl --buildroot $(RELBUILDROOT) --dataroot $(RELDATADIR)) @mv $@.tmp $@ -$(build_private_libdir)/sys.ji: $(build_private_libdir)/basecompiler.$(SHLIB_EXT) $(JULIAHOME)/VERSION $(BASE_SRCS) $(STDLIB_SRCS) - @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ - if ! JULIA_BINDIR=$(call cygpath_w,$(build_bindir)) WINEPATH="$(call cygpath_w,$(build_bindir));$$WINEPATH" \ - JULIA_NUM_THREADS=1 $(call spawn, $(JULIA_EXECUTABLE)) -g1 -O1 -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp $(JULIA_SYSIMG_BUILD_FLAGS) \ - --startup-file=no --warn-overwrite=yes --depwarn=error --sysimage $(call cygpath_w,$<) sysimg.jl --buildroot $(RELBUILDROOT) --dataroot $(RELDATADIR); then \ - echo '*** This error might be fixed by running `make clean`. If the error persists$(COMMA) try `make cleanall`. ***'; \ +define base_builder +$$(build_private_libdir)/basecompiler$1-o.a $$(build_private_libdir)/basecompiler$1-bc.a : $$(build_private_libdir)/basecompiler$1-%.a : $(COMPILER_SRCS) + @$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \ + WINEPATH="$$(call cygpath_w,$$(build_bindir));$$$$WINEPATH" \ + JULIA_NUM_THREADS=1 \ + $$(call spawn, $3) $2 -C "$$(JULIA_CPU_TARGET)" $$(HEAPLIM) --output-$$* $$(call cygpath_w,$$@).tmp \ + --startup-file=no --warn-overwrite=yes -g$$(BOOTSTRAP_DEBUG_LEVEL) Base_compiler.jl --buildroot $$(RELBUILDROOT) --dataroot $$(RELDATADIR)) + @mv $$@.tmp $$@ +$$(build_private_libdir)/sysbase$1.ji: $$(build_private_libdir)/basecompiler$1.$$(SHLIB_EXT) $$(JULIAHOME)/VERSION $$(BASE_SRCS) $$(STDLIB_SRCS) + @$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \ + if ! JULIA_BINDIR=$$(call cygpath_w,$$(build_bindir)) \ + WINEPATH="$$(call cygpath_w,$$(build_bindir));$$$$WINEPATH" \ + JULIA_NUM_THREADS=1 \ + $$(call spawn, $$(JULIA_EXECUTABLE)) -g1 $2 -C "$$(JULIA_CPU_TARGET)" $$(HEAPLIM) --output-ji $$(call cygpath_w,$$@).tmp $$(JULIA_SYSIMG_BUILD_FLAGS) \ + --startup-file=no --warn-overwrite=yes --sysimage $$(call cygpath_w,$$<) sysimg.jl --buildroot $$(RELBUILDROOT) --dataroot $$(RELDATADIR); then \ + echo '*** This error might be fixed by running `make clean`. If the error persists$$(COMMA) try `make cleanall`. ***'; \ false; \ fi ) - @mv $@.tmp $@ + @mv $$@.tmp $$@ +.SECONDARY: $$(build_private_libdir)/basecompiler$1-o.a $$(build_private_libdir)/basecompiler$1-bc.a $$(build_private_libdir)/sysbase$1.ji # request Make to keep these files around +endef define sysimg_builder -$$(build_private_libdir)/sys$1-o.a $$(build_private_libdir)/sys$1-bc.a : $$(build_private_libdir)/sys$1-%.a : $$(build_private_libdir)/sys.ji $$(JULIAHOME)/contrib/generate_precompile.jl +$$(build_private_libdir)/sysbase$1-o.a $$(build_private_libdir)/sysbase$1-bc.a : $$(build_private_libdir)/sysbase$1-%.a : $$(build_private_libdir)/basecompiler$1.$$(SHLIB_EXT) $$(JULIAHOME)/VERSION $$(BASE_SRCS) $$(STDLIB_SRCS) + @$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \ + if ! JULIA_BINDIR=$$(call cygpath_w,$$(build_bindir)) \ + WINEPATH="$$(call cygpath_w,$$(build_bindir));$$$$WINEPATH" \ + JULIA_NUM_THREADS=1 \ + $$(call spawn, $$(JULIA_EXECUTABLE)) -g1 $2 -C "$$(JULIA_CPU_TARGET)" $$(HEAPLIM) --output-$$* $$(call cygpath_w,$$@).tmp $$(JULIA_SYSIMG_BUILD_FLAGS) \ + --startup-file=no --warn-overwrite=yes --sysimage $$(call cygpath_w,$$<) sysimg.jl --buildroot $$(RELBUILDROOT) --dataroot $$(RELDATADIR); then \ + echo '*** This error might be fixed by running `make clean`. If the error persists$$(COMMA) try `make cleanall`. ***'; \ + false; \ + fi ) + @mv $$@.tmp $$@ +$$(build_private_libdir)/sys$1-o.a $$(build_private_libdir)/sys$1-bc.a : $$(build_private_libdir)/sys$1-%.a : $$(build_private_libdir)/sysbase$1.$$(SHLIB_EXT) $$(JULIAHOME)/contrib/generate_precompile.jl @$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \ if ! JULIA_BINDIR=$$(call cygpath_w,$(build_bindir)) \ WINEPATH="$$(call cygpath_w,$$(build_bindir));$$$$WINEPATH" \ @@ -105,6 +122,9 @@ $$(build_private_libdir)/sys$1-o.a $$(build_private_libdir)/sys$1-bc.a : $$(buil fi ) @mv $$@.tmp $$@ .SECONDARY: $$(build_private_libdir)/sys$1-o.a $(build_private_libdir)/sys$1-bc.a # request Make to keep these files around +.SECONDARY: $$(build_private_libdir)/sysbase$1-o.a $(build_private_libdir)/sysbase$1-bc.a # request Make to keep these files around endef +$(eval $(call base_builder,,-O1,$(JULIA_EXECUTABLE_release))) +$(eval $(call base_builder,-debug,-O0,$(JULIA_EXECUTABLE_debug))) $(eval $(call sysimg_builder,,-O3,$(JULIA_EXECUTABLE_release))) $(eval $(call sysimg_builder,-debug,-O0,$(JULIA_EXECUTABLE_debug))) From bde875a6845568c6934d4154c19c2bd3f74a9ab9 Mon Sep 17 00:00:00 2001 From: Dilum Aluthge Date: Sun, 12 Oct 2025 12:52:43 -0400 Subject: [PATCH 32/38] CI 1.12: Use the 1.12-specific Buildkite branch (#59759) This points to the [`release-julia-1.12` branch](https://github.com/JuliaCI/julia-buildkite/tree/release-julia-1.12) in the https://github.com/JuliaCI/julia-buildkite repo. --- .buildkite-external-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite-external-version b/.buildkite-external-version index ba2906d0666cf..9570137b1ef79 100644 --- a/.buildkite-external-version +++ b/.buildkite-external-version @@ -1 +1 @@ -main +release-julia-1.12 From cceb0f9ce784634373925314a5f5433da79f255a Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sat, 2 Aug 2025 07:53:31 -0400 Subject: [PATCH 33/38] Precompile out of env deps in serial within precompilepkgs (#59122) (cherry picked from commit 980881601e488d045acce570f67f84464eaa80d7) --- base/loading.jl | 3 +- base/precompilation.jl | 53 ++++++++++++++++++++++++++------ doc/src/manual/code-loading.md | 3 ++ test/embedding/embedding-test.jl | 2 +- test/loading.jl | 2 +- test/precompile.jl | 30 +++++++++--------- 6 files changed, 64 insertions(+), 29 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index c69f28cdc5a02..627f48014df75 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2622,8 +2622,7 @@ function __require_prelocked(pkg::PkgId, env) if JLOptions().use_compiled_modules == 1 if !generating_output(#=incremental=#false) project = active_project() - if !generating_output() && !parallel_precompile_attempted && !disable_parallel_precompile && @isdefined(Precompilation) && project !== nothing && - isfile(project) && project_file_manifest_path(project) !== nothing + if !generating_output() && !parallel_precompile_attempted && !disable_parallel_precompile && @isdefined(Precompilation) parallel_precompile_attempted = true unlock(require_lock) try diff --git a/base/precompilation.jl b/base/precompilation.jl index e737faa210c79..1b609f3e2015a 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -26,9 +26,24 @@ struct ExplicitEnv #local_prefs::Union{Nothing, Dict{String, Any}} end -function ExplicitEnv(envpath::String=Base.active_project()) +ExplicitEnv() = ExplicitEnv(Base.active_project()) +function ExplicitEnv(::Nothing, envpath::String="") + ExplicitEnv(envpath, + Dict{String, UUID}(), # project_deps + Dict{String, UUID}(), # project_weakdeps + Dict{String, UUID}(), # project_extras + Dict{String, Vector{UUID}}(), # project_extensions + Dict{UUID, Vector{UUID}}(), # deps + Dict{UUID, Vector{UUID}}(), # weakdeps + Dict{UUID, Dict{String, Vector{UUID}}}(), # extensions + Dict{UUID, String}(), # names + Dict{UUID, Union{SHA1, String, Nothing, Missing}}()) +end +function ExplicitEnv(envpath::String) + # Handle missing project file by creating an empty environment if !isfile(envpath) - error("expected a project file at $(repr(envpath))") + envpath = abspath(envpath) + return ExplicitEnv(nothing, envpath) end envpath = abspath(envpath) project_d = parsed_toml(envpath) @@ -468,6 +483,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; fancyprint::Bool = can_fancyprint(io) && !timing, manifest::Bool=false, ignore_loaded::Bool=true) + @debug "precompilepkgs called with" pkgs internal_call strict warn_loaded timing _from_loading configs fancyprint manifest ignore_loaded # monomorphize this to avoid latency problems _precompilepkgs(pkgs, internal_call, strict, warn_loaded, timing, _from_loading, configs isa Vector{Config} ? configs : [configs], @@ -518,9 +534,12 @@ function _precompilepkgs(pkgs::Vector{String}, # inverse map of `parent_to_ext` above (ext → parent) ext_to_parent = Dict{Base.PkgId, Base.PkgId}() - function describe_pkg(pkg::PkgId, is_project_dep::Bool, flags::Cmd, cacheflags::Base.CacheFlags) + function describe_pkg(pkg::PkgId, is_project_dep::Bool, is_serial_dep::Bool, flags::Cmd, cacheflags::Base.CacheFlags) name = full_name(ext_to_parent, pkg) name = is_project_dep ? name : color_string(name, :light_black) + if is_serial_dep + name *= color_string(" (serial)", :light_black) + end if nconfigs > 1 && !isempty(flags) config_str = join(flags, " ") name *= color_string(" `$config_str`", :light_black) @@ -630,15 +649,28 @@ function _precompilepkgs(pkgs::Vector{String}, end @debug "precompile: extensions collected" + serial_deps = Base.PkgId[] # packages that are being precompiled in serial + + if _from_loading + # if called from loading precompilation it may be a package from another environment stack + # where we don't have access to the dep graph, so just add as a single package and do serial + # precompilation of its deps within the job. + for pkg in requested_pkgs # In case loading asks for multiple packages + pkgid = Base.identify_package(pkg) + pkgid === nothing && continue + if !haskey(direct_deps, pkgid) + @debug "precompile: package `$(pkgid)` is outside of the environment, so adding as single package serial job" + direct_deps[pkgid] = Base.PkgId[] # no deps, do them in serial in the job + push!(project_deps, pkgid) # add to project_deps so it doesn't show up in gray + push!(serial_deps, pkgid) + end + end + end + # return early if no deps if isempty(direct_deps) if isempty(pkgs) return - elseif _from_loading - # if called from loading precompilation it may be a package from another environment stack so - # don't error and allow serial precompilation to try - # TODO: actually handle packages from other envs in the stack - return else error("No direct dependencies outside of the sysimage found matching $(pkgs)") end @@ -846,7 +878,7 @@ function _precompilepkgs(pkgs::Vector{String}, dep, config = pkg_config loaded = warn_loaded && haskey(Base.loaded_modules, dep) flags, cacheflags = config - name = describe_pkg(dep, dep in project_deps, flags, cacheflags) + name = describe_pkg(dep, dep in project_deps, dep in serial_deps, flags, cacheflags) line = if pkg_config in precomperr_deps string(color_string(" ? ", Base.warn_color()), name) elseif haskey(failed_deps, pkg_config) @@ -929,12 +961,13 @@ function _precompilepkgs(pkgs::Vector{String}, if !circular && is_stale Base.acquire(parallel_limiter) is_project_dep = pkg in project_deps + is_serial_dep = pkg in serial_deps # std monitoring std_pipe = Base.link_pipe!(Pipe(); reader_supports_async=true, writer_supports_async=true) t_monitor = @async monitor_std(pkg_config, std_pipe; single_requested_pkg) - name = describe_pkg(pkg, is_project_dep, flags, cacheflags) + name = describe_pkg(pkg, is_project_dep, is_serial_dep, flags, cacheflags) @lock print_lock begin if !fancyprint && isempty(pkg_queue) printpkgstyle(io, :Precompiling, something(target[], "packages...")) diff --git a/doc/src/manual/code-loading.md b/doc/src/manual/code-loading.md index 24e64b0ca068e..9b17518d32882 100644 --- a/doc/src/manual/code-loading.md +++ b/doc/src/manual/code-loading.md @@ -42,6 +42,9 @@ These environment each serve a different purpose: * Package directories provide **convenience** when a full carefully-tracked project environment is unnecessary. They are useful when you want to put a set of packages somewhere and be able to directly use them, without needing to create a project environment for them. * Stacked environments allow for **adding** tools to the primary environment. You can push an environment of development tools onto the end of the stack to make them available from the REPL and scripts, but not from inside packages. +!!! note + When loading a package from another environment in the stack other than the active environment the package is loaded in the context of the active environment. This means that the package will be loaded as if it were imported in the active environment, which may affect how its dependencies versions are resolved. When such a package is precompiling it will be marked as a `(serial)` precompile job, which means that its dependencies will be precompiled in series within the same job, which will likely be slower. + At a high-level, each environment conceptually defines three maps: roots, graph and paths. When resolving the meaning of `import X`, the roots and graph maps are used to determine the identity of `X`, while the paths map is used to locate the source code of `X`. The specific roles of the three maps are: - **roots:** `name::Symbol` ⟶ `uuid::UUID` diff --git a/test/embedding/embedding-test.jl b/test/embedding/embedding-test.jl index 34ef9a796ba56..0744cac679698 100644 --- a/test/embedding/embedding-test.jl +++ b/test/embedding/embedding-test.jl @@ -32,5 +32,5 @@ end @test lines[9] == "called bar" @test lines[10] == "calling new bar" @test lines[11] == " From worker 2:\tTaking over the world..." - @test readline(err) == "exception caught from C" + @test "exception caught from C" in readlines(err) end diff --git a/test/loading.jl b/test/loading.jl index c92bbf52f16c1..49db010ce1adc 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1483,7 +1483,7 @@ end # helper function to load a package and return the output function load_package(name, args=``) - code = "using $name" + code = "Base.disable_parallel_precompile = true; using $name" cmd = addenv(`$(Base.julia_cmd()) -e $code $args`, "JULIA_LOAD_PATH" => dir, "JULIA_DEPOT_PATH" => depot_path, diff --git a/test/precompile.jl b/test/precompile.jl index e40410cddc465..65620dda5c3c5 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -687,16 +687,15 @@ precompile_test_harness(false) do dir error("break me") end """) - @test_warn r"LoadError: break me\nStacktrace:\n[ ]*\[1\] [\e01m\[]*error" try - Base.require(Main, :FooBar2) - error("the \"break me\" test failed") - catch exc - isa(exc, ErrorException) || rethrow() - # The LoadError shouldn't be surfaced but is printed to stderr, hence the `@test_warn` capture tests - occursin("LoadError: break me", exc.msg) && rethrow() - # The actual error that is thrown - occursin("Failed to precompile FooBar2", exc.msg) || rethrow() - end + try + Base.require(Main, :FooBar2) + error("the \"break me\" test failed") + catch exc + isa(exc, Base.Precompilation.PkgPrecompileError) || rethrow() + occursin("Failed to precompile FooBar2", exc.msg) || rethrow() + # The LoadError is printed to stderr in the precompilepkgs worker and captured in the PkgPrecompileError msg + occursin("LoadError: break me", exc.msg) || rethrow() + end # Test that trying to eval into closed modules during precompilation is an error FooBar3_file = joinpath(dir, "FooBar3.jl") @@ -708,11 +707,12 @@ precompile_test_harness(false) do dir $code end """) - @test_warn "Evaluation into the closed module `Base` breaks incremental compilation" try - Base.require(Main, :FooBar3) - catch exc - isa(exc, ErrorException) || rethrow() - end + try + Base.require(Main, :FooBar3) + catch exc + isa(exc, Base.Precompilation.PkgPrecompileError) || rethrow() + occursin("Evaluation into the closed module `Base` breaks incremental compilation", exc.msg) || rethrow() + end end # Test transitive dependency for #21266 From c501ddd3a835f729f1d293753881c0a074bbc906 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 7 Aug 2025 08:47:39 -0400 Subject: [PATCH 34/38] Fix precompiling when there's no manifest (#59212) (cherry picked from commit e4a3a2af62de01512d2194c77cd954394acb1850) --- base/loading.jl | 2 +- base/precompilation.jl | 51 +++++++++++++++++++++++++++++++++--------- test/precompile.jl | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 627f48014df75..428b106aa08ee 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2626,7 +2626,7 @@ function __require_prelocked(pkg::PkgId, env) parallel_precompile_attempted = true unlock(require_lock) try - Precompilation.precompilepkgs([pkg.name]; _from_loading=true, ignore_loaded=false) + Precompilation.precompilepkgs([pkg]; _from_loading=true, ignore_loaded=false) finally lock(require_lock) end diff --git a/base/precompilation.jl b/base/precompilation.jl index 1b609f3e2015a..45634d877115f 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -41,7 +41,7 @@ function ExplicitEnv(::Nothing, envpath::String="") end function ExplicitEnv(envpath::String) # Handle missing project file by creating an empty environment - if !isfile(envpath) + if !isfile(envpath) || project_file_manifest_path(envpath) === nothing envpath = abspath(envpath) return ExplicitEnv(nothing, envpath) end @@ -471,7 +471,7 @@ function collect_all_deps(direct_deps, dep, alldeps=Set{Base.PkgId}()) end -function precompilepkgs(pkgs::Vector{String}=String[]; +function precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}}=String[]; internal_call::Bool=false, strict::Bool = false, warn_loaded::Bool = true, @@ -490,7 +490,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; IOContext{IO}(io), fancyprint, manifest, ignore_loaded) end -function _precompilepkgs(pkgs::Vector{String}, +function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}}, internal_call::Bool, strict::Bool, warn_loaded::Bool, @@ -502,6 +502,23 @@ function _precompilepkgs(pkgs::Vector{String}, manifest::Bool, ignore_loaded::Bool) requested_pkgs = copy(pkgs) # for understanding user intent + pkg_names = pkgs isa Vector{String} ? copy(pkgs) : String[pkg.name for pkg in pkgs] + if pkgs isa Vector{PkgId} + requested_pkgids = copy(pkgs) + else + requested_pkgids = PkgId[] + for name in pkgs + pkgid = Base.identify_package(name) + if pkgid === nothing + if _from_loading + return # leave it up to loading to handle this + else + throw(PkgPrecompileError("Unknown package: $name")) + end + end + push!(requested_pkgids, pkgid) + end + end time_start = time_ns() @@ -655,8 +672,7 @@ function _precompilepkgs(pkgs::Vector{String}, # if called from loading precompilation it may be a package from another environment stack # where we don't have access to the dep graph, so just add as a single package and do serial # precompilation of its deps within the job. - for pkg in requested_pkgs # In case loading asks for multiple packages - pkgid = Base.identify_package(pkg) + for pkgid in requested_pkgids # In case loading asks for multiple packages pkgid === nothing && continue if !haskey(direct_deps, pkgid) @debug "precompile: package `$(pkgid)` is outside of the environment, so adding as single package serial job" @@ -704,6 +720,7 @@ function _precompilepkgs(pkgs::Vector{String}, circular_deps = Base.PkgId[] for pkg in keys(direct_deps) @assert isempty(stack) + pkg in serial_deps && continue # skip serial deps as we don't have their dependency graph if scan_pkg!(stack, could_be_cycle, cycles, pkg, direct_deps) push!(circular_deps, pkg) for pkg_config in keys(was_processed) @@ -717,18 +734,31 @@ function _precompilepkgs(pkgs::Vector{String}, end @debug "precompile: circular dep check done" + # If you have a workspace and want to precompile all projects in it, look through all packages in the manifest + # instead of collecting from a project i.e. not filter out packages that are in the current project. + # i.e. Pkg sets manifest to true for workspace precompile requests + # TODO: rename `manifest`? if !manifest - if isempty(pkgs) - pkgs = [pkg.name for pkg in project_deps] + if isempty(pkg_names) + pkg_names = [pkg.name for pkg in project_deps] end keep = Set{Base.PkgId}() for dep in direct_deps dep_pkgid = first(dep) - if dep_pkgid.name in pkgs + if dep_pkgid.name in pkg_names push!(keep, dep_pkgid) collect_all_deps(direct_deps, dep_pkgid, keep) end end + # Also keep packages that were explicitly requested as PkgIds (for extensions) + if pkgs isa Vector{PkgId} + for requested_pkgid in requested_pkgids + if haskey(direct_deps, requested_pkgid) + push!(keep, requested_pkgid) + collect_all_deps(direct_deps, requested_pkgid, keep) + end + end + end for ext in keys(ext_to_parent) if issubset(collect_all_deps(direct_deps, ext), keep) # if all extension deps are kept push!(keep, ext) @@ -936,7 +966,8 @@ function _precompilepkgs(pkgs::Vector{String}, for (pkg, deps) in direct_deps cachepaths = get!(() -> Base.find_all_in_cache_path(pkg), cachepath_cache, pkg) sourcepath = Base.locate_package(pkg) - single_requested_pkg = length(requested_pkgs) == 1 && only(requested_pkgs) == pkg.name + single_requested_pkg = length(requested_pkgs) == 1 && + (pkg in requested_pkgids || pkg.name in pkg_names) for config in configs pkg_config = (pkg, config) if sourcepath === nothing @@ -1063,7 +1094,7 @@ function _precompilepkgs(pkgs::Vector{String}, str = sprint(context=io) do iostr if !quick_exit if fancyprint # replace the progress bar - what = isempty(requested_pkgs) ? "packages finished." : "$(join(requested_pkgs, ", ", " and ")) finished." + what = isempty(requested_pkgids) ? "packages finished." : "$(join((p.name for p in requested_pkgids), ", ", " and ")) finished." printpkgstyle(iostr, :Precompiling, what) end plural = length(configs) > 1 ? "dependency configurations" : ndeps == 1 ? "dependency" : "dependencies" diff --git a/test/precompile.jl b/test/precompile.jl index 65620dda5c3c5..b83d675862ce5 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -2492,6 +2492,46 @@ precompile_test_harness("Package top-level load itself") do load_path end end +precompile_test_harness("Package precompilation works without manifest") do load_path + pkg_dir = joinpath(load_path, "TestPkgNoManifest") + mkpath(pkg_dir) + + # Create Project.toml with stdlib dependencies + write(joinpath(pkg_dir, "Project.toml"), """ + name = "TestPkgNoManifest" + uuid = "f47a8e44-5f82-4c5c-9076-4b4e8b7e8e8e" + version = "0.1.0" + + [deps] + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + """) + + # Create src directory and main module file + src_dir = joinpath(pkg_dir, "src") + mkpath(src_dir) + write(joinpath(src_dir, "TestPkgNoManifest.jl"), """ + module TestPkgNoManifest + end + """) + + old_active_project = Base.active_project() + try + # Activate the new package environment + Base.set_active_project(joinpath(pkg_dir, "Project.toml")) + + # Ensure there's no manifest file (this is the key to the test) + manifest_path = joinpath(pkg_dir, "Manifest.toml") + isfile(manifest_path) && rm(manifest_path) + + # This should work without errors - precompiling a package with no manifest + @eval using TestPkgNoManifest + finally + # Restore original load path and active project + Base.set_active_project(old_active_project) + end +end + # Verify that inference / caching was not performed for any macros in the sysimage let m = only(methods(Base.var"@big_str")) @test m.specializations === Core.svec() || !isdefined(m.specializations, :cache) From 971c4af91226ee6f9389891e8c13ed8fe5f31b18 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sat, 11 Oct 2025 13:02:25 +0200 Subject: [PATCH 35/38] Add note about `@threads` threadpool (#59802) (cherry picked from commit a817940468d4d69aab67c70337c06b9e6892919f) --- base/threadingconstructs.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index b83f47ef7c8cd..bb45219c60a88 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -287,8 +287,14 @@ A macro to execute a `for` loop in parallel. The iteration space is distributed coarse-grained tasks. This policy can be specified by the `schedule` argument. The execution of the loop waits for the evaluation of all iterations. +Tasks spawned by `@threads` are scheduled on the `:default` threadpool. This means that +`@threads` will not use threads from the `:interactive` threadpool, even if called from +the main thread or from a task in the interactive pool. The `:default` threadpool is +intended for compute-intensive parallel workloads. + See also: [`@spawn`](@ref Threads.@spawn) and `pmap` in [`Distributed`](@ref man-distributed). +For more information on threadpools, see the chapter on [threadpools](@ref man-threadpools). # Extended help From 1f8bc096b90c39f1cdfc52ae8db43bc3ee664725 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 14 Oct 2025 18:02:13 +0200 Subject: [PATCH 36/38] run custom platform comparison strategies in the latest world (#59832) (cherry picked from commit c171ddb91de8acffb270f364b650a7708a840c38) --- base/binaryplatforms.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index c4c65504be424..e211ac763fcad 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -1044,7 +1044,7 @@ function platforms_match(a::AbstractPlatform, b::AbstractPlatform) # Call the comparator, passing in which objects requested this comparison (one, the other, or both) # For some comparators this doesn't matter, but for non-symmetrical comparisons, it does. - if !(comparator(ak, bk, a_comp === comparator, b_comp === comparator)::Bool) + if !(@invokelatest(comparator(ak, bk, a_comp === comparator, b_comp === comparator))::Bool) return false end end From 4f45f3ff5cd7ca5a78a4a43c8291c45c7b0b6c72 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 13 Oct 2025 16:58:07 +0200 Subject: [PATCH 37/38] force inline `pow_fast` (#59824) (cherry picked from commit 354c575fe5f3fdf0d669e942edeaa4e0278a234a) --- base/fastmath.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/fastmath.jl b/base/fastmath.jl index f2f60519b99ac..ed686fb92bf34 100644 --- a/base/fastmath.jl +++ b/base/fastmath.jl @@ -297,7 +297,7 @@ exp10_fast(x::Union{Float32,Float64}) = Base.Math.exp10_fast(x) # builtins -function pow_fast(x::Float64, y::Integer) +@inline function pow_fast(x::Float64, y::Integer) z = y % Int32 z == y ? pow_fast(x, z) : x^y end From 4f05d8d9f6691960662407c8f063faf65f765b05 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 15 Oct 2025 14:43:41 +0200 Subject: [PATCH 38/38] bump Pkg to latest 1.12 --- .../Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/md5 | 1 - .../Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/sha512 | 1 - .../Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/md5 | 1 + .../Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/md5 create mode 100644 deps/checksums/Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/sha512 diff --git a/deps/checksums/Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/md5 b/deps/checksums/Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/md5 deleted file mode 100644 index faee08623f288..0000000000000 --- a/deps/checksums/Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -6e3a44c3870f135953b325a155fecd65 diff --git a/deps/checksums/Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/sha512 b/deps/checksums/Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/sha512 deleted file mode 100644 index e09a59d4feb83..0000000000000 --- a/deps/checksums/Pkg-69926e385c878253d62e2588a19b252277196ebf.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -565bdb1a4e8bda374d08a49b2ba70130762ca06004cc50d07ca58ca9a3741195977f563bc990bd627735b6464162a15a7bfb08570e812abf2f22151713de2210 diff --git a/deps/checksums/Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/md5 b/deps/checksums/Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/md5 new file mode 100644 index 0000000000000..480f2f77c5cb8 --- /dev/null +++ b/deps/checksums/Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/md5 @@ -0,0 +1 @@ +1f7f4710e3216abc3109024c41051ae8 diff --git a/deps/checksums/Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/sha512 b/deps/checksums/Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/sha512 new file mode 100644 index 0000000000000..5c3356eb58bc0 --- /dev/null +++ b/deps/checksums/Pkg-f571edda902c848382517503665fea6aa275ceb9.tar.gz/sha512 @@ -0,0 +1 @@ +47286eb6d32836b34524833e0461dabc2e25fd917f2cf552242c6807da1cb2b637c60a2e3b40e457bc5296ae932ba4432ec2c5647dc1861407031f74bdb674fc diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 865c71e8767ac..7f2e0b664dcc1 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.12 -PKG_SHA1 = 69926e385c878253d62e2588a19b252277196ebf +PKG_SHA1 = f571edda902c848382517503665fea6aa275ceb9 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1