From 3c15710d087a6e41706c26f8eba24be0c79dace8 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Sun, 13 Apr 2025 08:08:55 +0200 Subject: [PATCH 01/46] fix zero-dimensional `reverse!` (#58086) Also changed the check to compare against the tuple argument, instead of against the method static parameter for the tuple length. Should be better when the length isn't known at compile time. Fixes #58085 (cherry picked from commit b265dbac3ef8d1f8f7d779044f458a3c5e2b4689) --- base/arraymath.jl | 2 +- test/arrayops.jl | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/base/arraymath.jl b/base/arraymath.jl index 62dc3772e4938..53a7d132a2c0c 100644 --- a/base/arraymath.jl +++ b/base/arraymath.jl @@ -72,12 +72,12 @@ _reverse!(A::AbstractArray{<:Any,N}, ::Colon) where {N} = _reverse!(A, ntuple(id _reverse!(A, dim::Integer) = _reverse!(A, (Int(dim),)) _reverse!(A, dims::NTuple{M,Integer}) where {M} = _reverse!(A, Int.(dims)) function _reverse!(A::AbstractArray{<:Any,N}, dims::NTuple{M,Int}) where {N,M} + dims === () && return A # nothing to reverse dimrev = ntuple(k -> k in dims, Val{N}()) # boolean tuple indicating reversed dims if N < M || M != sum(dimrev) throw(ArgumentError("invalid dimensions $dims in reverse!")) end - M == 0 && return A # nothing to reverse # swapping loop only needs to traverse ≈half of the array halfsz = ntuple(k -> k == dims[1] ? size(A,k) ÷ 2 : size(A,k), Val{N}()) diff --git a/test/arrayops.jl b/test/arrayops.jl index 5dfc9a74887ec..aa67ad1d46f56 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1714,6 +1714,12 @@ end end end +@testset "reverse zero dims" begin + a = fill(3) + @test a == reverse(a) + @test a === reverse!(a) +end + @testset "isdiag, istril, istriu" begin # Scalar @test isdiag(3) From fcf0174d4b5957e0661180da5285881f30dc2ea8 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 15 Apr 2025 13:10:00 -0400 Subject: [PATCH 02/46] prevent allocation of Memory (layout and object) when not concrete (#58064) Fix #54969 (cherry picked from commit cca5ac75406b7e728a5ff3802b76fab16975ac2a) --- src/datatype.c | 8 +++++++- src/julia.h | 2 +- test/core.jl | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/datatype.c b/src/datatype.c index bb33aa9e397bc..5fbc4cd109521 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -496,7 +496,13 @@ void jl_get_genericmemory_layout(jl_datatype_t *st) jl_value_t *kind = jl_tparam0(st); jl_value_t *eltype = jl_tparam1(st); jl_value_t *addrspace = jl_tparam2(st); - if (!jl_is_typevar(eltype) && !jl_is_type(eltype)) { + if (!st->isconcretetype) { + // Since parent dt has an opaque layout, we may end up here being asked to copy that layout to subtypes, + // but we don't actually want to do that unless this object is constructable (or at least has a layout). + // The real layout is stored only on the wrapper. + return; + } + if (!jl_is_type(eltype)) { // this is expected to have a layout, but since it is not constructable, we don't care too much what it is static const jl_datatype_layout_t opaque_ptr_layout = {0, 0, 1, -1, sizeof(void*), {0}}; st->layout = &opaque_ptr_layout; diff --git a/src/julia.h b/src/julia.h index e321fe0ef9ee5..2bb8bc5d0b59f 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1335,7 +1335,7 @@ JL_DLLEXPORT jl_value_t *jl_unwrap_unionall(jl_value_t *v JL_PROPAGATES_ROOT) JL #define jl_inlinedatatype_layout(t) (((jl_datatype_t*)t)->layout) STATIC_INLINE const jl_datatype_layout_t *jl_datatype_layout(jl_datatype_t *t) JL_NOTSAFEPOINT { - if (jl_is_layout_opaque(t->layout)) // e.g. GenericMemory + if (t->layout == NULL || jl_is_layout_opaque(t->layout)) // e.g. GenericMemory t = (jl_datatype_t*)jl_unwrap_unionall(t->name->wrapper); return t->layout; } diff --git a/test/core.jl b/test/core.jl index c81bc12e571da..af05a96b4b208 100644 --- a/test/core.jl +++ b/test/core.jl @@ -4904,6 +4904,9 @@ let ft = Base.datatype_fieldtypes @test !isdefined(ft(B12238.body.body)[1], :instance) # has free type vars end +# issue #54969 +@test !isdefined(Memory.body, :instance) + # `where` syntax in constructor definitions (A12238{T} where T<:Real)(x) = 0 @test A12238{<:Real}(0) == 0 From 16832c1fd801bbc4493c081912bfb5f19b943406 Mon Sep 17 00:00:00 2001 From: adienes <51664769+adienes@users.noreply.github.com> Date: Sat, 19 Apr 2025 09:05:09 -0400 Subject: [PATCH 03/46] Switch from segfault to `zip` behavior for mismatched indices in `map!` (#56673) (cherry picked from commit 0947114d9d443e14c751ac40c9b2d8c2245d045e) --- base/abstractarray.jl | 19 +++++++++++++------ test/abstractarray.jl | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index be78c308afa53..8d057a24a3235 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3420,12 +3420,19 @@ function ith_all(i, as) end function map_n!(f::F, dest::AbstractArray, As) where F - idxs1 = LinearIndices(As[1]) - @boundscheck LinearIndices(dest) == idxs1 && all(x -> LinearIndices(x) == idxs1, As) - for i = idxs1 - @inbounds I = ith_all(i, As) - val = f(I...) - @inbounds dest[i] = val + idxs = LinearIndices(dest) + if all(x -> LinearIndices(x) == idxs, As) + for i in idxs + @inbounds as = ith_all(i, As) + val = f(as...) + @inbounds dest[i] = val + end + else + for (i, Is...) in zip(eachindex(dest), map(eachindex, As)...) + as = ntuple(j->getindex(As[j], Is[j]), length(As)) + val = f(as...) + dest[i] = val + end end return dest end diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 8244d3fc791f5..1e52c913cfa30 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -889,6 +889,26 @@ include("generic_map_tests.jl") generic_map_tests(map, map!) @test_throws ArgumentError map!(-, [1]) +@testset "#30624" begin + ### unstructured + @test map!(+, ones(3), ones(3), ones(3), [1]) == [3, 1, 1] + @test map!(+, ones(3), [1], ones(3), ones(3)) == [3, 1, 1] + @test map!(+, [1], [1], [], []) == [1] + @test map!(+, [[1]], [1], [], []) == [[1]] + + # TODO: decide if input axes & lengths should be validated + # @test_throws BoundsError map!(+, ones(1), ones(2)) + # @test_throws BoundsError map!(+, ones(1), ones(2, 2)) + + @test map!(+, ones(3), view(ones(2, 3), 1:2, 2:3), ones(3)) == [2, 2, 2] + @test map!(+, ones(3), ones(2, 2), ones(3)) == [2, 2, 2] + + ### structured (all mapped arguments are <:AbstractArray equal ndims > 1) + @test map!(+, ones(4), ones(2, 2), ones(2, 2)) == [2, 2, 2, 2] + @test map!(+, ones(4), ones(2, 2), ones(1, 2)) == [2, 2, 1, 1] + # @test_throws BoundsError map!(+, ones(3), ones(2, 2), ones(2, 2)) +end + test_UInt_indexing(TestAbstractArray) test_13315(TestAbstractArray) test_checksquare() From 0840c0c8d6b723c13e5e6637e8fe018b034b6731 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sun, 20 Apr 2025 18:48:32 +0800 Subject: [PATCH 04/46] =?UTF-8?q?subtype:=20save=20some=20union=20stack=20?= =?UTF-8?q?space=20for=20=E2=88=83=20free=20cases.=20(#58159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and avoid eager UnionAll unwrapping to hit more fast path. close #58129 (test passed locally) close #56350 (MWE returns `Tuple{Any, Any, Vararg}` now.) (cherry picked from commit 334c3167e853daeb11e5121d2041de657e175726) --- src/subtype.c | 34 +++++++++++++++++++++++++--------- test/subtype.jl | 14 ++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 1745e5b9ec11d..a624e5bb3bec8 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -139,7 +139,7 @@ static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED J static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT { - assert(i >= 0 && i <= 32767); // limited by the depth bit. + assert(i >= 0 && i < 32767); // limited by the depth bit. // get the `i`th bit in an array of 32-bit words jl_bits_stack_t *stack = &st->stack; while (i >= sizeof(stack->data) * 8) { @@ -153,7 +153,7 @@ static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT static void statestack_set(jl_unionstate_t *st, int i, int val) JL_NOTSAFEPOINT { - assert(i >= 0 && i <= 32767); // limited by the depth bit. + assert(i >= 0 && i < 32767); // limited by the depth bit. jl_bits_stack_t *stack = &st->stack; while (i >= sizeof(stack->data) * 8) { if (__unlikely(stack->next == NULL)) { @@ -1437,11 +1437,14 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) } if (jl_is_unionall(y)) { jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)x); - if (xb == NULL ? !e->ignore_free : !xb->right) { + jl_value_t *xub = xb == NULL ? ((jl_tvar_t *)x)->ub : xb->ub; + if ((xb == NULL ? !e->ignore_free : !xb->right) && xub != y) { // We'd better unwrap `y::UnionAll` eagerly if `x` isa ∀-var. // This makes sure the following cases work correct: // 1) `∀T <: Union{∃S, SomeType{P}} where {P}`: `S == Any` ==> `S >: T` // 2) `∀T <: Union{∀T, SomeType{P}} where {P}`: + // note: if xub == y we'd better try `subtype_var` as `subtype_left_var` + // hit `==` based fast path. return subtype_unionall(x, (jl_unionall_t*)y, e, 1, param); } } @@ -1579,6 +1582,8 @@ static int has_exists_typevar(jl_value_t *x, jl_stenv_t *e) JL_NOTSAFEPOINT return env != NULL && jl_has_bound_typevars(x, env); } +static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param); + static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int limit_slow) { int16_t oldRmore = e->Runions.more; @@ -1592,7 +1597,18 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t return jl_subtype(x, y); int has_exists = (!kindx && has_exists_typevar(x, e)) || (!kindy && has_exists_typevar(y, e)); - if (has_exists && (is_exists_typevar(x, e) != is_exists_typevar(y, e))) { + if (!has_exists) { + // We can use ∀_∃_subtype safely for ∃ free inputs. + // This helps to save some bits in union stack. + jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); + e->Lunions.used = e->Runions.used = 0; + e->Lunions.depth = e->Runions.depth = 0; + e->Lunions.more = e->Runions.more = 0; + sub = forall_exists_subtype(x, y, e, param); + pop_unionstate(&e->Runions, &oldRunions); + return sub; + } + if (is_exists_typevar(x, e) != is_exists_typevar(y, e)) { e->Lunions.used = 0; while (1) { e->Lunions.more = 0; @@ -1606,7 +1622,7 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t if (limit_slow == -1) limit_slow = kindx || kindy; jl_savedenv_t se; - save_env(e, &se, has_exists); + save_env(e, &se, 1); int count, limited = 0, ini_count = 0; jl_saved_unionstate_t latestLunions = {0, 0, 0, NULL}; while (1) { @@ -1624,13 +1640,13 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t limited = 1; if (!sub || !next_union_state(e, 0)) break; - if (limited || !has_exists || e->Runions.more == oldRmore) { + if (limited || e->Runions.more == oldRmore) { // re-save env and freeze the ∃decision for previous ∀Union // Note: We could ignore the rest `∃Union` decisions if `x` and `y` // contain no ∃ typevar, as they have no effect on env. ini_count = count; push_unionstate(&latestLunions, &e->Lunions); - re_save_env(e, &se, has_exists); + re_save_env(e, &se, 1); e->Runions.more = oldRmore; } } @@ -1638,12 +1654,12 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t break; assert(e->Runions.more > oldRmore); next_union_state(e, 1); - restore_env(e, &se, has_exists); // also restore Rdepth here + restore_env(e, &se, 1); // also restore Rdepth here e->Runions.more = oldRmore; } if (!sub) assert(e->Runions.more == oldRmore); - else if (limited || !has_exists) + else if (limited) e->Runions.more = oldRmore; free_env(&se); return sub; diff --git a/test/subtype.jl b/test/subtype.jl index d186262a6e1ba..da69022b1466e 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2768,3 +2768,17 @@ end Tuple{Type{Complex{T}} where T, Type{Complex{T}} where T, Type{String}}, Tuple{Type{Complex{T}}, Type{Complex{T}}, Type{String}} where T ) + +#issue 58129 +for k in 1:500 + @eval struct $(Symbol(:T58129, k)){T} end +end +let Tvar = TypeVar(:Tvar) + V = UnionAll(Tvar, Union{(@eval($(Symbol(:T58129, k)){$Tvar}) for k in 1:500)...}) + @test Set{<:V} <: AbstractSet{<:V} +end +let Tvar1 = TypeVar(:Tvar1), Tvar2 = TypeVar(:Tvar2) + V1 = UnionAll(Tvar1, Union{(@eval($(Symbol(:T58129, k)){$Tvar1}) for k in 1:100)...}) + V2 = UnionAll(Tvar2, Union{(@eval($(Symbol(:T58129, k)){$Tvar2}) for k in 1:100)...}) + @test Set{<:V2} <: AbstractSet{<:V1} +end From 5ad6ee6704bb2e0b5b35718e782275af067e977c Mon Sep 17 00:00:00 2001 From: adienes <51664769+adienes@users.noreply.github.com> Date: Thu, 24 Apr 2025 07:39:38 -0700 Subject: [PATCH 05/46] Narrow `@inbounds` annotations in `accumulate.jl` to only indexing calls (#58200) `op` should not be `inbounds`-ed here the test case I added might technically be an illegal (or at least bad) use of `@propagate_inbounds` in case anyone has a suggestion for a more natural test case, but nonetheless it demonstrates getting a `BoundsError` instead of a segfault (worse) or incorrect / junk data (more worse), both of which happen on master (cherry picked from commit bdab032dbd80b6469353cd7fb8389c09512b2a9f) --- base/accumulate.jl | 55 +++++++++++++++++++++++----------------- test/boundscheck_exec.jl | 7 +++++ 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/base/accumulate.jl b/base/accumulate.jl index a2d8a1d368d86..4265f6ef67cc8 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -5,12 +5,14 @@ # it does double the number of operations compared to accumulate, # though for cheap operations like + this does not have much impact (20%) function _accumulate_pairwise!(op::Op, c::AbstractVector{T}, v::AbstractVector, s, i1, n)::T where {T,Op} - @inbounds if n < 128 - s_ = v[i1] - c[i1] = op(s, s_) + if n < 128 + @inbounds s_ = v[i1] + ci1 = op(s, s_) + @inbounds c[i1] = ci1 for i = i1+1:i1+n-1 - s_ = op(s_, v[i]) - c[i] = op(s, s_) + s_ = op(s_, @inbounds(v[i])) + ci = op(s, s_) + @inbounds c[i] = ci end else n2 = n >> 1 @@ -26,7 +28,8 @@ function accumulate_pairwise!(op::Op, result::AbstractVector, v::AbstractVector) n = length(li) n == 0 && return result i1 = first(li) - @inbounds result[i1] = v1 = reduce_first(op,v[i1]) + v1 = reduce_first(op, @inbounds(v[i1])) + @inbounds result[i1] = v1 n == 1 && return result _accumulate_pairwise!(op, result, v, v1, i1+1, n-1) return result @@ -379,16 +382,16 @@ function _accumulate!(op, B, A, dims::Integer, init::Union{Nothing, Some}) # We can accumulate to a temporary variable, which allows # register usage and will be slightly faster ind1 = inds_t[1] - @inbounds for I in CartesianIndices(tail(inds_t)) + for I in CartesianIndices(tail(inds_t)) if init === nothing - tmp = reduce_first(op, A[first(ind1), I]) + tmp = reduce_first(op, @inbounds(A[first(ind1), I])) else - tmp = op(something(init), A[first(ind1), I]) + tmp = op(something(init), @inbounds(A[first(ind1), I])) end - B[first(ind1), I] = tmp + @inbounds B[first(ind1), I] = tmp for i_1 = first(ind1)+1:last(ind1) - tmp = op(tmp, A[i_1, I]) - B[i_1, I] = tmp + tmp = op(tmp, @inbounds(A[i_1, I])) + @inbounds B[i_1, I] = tmp end end else @@ -402,12 +405,15 @@ end @noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Nothing) # Copy the initial element in each 1d vector along dimension `dim` ii = first(ind) - @inbounds for J in R2, I in R1 - B[I, ii, J] = reduce_first(op, A[I, ii, J]) + for J in R2, I in R1 + tmp = reduce_first(op, @inbounds(A[I, ii, J])) + @inbounds B[I, ii, J] = tmp end # Accumulate - @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 - B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) + for J in R2, i in first(ind)+1:last(ind), I in R1 + @inbounds Bv, Av = B[I, i-1, J], A[I, i, J] + tmp = op(Bv, Av) + @inbounds B[I, i, J] = tmp end B end @@ -415,12 +421,15 @@ end @noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Some) # Copy the initial element in each 1d vector along dimension `dim` ii = first(ind) - @inbounds for J in R2, I in R1 - B[I, ii, J] = op(something(init), A[I, ii, J]) + for J in R2, I in R1 + tmp = op(something(init), @inbounds(A[I, ii, J])) + @inbounds B[I, ii, J] = tmp end # Accumulate - @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 - B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) + for J in R2, i in first(ind)+1:last(ind), I in R1 + @inbounds Bv, Av = B[I, i-1, J], A[I, i, J] + tmp = op(Bv, Av) + @inbounds B[I, i, J] = tmp end B end @@ -434,10 +443,10 @@ function _accumulate1!(op, B, v1, A::AbstractVector, dim::Integer) cur_val = v1 B[i1] = cur_val next = iterate(inds, state) - @inbounds while next !== nothing + while next !== nothing (i, state) = next - cur_val = op(cur_val, A[i]) - B[i] = cur_val + cur_val = op(cur_val, @inbounds(A[i])) + @inbounds B[i] = cur_val next = iterate(inds, state) end return B diff --git a/test/boundscheck_exec.jl b/test/boundscheck_exec.jl index 10f46eb4a8031..78781a40fea32 100644 --- a/test/boundscheck_exec.jl +++ b/test/boundscheck_exec.jl @@ -239,6 +239,13 @@ if bc_opt != bc_off @test_throws BoundsError BadVector20469([1,2,3])[:] end +# Accumulate: do not set inbounds context for user-supplied functions +if bc_opt != bc_off + Base.@propagate_inbounds op58200(a, b) = (1, 2)[a] + (1, 2)[b] + @test_throws BoundsError accumulate(op58200, 1:10) + @test_throws BoundsError Base.accumulate_pairwise(op58200, 1:10) +end + # Ensure iteration over arrays is vectorizable function g27079(X) r = 0 From 3997241d2ab5d8bf5e527e6478729bfa93e016c9 Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Mon, 28 Apr 2025 23:50:44 +0200 Subject: [PATCH 06/46] [release-1.11] malloc: use jl_get_current_task to fix null check (#58202) --- src/gc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gc.c b/src/gc.c index 28e7ce3e4e91f..7dc7b2770c3ee 100644 --- a/src/gc.c +++ b/src/gc.c @@ -4119,7 +4119,7 @@ JL_DLLEXPORT void *jl_realloc(void *p, size_t sz) JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz) { - jl_task_t *ct = jl_current_task; + jl_task_t *ct = jl_get_current_task(); void *data = malloc(sz); if (data != NULL && ct != NULL && ct->world_age) { sz = memory_block_usable_size(data, 0); @@ -4136,7 +4136,7 @@ JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz) JL_DLLEXPORT void *jl_gc_counted_calloc(size_t nm, size_t sz) { - jl_task_t *ct = jl_current_task; + jl_task_t *ct = jl_get_current_task(); void *data = calloc(nm, sz); if (data != NULL && ct != NULL && ct->world_age) { sz = memory_block_usable_size(data, 0); From 33786cf84823df1f79da59436c5fd5b5479a6634 Mon Sep 17 00:00:00 2001 From: gbaraldi Date: Mon, 12 May 2025 16:32:37 -0300 Subject: [PATCH 07/46] Backport of 088bb90 (Fix removal of globals with addrspaces in removeAddrspaces (#58322)) --- src/llvm-remove-addrspaces.cpp | 4 ++-- test/llvmpasses/remove-addrspaces.ll | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index e291e47b59b0e..bd0fd14d700de 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -270,7 +270,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) Name, (GlobalVariable *)nullptr, GV->getThreadLocalMode(), - GV->getType()->getAddressSpace()); + cast(TypeRemapper.remapType(GV->getType()))->getAddressSpace()); NGV->copyAttributesFrom(GV); VMap[GV] = NGV; } @@ -290,7 +290,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) auto *NGA = GlobalAlias::create( TypeRemapper.remapType(GA->getValueType()), - GA->getType()->getPointerAddressSpace(), + cast(TypeRemapper.remapType(GA->getType()))->getAddressSpace(), GA->getLinkage(), Name, &M); diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index 90043a7d85cf4..756d05b98dd8b 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -11,6 +11,10 @@ ; TYPED-SAME: {}* ({}***, {}*, [1 x i64]*)* null ; OPAQUE-SAME: ptr null +; COM: check that the addrspace of the global itself is removed +; OPAQUE: @ejl_enz_runtime_exc = external global {} +@ejl_enz_runtime_exc = external addrspace(10) global {} + define i64 @getindex({} addrspace(10)* nonnull align 16 dereferenceable(40)) { ; CHECK-LABEL: @getindex top: @@ -121,6 +125,13 @@ define void @byval_type([1 x {} addrspace(10)*] addrspace(11)* byval([1 x {} add ret void } +define private fastcc void @diffejulia__mapreduce_97() { +L6: +; OPAQUE: store atomic ptr @ejl_enz_runtime_exc, ptr null unordered + store atomic {} addrspace(10)* @ejl_enz_runtime_exc, {} addrspace(10)* addrspace(10)* null unordered, align 8 + unreachable +} + ; COM: check that function attributes are preserved on declarations too declare void @convergent_function() #0 From 066228c58e1e880356f81b3576c485958c29d656 Mon Sep 17 00:00:00 2001 From: Jakob Nybo Nissen Date: Mon, 27 May 2024 16:09:54 +0200 Subject: [PATCH 08/46] Fix typo in unsafe_wrap method (#54582) (cherry picked from commit 58ed79361f4faad0da8d96aeaffbd8d75ebbb386) --- base/pointer.jl | 2 +- test/core.jl | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/base/pointer.jl b/base/pointer.jl index 2f67c8a6757cb..47198c503c612 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -118,7 +118,7 @@ end function unsafe_wrap(::Union{Type{GenericMemory{kind,<:Any,Core.CPU}},Type{GenericMemory{kind,T,Core.CPU}}}, p::Ptr{T}, dims::Tuple{Int}; own::Bool = false) where {kind,T} ccall(:jl_ptr_to_genericmemory, Ref{GenericMemory{kind,T,Core.CPU}}, - (Any, Ptr{Cvoid}, Csize_t, Cint), GenericMemory{kind,T,Core.CPU}, p, dim[1], own) + (Any, Ptr{Cvoid}, Csize_t, Cint), GenericMemory{kind,T,Core.CPU}, p, dims[1], own) end function unsafe_wrap(::Union{Type{GenericMemory{kind,<:Any,Core.CPU}},Type{GenericMemory{kind,T,Core.CPU}}}, p::Ptr{T}, d::Integer; own::Bool = false) where {kind,T} diff --git a/test/core.jl b/test/core.jl index af05a96b4b208..3e934da49f694 100644 --- a/test/core.jl +++ b/test/core.jl @@ -6258,6 +6258,16 @@ let @test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Nothing}}, pointer(A5)), 6) end +# More unsafe_wrap +let + a = [1, 2, 3] + GC.@preserve a begin + m = unsafe_wrap(Memory{Int}, pointer(a), (3,)) + @test m == a + @test m isa Memory{Int} + end +end + # copyto! A23567 = Vector{Union{Float64, Nothing}}(undef, 5) B23567 = collect(Union{Float64, Nothing}, 1.0:3.0) From 63aaa307a742d86419a522ab637c5e55513faa17 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 4 Apr 2025 09:30:32 -0400 Subject: [PATCH 09/46] Logging: Improve threadsafety (#57591) Closes https://github.com/JuliaLang/julia/issues/57376 Closes https://github.com/JuliaLang/julia/issues/34037 - Adds a lock in `SimpleLogger` and `ConsoleLogger` for use on maxlog tracking and stream writes to improve threadsafety. Closely similar to https://github.com/JuliaLang/julia/pull/54497 - Turns the internal `_min_enabled_level` into a `Threads.Atomic`. There are [some direct interactions](https://juliahub.com/ui/Search?type=code&q=_min_enabled_level&w=true) to this internal in the ecosystem, but they should still work ``` julia> Base.CoreLogging._min_enabled_level[] = Logging.Info+1 LogLevel(1) ``` - Brings tests over from https://github.com/JuliaLang/julia/pull/57448 Performance seems highly similar: ### Master ``` julia> @time for i in 1:10000 @info "foo" maxlog=10000000 end [ Info: foo ... 0.481446 seconds (1.33 M allocations: 89.226 MiB, 0.49% gc time) ``` ### This PR ``` 0.477235 seconds (1.31 M allocations: 79.002 MiB, 1.77% gc time) ``` (cherry picked from commit 9af96508e9715e22154fc7b5a7283ad41d23765a) --- base/logging/ConsoleLogger.jl | 21 +++++++++----- base/logging/logging.jl | 36 ++++++++++++++++-------- stdlib/Logging/test/runtests.jl | 43 +++++++++++++++++++++++++++++ stdlib/Logging/test/threads_exec.jl | 13 +++++++++ stdlib/Test/src/logging.jl | 2 +- 5 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 stdlib/Logging/test/threads_exec.jl diff --git a/base/logging/ConsoleLogger.jl b/base/logging/ConsoleLogger.jl index 818b2272b773c..8766d0ae56331 100644 --- a/base/logging/ConsoleLogger.jl +++ b/base/logging/ConsoleLogger.jl @@ -9,6 +9,9 @@ interactive work with the Julia REPL. Log levels less than `min_level` are filtered out. +This Logger is thread-safe, with locks for both orchestration of message +limits i.e. `maxlog`, and writes to the stream. + Message formatting can be controlled by setting keyword arguments: * `meta_formatter` is a function which takes the log event metadata @@ -24,6 +27,7 @@ Message formatting can be controlled by setting keyword arguments: """ struct ConsoleLogger <: AbstractLogger stream::IO + lock::ReentrantLock # do not log within this lock min_level::LogLevel meta_formatter show_limited::Bool @@ -33,19 +37,19 @@ end function ConsoleLogger(stream::IO, min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) - ConsoleLogger(stream, min_level, meta_formatter, + ConsoleLogger(stream, ReentrantLock(), min_level, meta_formatter, show_limited, right_justify, Dict{Any,Int}()) end function ConsoleLogger(min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) - ConsoleLogger(closed_stream, min_level, meta_formatter, + ConsoleLogger(closed_stream, ReentrantLock(), min_level, meta_formatter, show_limited, right_justify, Dict{Any,Int}()) end shouldlog(logger::ConsoleLogger, level, _module, group, id) = - get(logger.message_limits, id, 1) > 0 + @lock logger.lock get(logger.message_limits, id, 1) > 0 min_enabled_level(logger::ConsoleLogger) = logger.min_level @@ -109,9 +113,11 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module hasmaxlog = haskey(kwargs, :maxlog) ? 1 : 0 maxlog = get(kwargs, :maxlog, nothing) if maxlog isa Core.BuiltinInts - remaining = get!(logger.message_limits, id, Int(maxlog)::Int) - logger.message_limits[id] = remaining - 1 - remaining > 0 || return + @lock logger.lock begin + remaining = get!(logger.message_limits, id, Int(maxlog)::Int) + remaining == 0 && return + logger.message_limits[id] = remaining - 1 + end end # Generate a text representation of the message and all key value pairs, @@ -184,6 +190,7 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module println(iob) end - write(stream, take!(buf)) + b = take!(buf) + @lock logger.lock write(stream, b) nothing end diff --git a/base/logging/logging.jl b/base/logging/logging.jl index c1602abcdbe4a..cf0c89b46f07f 100644 --- a/base/logging/logging.jl +++ b/base/logging/logging.jl @@ -132,6 +132,7 @@ isless(a::LogLevel, b::LogLevel) = isless(a.level, b.level) +(level::LogLevel, inc::Integer) = LogLevel(level.level+inc) -(level::LogLevel, inc::Integer) = LogLevel(level.level-inc) convert(::Type{LogLevel}, level::Integer) = LogLevel(level) +convert(::Type{Int32}, level::LogLevel) = level.level """ BelowMinLevel @@ -171,7 +172,8 @@ Alias for [`LogLevel(1_000_001)`](@ref LogLevel). const AboveMaxLevel = LogLevel( 1000001) # Global log limiting mechanism for super fast but inflexible global log limiting. -const _min_enabled_level = Ref{LogLevel}(Debug) +# Atomic ensures that the value is always consistent across threads. +const _min_enabled_level = Threads.Atomic{Int32}(Debug) function show(io::IO, level::LogLevel) if level == BelowMinLevel print(io, "BelowMinLevel") @@ -394,7 +396,7 @@ function logmsg_code(_module, file, line, level, message, exs...) level = $level # simplify std_level code emitted, if we know it is one of our global constants std_level = $(level isa Symbol ? :level : :(level isa $LogLevel ? level : convert($LogLevel, level)::$LogLevel)) - if std_level >= $(_min_enabled_level)[] + if std_level.level >= $(_min_enabled_level)[] group = $(log_data._group) _module = $(log_data._module) logger = $(current_logger_for_env)(std_level, group, _module) @@ -529,7 +531,8 @@ with_logstate(f::Function, logstate) = @with(CURRENT_LOGSTATE => logstate, f()) Disable all log messages at log levels equal to or less than `level`. This is a *global* setting, intended to make debug logging extremely cheap when -disabled. +disabled. Note that this cannot be used to enable logging that is currently disabled +by other mechanisms. # Examples ```julia @@ -651,17 +654,21 @@ close(closed_stream) Simplistic logger for logging all messages with level greater than or equal to `min_level` to `stream`. If stream is closed then messages with log level greater or equal to `Warn` will be logged to `stderr` and below to `stdout`. + +This Logger is thread-safe, with a lock taken around orchestration of message +limits i.e. `maxlog`, and writes to the stream. """ struct SimpleLogger <: AbstractLogger stream::IO + lock::ReentrantLock min_level::LogLevel message_limits::Dict{Any,Int} end -SimpleLogger(stream::IO, level=Info) = SimpleLogger(stream, level, Dict{Any,Int}()) +SimpleLogger(stream::IO, level=Info) = SimpleLogger(stream, ReentrantLock(), level, Dict{Any,Int}()) SimpleLogger(level=Info) = SimpleLogger(closed_stream, level) shouldlog(logger::SimpleLogger, level, _module, group, id) = - get(logger.message_limits, id, 1) > 0 + @lock logger.lock get(logger.message_limits, id, 1) > 0 min_enabled_level(logger::SimpleLogger) = logger.min_level @@ -672,15 +679,14 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module, @nospecialize maxlog = get(kwargs, :maxlog, nothing) if maxlog isa Core.BuiltinInts - remaining = get!(logger.message_limits, id, Int(maxlog)::Int) - logger.message_limits[id] = remaining - 1 - remaining > 0 || return + @lock logger.lock begin + remaining = get!(logger.message_limits, id, Int(maxlog)::Int) + remaining == 0 && return + logger.message_limits[id] = remaining - 1 + end end buf = IOBuffer() stream::IO = logger.stream - if !(isopen(stream)::Bool) - stream = stderr - end iob = IOContext(buf, stream) levelstr = level == Warn ? "Warning" : string(level) msglines = eachsplit(chomp(convert(String, string(message))::String), '\n') @@ -694,7 +700,13 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module, println(iob, "│ ", key, " = ", val) end println(iob, "└ @ ", _module, " ", filepath, ":", line) - write(stream, take!(buf)) + b = take!(buf) + @lock logger.lock begin + if !(isopen(stream)::Bool) + stream = stderr + end + write(stream, b) + end nothing end diff --git a/stdlib/Logging/test/runtests.jl b/stdlib/Logging/test/runtests.jl index 176860fcdec63..7250fc62d058e 100644 --- a/stdlib/Logging/test/runtests.jl +++ b/stdlib/Logging/test/runtests.jl @@ -306,4 +306,47 @@ end @test isempty(undoc) end +@testset "Logging when multithreaded" begin + n = 10000 + cmd = `$(Base.julia_cmd()) -t4 --color=no $(joinpath(@__DIR__, "threads_exec.jl")) $n` + fname = tempname() + @testset "Thread safety" begin + f = open(fname, "w") + @test success(run(pipeline(cmd, stderr=f))) + close(f) + end + + @testset "No tearing in log printing" begin + # Check for print tearing by verifying that each log entry starts and ends correctly + f = open(fname, "r") + entry_start = r"┌ (Info|Warning|Error): iteration" + entry_end = r"└ " + + open_entries = 0 + total_entries = 0 + for line in eachline(fname) + starts = count(entry_start, line) + starts > 1 && error("Interleaved logs: Multiple log entries started on one line") + if starts == 1 + startswith(line, entry_start) || error("Interleaved logs: Log entry started in the middle of a line") + open_entries += 1 + total_entries += 1 + end + + ends = count(entry_end, line) + starts == 1 && ends == 1 && error("Interleaved logs: Log entry started and and another ended on one line") + ends > 1 && error("Interleaved logs: Multiple log entries ended on one line") + if ends == 1 + startswith(line, entry_end) || error("Interleaved logs: Log entry ended in the middle of a line") + open_entries -= 1 + end + # Ensure no mismatched log entries + open_entries >= 0 || error("Interleaved logs") + end + + @test open_entries == 0 # Ensure all entries closed properly + @test total_entries == n * 3 # Ensure all logs were printed (3 because @debug is hidden) + end +end + end diff --git a/stdlib/Logging/test/threads_exec.jl b/stdlib/Logging/test/threads_exec.jl new file mode 100644 index 0000000000000..497a22b1c7b22 --- /dev/null +++ b/stdlib/Logging/test/threads_exec.jl @@ -0,0 +1,13 @@ +using Logging + +function test_threads_exec(n) + Threads.@threads for i in 1:n + @debug "iteration" maxlog=1 _id=Symbol("$(i)_debug") i Threads.threadid() + @info "iteration" maxlog=1 _id=Symbol("$(i)_info") i Threads.threadid() + @warn "iteration" maxlog=1 _id=Symbol("$(i)_warn") i Threads.threadid() + @error "iteration" maxlog=1 _id=Symbol("$(i)_error") i Threads.threadid() + end +end + +n = parse(Int, ARGS[1]) +test_threads_exec(n) diff --git a/stdlib/Test/src/logging.jl b/stdlib/Test/src/logging.jl index b224d79e47cd9..a3a94a642f250 100644 --- a/stdlib/Test/src/logging.jl +++ b/stdlib/Test/src/logging.jl @@ -107,8 +107,8 @@ function Logging.handle_message(logger::TestLogger, level, msg, _module, if maxlog isa Core.BuiltinInts @lock logger.lock begin remaining = get!(logger.message_limits, id, Int(maxlog)::Int) + remaining == 0 && return logger.message_limits[id] = remaining - 1 - remaining > 0 || return end end end From f497d6246e222b6b8d33265beb7a02b30cf15404 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sun, 27 Apr 2025 12:53:58 +0800 Subject: [PATCH 10/46] Subtype: enable more Tuple related fast path. (#58196) Fix the subtyping hang found in https://github.com/JuliaLang/julia/issues/58115#issuecomment-2821388658 (cherry picked from commit c9ad04dcc73256bc24eb079f9f6506299b64b8ec) --- src/subtype.c | 38 +++++++++++++++++++++++++++++--------- test/subtype.jl | 4 ++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index a624e5bb3bec8..88e8086169502 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1069,7 +1069,8 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); static int subtype_tuple_varargs( jl_vararg_t *vtx, jl_vararg_t *vty, - size_t vx, size_t vy, + jl_value_t *lastx, jl_value_t *lasty, + size_t vx, size_t vy, size_t x_reps, jl_stenv_t *e, int param) { jl_value_t *xp0 = jl_unwrap_vararg(vtx); jl_value_t *xp1 = jl_unwrap_vararg_num(vtx); @@ -1111,12 +1112,30 @@ static int subtype_tuple_varargs( } } } - - // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to - // simulate the possibility of multiple arguments, which is needed - // to implement the diagonal rule correctly. - if (!subtype(xp0, yp0, e, param)) return 0; - if (!subtype(xp0, yp0, e, 1)) return 0; + int x_same = vx > 1 || (lastx && obviously_egal(xp0, lastx)); + int y_same = vy > 1 || (lasty && obviously_egal(yp0, lasty)); + // keep track of number of consecutive identical subtyping + x_reps = y_same && x_same ? x_reps + 1 : 1; + if (x_reps > 2) { + // an identical type on the left doesn't need to be compared to the same + // element type on the right more than twice. + } + else if (x_same && e->Runions.depth == 0 && y_same && + !jl_has_free_typevars(xp0) && !jl_has_free_typevars(yp0)) { + // fast path for repeated elements + } + else if ((e->Runions.depth == 0 ? !jl_has_free_typevars(xp0) : jl_is_concrete_type(xp0)) && !jl_has_free_typevars(yp0)) { + // fast path for separable sub-formulas + if (!jl_subtype(xp0, yp0)) + return 0; + } + else { + // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to + // simulate the possibility of multiple arguments, which is needed + // to implement the diagonal rule correctly. + if (!subtype(xp0, yp0, e, param)) return 0; + if (x_reps < 2 && !subtype(xp0, yp0, e, 1)) return 0; + } constrain_length: if (!yp1) { @@ -1246,7 +1265,8 @@ static int subtype_tuple_tail(jl_datatype_t *xd, jl_datatype_t *yd, int8_t R, jl return subtype_tuple_varargs( (jl_vararg_t*)xi, (jl_vararg_t*)yi, - vx, vy, e, param); + lastx, lasty, + vx, vy, x_reps, e, param); } if (j >= ly) @@ -1267,7 +1287,7 @@ static int subtype_tuple_tail(jl_datatype_t *xd, jl_datatype_t *yd, int8_t R, jl (yi == lastx && !vx && vy && jl_is_concrete_type(xi)))) { // fast path for repeated elements } - else if (e->Runions.depth == 0 && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) { + else if ((e->Runions.depth == 0 ? !jl_has_free_typevars(xi) : jl_is_concrete_type(xi)) && !jl_has_free_typevars(yi)) { // fast path for separable sub-formulas if (!jl_subtype(xi, yi)) return 0; diff --git a/test/subtype.jl b/test/subtype.jl index da69022b1466e..dbd761c7f5867 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2782,3 +2782,7 @@ let Tvar1 = TypeVar(:Tvar1), Tvar2 = TypeVar(:Tvar2) V2 = UnionAll(Tvar2, Union{(@eval($(Symbol(:T58129, k)){$Tvar2}) for k in 1:100)...}) @test Set{<:V2} <: AbstractSet{<:V1} end + +#issue 58115 +@test Tuple{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{ Union{Tuple{}, Tuple{Tuple{}}}}}}}}}}}}} , Tuple{}} <: + Tuple{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Union{Tuple{}, Tuple{Tuple{}}}}}}}}}}}}}}}, Tuple{}} From 471598353ef6a604bc36b9077034eed2cff905f5 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Tue, 29 Apr 2025 18:01:53 +0200 Subject: [PATCH 11/46] doc: cross-reference `bind` in `Channel` method doc string (#58113) (cherry picked from commit d4f2e8ab3758bc614c80535b13d487cac55a6c8b) --- base/channels.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/channels.jl b/base/channels.jl index 3ddf9cff7634c..09e5f1bb52423 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -61,7 +61,7 @@ Channel(sz=0) = Channel{Any}(sz) """ Channel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing) -Create a new task from `func`, bind it to a new channel of type +Create a new task from `func`, [`bind`](@ref) it to a new channel of type `T` and size `size`, and schedule the task, all in a single call. The channel is automatically closed when the task terminates. From ec2763b6d9e17afd5022d69193662ce8609fad6b Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Sat, 3 May 2025 12:33:46 -0300 Subject: [PATCH 12/46] Make build_id.lo more random (#58258) This does not fix the underlying issue that can occur here which is a collision of build_ids.lo between modules in IR decompression. Fixing that requires a somewhat significant overhaul to the serialization of IR (probably using the module identity as a key). This does mean we use a lot more of the bits available here so it makes collisions a lot less likely( they were already extremely rare) but hrtime does tend to only use the lower bits of a 64 bit integer and this will hopefully add some more randomness and make this even less likely (cherry picked from commit 71574073759506b0a3171640ea5e1b468f2757c9) --- src/module.c | 3 ++- src/staticdata.c | 6 +++++- src/staticdata_utils.c | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/module.c b/src/module.c index cb19c87d10ef6..0a793db08bd2a 100644 --- a/src/module.c +++ b/src/module.c @@ -25,7 +25,8 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui m->istopmod = 0; m->uuid = uuid_zero; static unsigned int mcounter; // simple counter backup, in case hrtime is not incrementing - m->build_id.lo = jl_hrtime() + (++mcounter); + // TODO: this is used for ir decompression and is liable to hash collisions so use more of the bits + m->build_id.lo = bitmix(jl_hrtime() + (++mcounter), jl_rand()); if (!m->build_id.lo) m->build_id.lo++; // build id 0 is invalid m->build_id.hi = ~(uint64_t)0; diff --git a/src/staticdata.c b/src/staticdata.c index 02ba108e75c78..cac3b78545cca 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3673,7 +3673,11 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i // No special processing of `new_ext_cis` is required because recaching handled it // Add roots to methods - jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); + int failed = jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); + if (failed != 0) { + jl_printf(JL_STDERR, "Error copying roots to methods from Module: %s\n", pkgname); + abort(); + } // Insert method extensions and handle edges JL_LOCK(&world_counter_lock); // allocate a world for the new methods, and insert them there, invalidating content as needed diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 49e292f8da514..8dd9403e5866b 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -947,17 +947,31 @@ static void jl_activate_methods(jl_array_t *external, jl_array_t *internal, size } } -static void jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) +static int jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) { size_t i, l = jl_array_nrows(method_roots_list); + int failed = 0; for (i = 0; i < l; i+=2) { jl_method_t *m = (jl_method_t*)jl_array_ptr_ref(method_roots_list, i); jl_array_t *roots = (jl_array_t*)jl_array_ptr_ref(method_roots_list, i+1); if (roots) { assert(jl_is_array(roots)); + if (m->root_blocks) { + // check for key collision + uint64_t *blocks = jl_array_data(m->root_blocks, uint64_t); + size_t nx2 = jl_array_nrows(m->root_blocks); + for (size_t i = 0; i < nx2; i+=2) { + if (blocks[i] == key) { + // found duplicate block + failed = -1; + } + } + } + jl_append_method_roots(m, key, roots); } } + return failed; } From fcb5a66c2565fb4bce7080e9fbc7da6d344da8e5 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 6 May 2025 06:08:40 -0300 Subject: [PATCH 13/46] Fix removal of globals with addrspaces in removeAddrspaces (#58322) (cherry picked from commit 088bb9002e95631738c8ec5ba58b7b8a7b33019d) --- test/llvmpasses/remove-addrspaces.ll | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index 756d05b98dd8b..6511a6671e449 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -4,6 +4,9 @@ ; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='RemoveJuliaAddrspaces' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE +; COM: check that the addrspace of the global itself is removed +; OPAQUE: @ejl_enz_runtime_exc = external global {} +@ejl_enz_runtime_exc = external addrspace(10) global {} ; COM: check that package image fptrs work @pjlsys_BoundsError_32 = internal global {} addrspace(10)* ({}***, {} addrspace(10)*, [1 x i64] addrspace(11)*)* null @@ -133,6 +136,13 @@ L6: } +define private fastcc void @diffejulia__mapreduce_97() { +L6: +; OPAQUE: store atomic ptr @ejl_enz_runtime_exc, ptr null unordered + store atomic {} addrspace(10)* @ejl_enz_runtime_exc, {} addrspace(10)* addrspace(10)* null unordered, align 8 + unreachable +} + ; COM: check that function attributes are preserved on declarations too declare void @convergent_function() #0 attributes #0 = { convergent } From bb6dc835ba98d4bedc07dcc0776d176afb07119d Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 14 May 2025 17:36:33 -0400 Subject: [PATCH 14/46] fix `hasmethod` with kwargs to exclude positional arg names (#58410) (cherry picked from commit 7df60f480df8c6aed874b35eb7c8a26fc769a4cc) --- base/reflection.jl | 2 +- test/reflection.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/reflection.jl b/base/reflection.jl index d70683feaaae7..396c9b2613644 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -2363,11 +2363,11 @@ function hasmethod(f, t, kwnames::Tuple{Vararg{Symbol}}; world::UInt=get_world_c match = ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tt, nothing, world) match === nothing && return false kws = ccall(:jl_uncompress_argnames, Array{Symbol,1}, (Any,), (match::Method).slot_syms) + kws = kws[((match::Method).nargs + 1):end] # remove positional arguments isempty(kws) && return true # some kwfuncs simply forward everything directly for kw in kws endswith(String(kw), "...") && return true end - kwnames = collect(kwnames) return issubset(kwnames, kws) end diff --git a/test/reflection.jl b/test/reflection.jl index 7a0fc9575f952..defa5a201ea51 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -836,6 +836,7 @@ f(x::Int; y=3) = x + y @test hasmethod(f, Tuple{Int}) @test hasmethod(f, Tuple{Int}, ()) @test hasmethod(f, Tuple{Int}, (:y,)) +@test !hasmethod(f, Tuple{Int}, (:x,)) @test !hasmethod(f, Tuple{Int}, (:jeff,)) @test !hasmethod(f, Tuple{Int}, (:y,), world=typemin(UInt)) g(; b, c, a) = a + b + c From 0aac5dbf00220dfdc623a84a352e9bf508c85c4d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 14 May 2025 22:54:15 -0400 Subject: [PATCH 15/46] [REPL] fix type confusion resulting in nonsensical errors (#58414) (cherry picked from commit a87b05665fa58dd0360b2a69e4468404a552cd00) --- stdlib/REPL/src/REPL.jl | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 2cc844068bc52..ddd75b020cb34 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -360,8 +360,8 @@ function repl_backend_loop(backend::REPLBackend, get_module::Function) try ret = f() put!(backend.response_channel, Pair{Any, Bool}(ret, false)) - catch err - put!(backend.response_channel, Pair{Any, Bool}(err, true)) + catch + put!(backend.response_channel, Pair{Any, Bool}(current_exceptions(), true)) end else ast = ast_or_func @@ -433,11 +433,11 @@ function print_response(errio::IO, response, backend::Union{REPLBackendRef,Nothi if val !== nothing && show_value val2, iserr = if specialdisplay === nothing # display calls may require being run on the main thread - eval_with_backend(backend) do + call_on_backend(backend) do Base.invokelatest(display, val) end else - eval_with_backend(backend) do + call_on_backend(backend) do Base.invokelatest(display, specialdisplay, val) end end @@ -554,7 +554,7 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef) (isa(ast,Expr) && ast.head === :incomplete) || break end if !isempty(line) - response = eval_with_backend(ast, backend) + response = eval_on_backend(ast, backend) print_response(repl, response, !ends_with_semicolon(line), false) end write(repl.terminal, '\n') @@ -997,21 +997,23 @@ find_hist_file() = get(ENV, "JULIA_HISTORY", backend(r::AbstractREPL) = hasproperty(r, :backendref) ? r.backendref : nothing -function eval_with_backend(ast::Expr, backend::REPLBackendRef) +function eval_on_backend(ast, backend::REPLBackendRef) put!(backend.repl_channel, (ast, 1)) # (f, show_value) return take!(backend.response_channel) # (val, iserr) end -function eval_with_backend(f, backend::REPLBackendRef) +function call_on_backend(f, backend::REPLBackendRef) + applicable(f) || error("internal error: f is not callable") put!(backend.repl_channel, (f, 2)) # (f, show_value) 2 indicates function (rather than ast) return take!(backend.response_channel) # (val, iserr) end # if no backend just eval (used by tests) -function eval_with_backend(f, backend::Nothing) +eval_on_backend(ast, backend::Nothing) = error("no backend for eval ast") +function call_on_backend(f, backend::Nothing) try ret = f() return (ret, false) # (val, iserr) - catch err - return (err, true) + catch + return (current_exceptions(), true) end end @@ -1027,7 +1029,7 @@ function respond(f, repl, main; pass_empty::Bool = false, suppress_on_semicolon: local response try ast = Base.invokelatest(f, line) - response = eval_with_backend(ast, backend(repl)) + response = eval_on_backend(ast, backend(repl)) catch response = Pair{Any, Bool}(current_exceptions(), true) end @@ -1667,7 +1669,7 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef) if have_color print(repl.stream, Base.color_normal) end - response = eval_with_backend(ast, backend) + response = eval_on_backend(ast, backend) print_response(repl, response, !ends_with_semicolon(line), have_color) end end From 9c02bf38b7975ee35186f2024a0394be50fc2e22 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Mon, 2 Jun 2025 03:14:22 +0200 Subject: [PATCH 16/46] fix Markdown in the `Lockable` doc strings (#58603) (cherry picked from commit 9da64559e3046cf9215c1cc752d301e735996473) --- base/lock.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/lock.jl b/base/lock.jl index b69f3c5c03638..1d63e51f08b15 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -295,7 +295,7 @@ macro lock_nofail(l, expr) end """ - Lockable(value, lock = ReentrantLock()) + Lockable(value, lock = ReentrantLock()) Creates a `Lockable` object that wraps `value` and associates it with the provided `lock`. This object @@ -328,7 +328,7 @@ Lockable(value) = Lockable(value, ReentrantLock()) getindex(l::Lockable) = (assert_havelock(l.lock); l.value) """ - lock(f::Function, l::Lockable) + lock(f::Function, l::Lockable) Acquire the lock associated with `l`, execute `f` with the lock held, and release the lock when `f` returns. `f` will receive one positional From 75aebb67cf8dae2c865e7dc8a31b6ec5209b6d07 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 2 Jun 2025 21:47:28 +0200 Subject: [PATCH 17/46] Update readlines(::Cmd) test to not rely on the filesystem (#58607) (cherry picked from commit 48c0e7f99c4e83d98761898e090ca3b5c6d3be90) --- test/spawn.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/spawn.jl b/test/spawn.jl index 831eac493d4aa..b0e5ca869fffe 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -609,7 +609,9 @@ end @test reduce(&, [`$echocmd abc`, `$echocmd def`, `$echocmd hij`]) == `$echocmd abc` & `$echocmd def` & `$echocmd hij` # readlines(::Cmd), accidentally broken in #20203 -@test sort(readlines(`$lscmd -A`)) == sort(readdir()) +let str = "foo\nbar" + @test readlines(`$echocmd $str`) == split(str) +end # issue #19864 (PR #20497) let c19864 = readchomp(pipeline(ignorestatus( From 0eb762cf2d1cf3dc74e5c0bd83b64ef38823512f Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 5 Jun 2025 08:49:12 -0400 Subject: [PATCH 18/46] Update install link in warning (#58638) (cherry picked from commit f5e983eafe6c879027217808a29a9c12bb27f5af) --- stdlib/InteractiveUtils/src/InteractiveUtils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index 661213567e0db..b5a52e26ebf02 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -119,7 +119,7 @@ function versioninfo(io::IO=stdout; verbose::Bool=false) Note: This is an unofficial build, please report bugs to the project responsible for this build and not to the Julia project unless you can - reproduce the issue using official builds available at https://julialang.org/downloads + reproduce the issue using official builds available at https://julialang.org """ ) end From 6a832a681053e7a2a6b39e6c806ed84b27f9e35d Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Sat, 7 Jun 2025 23:10:32 -0700 Subject: [PATCH 19/46] Try workaround for https://github.com/JuliaLang/www.julialang.org/issues/2291 This dodges the issue on my machine, let's see if it works for everyone. (cherry picked from commit dda37f97d6d41c01ecea793e9603fc51ff4d6789) --- contrib/mac/app/startup.applescript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/mac/app/startup.applescript b/contrib/mac/app/startup.applescript index 9964049f34ed6..45ccbbb977d25 100644 --- a/contrib/mac/app/startup.applescript +++ b/contrib/mac/app/startup.applescript @@ -1,4 +1,4 @@ set RootPath to (path to me) set JuliaPath to POSIX path of ((RootPath as text) & "Contents:Resources:julia:bin:julia") set JuliaFile to POSIX file JuliaPath -tell application id "com.apple.finder" to open JuliaFile +do shell script "open -a Terminal '" & JuliaFile & "'" From b1809861cc43f66b5ebc78c7ef9f08a6e3f95183 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Sun, 8 Jun 2025 16:25:59 -0700 Subject: [PATCH 20/46] Adjust applescript workaround It turns out that there are two path types in applescript, and I had mixed two of them in my previous patch. Annoyingly, things seemed to work when editing locally, unsure why. (cherry picked from commit c759aa933ed041e678d370dede7030c8893bbd10) --- contrib/mac/app/startup.applescript | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/mac/app/startup.applescript b/contrib/mac/app/startup.applescript index 45ccbbb977d25..d7b46cec1a89d 100644 --- a/contrib/mac/app/startup.applescript +++ b/contrib/mac/app/startup.applescript @@ -1,4 +1,3 @@ set RootPath to (path to me) set JuliaPath to POSIX path of ((RootPath as text) & "Contents:Resources:julia:bin:julia") -set JuliaFile to POSIX file JuliaPath -do shell script "open -a Terminal '" & JuliaFile & "'" +do shell script "open -a Terminal '" & JuliaPath & "'" From 40f6d6b5101d0a15a73de8de44cb1265151a598d Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 10 Jun 2025 00:27:37 -0400 Subject: [PATCH 21/46] Unicode: Force-inline isgraphemebreak! (#58674) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When this API was added, this function inlined, which is important, because the API relies on the allocation of the `Ref` being elided. At some point (I went back to 1.8) this regressed. For example, it is currently responsible for substantially all non-Expr allocations in JuliaParser. Before (parsing all of Base with JuliaParser): ``` │ Memory estimate: 76.93 MiB, allocs estimate: 719922. ``` After: ``` │ Memory estimate: 53.31 MiB, allocs estimate: 156. ``` Also add a test to make sure this doesn't regress again. (cherry picked from commit d6294ba973db1dea9dc932779008fd66d27c4bd2) --- base/strings/unicode.jl | 2 +- stdlib/Unicode/test/runtests.jl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl index 2e142be901b0b..d57b95c2e75c6 100644 --- a/base/strings/unicode.jl +++ b/base/strings/unicode.jl @@ -776,7 +776,7 @@ isgraphemebreak(c1::AbstractChar, c2::AbstractChar) = # Stateful grapheme break required by Unicode-9 rules: the string # must be processed in sequence, with state initialized to Ref{Int32}(0). # Requires utf8proc v2.0 or later. -function isgraphemebreak!(state::Ref{Int32}, c1::AbstractChar, c2::AbstractChar) +@inline function isgraphemebreak!(state::Ref{Int32}, c1::AbstractChar, c2::AbstractChar) if ismalformed(c1) || ismalformed(c2) state[] = 0 return true diff --git a/stdlib/Unicode/test/runtests.jl b/stdlib/Unicode/test/runtests.jl index 7fa57508cffbf..2af7015afa249 100644 --- a/stdlib/Unicode/test/runtests.jl +++ b/stdlib/Unicode/test/runtests.jl @@ -284,6 +284,8 @@ end @test_throws BoundsError graphemes("äöüx", 2:5) @test_throws BoundsError graphemes("äöüx", 5:5) @test_throws ArgumentError graphemes("äöüx", 0:1) + + @test @allocated(length(graphemes("äöüx"))) == 0 end @testset "#3721, #6939 up-to-date character widths" begin From 520c4c198a1bb5b412ac42b2f741ee50510324a9 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 11 Jun 2025 01:14:38 -0400 Subject: [PATCH 22/46] Test: Fix failfast for for loops (#58695) (cherry picked from commit 8567a3a10f4b746b91bf406bfe3171c3399aed8d) --- stdlib/Test/src/Test.jl | 10 ++++++++-- stdlib/Test/test/runtests.jl | 28 +++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index b3711398c2e83..c01ab21ef6637 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1616,6 +1616,10 @@ end trigger_test_failure_break(@nospecialize(err)) = ccall(:jl_test_failure_breakpoint, Cvoid, (Any,), err) +is_failfast_error(err::FailFastError) = true +is_failfast_error(err::LoadError) = is_failfast_error(err.error) # handle `include` barrier +is_failfast_error(err) = false + """ Generate the code for an `@testset` with a `let` argument. """ @@ -1708,7 +1712,7 @@ function testset_beginend_call(args, tests, source) # something in the test block threw an error. Count that as an # error in this test set trigger_test_failure_break(err) - if err isa FailFastError + if is_failfast_error(err) get_testset_depth() > 1 ? rethrow() : failfast_print() else record(ts, Error(:nontest_error, Expr(:tuple), err, Base.current_exceptions(), $(QuoteNode(source)))) @@ -1796,7 +1800,9 @@ function testset_forloop(args, testloop, source) # Something in the test block threw an error. Count that as an # error in this test set trigger_test_failure_break(err) - if !isa(err, FailFastError) + if is_failfast_error(err) + get_testset_depth() > 1 ? rethrow() : failfast_print() + else record(ts, Error(:nontest_error, Expr(:tuple), err, Base.current_exceptions(), $(QuoteNode(source)))) end end diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index 0a833c33aa026..ce9d4d7bab9bc 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -1348,7 +1348,7 @@ end @test occursin(expected, result) end end - @testset "failfast" begin + @testset "failfast begin-end" begin expected = r""" Test Summary: | Fail Total Time Foo | 1 1 \s*\d*.\ds @@ -1373,6 +1373,32 @@ end @test occursin(expected, result) end end + @testset "failfast for-loop" begin + expected = r""" + Test Summary: \| Fail Total +Time + Foo \| 1 1 \s*\d*\.\ds + 1 \| 1 1 \s*\d*\.\ds + """ + mktemp() do f, _ + write(f, + """ + using Test + + @testset "Foo" failfast=true begin + @testset "\$x" for x in 1:2 + @test false + end + @testset "Bar" begin + @test false + @test true + end + end + """) + cmd = `$(Base.julia_cmd()) --startup-file=no --color=no $f` + result = read(pipeline(ignorestatus(cmd), stderr=devnull), String) + @test occursin(expected, result) + end + end @testset "failfast passes to child testsets" begin expected = r""" Test Summary: | Fail Total Time From a0217139af037e1f7f0c75ea5d93b98813cfe27f Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 17 Jun 2025 15:07:28 +0530 Subject: [PATCH 23/46] Increment state conditionally in `CartesianIndices` iteration (#58742) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/JuliaLang/julia/issues/53430 ```julia julia> a = rand(100,100); b = similar(a); av = view(a, axes(a)...); bv = view(b, axes(b)...); bv2 = view(b, UnitRange.(axes(b))...); julia> @btime copyto!($bv2, $av); # slow, indices are UnitRanges 12.352 μs (0 allocations: 0 bytes) # master, v"1.13.0-DEV.745" 1.662 μs (0 allocations: 0 bytes) # this PR julia> @btime copyto!($bv, $av); # reference 1.733 μs (0 allocations: 0 bytes) ``` The performances become comparable after this PR. I've also renamed the second `I` to `Itail`, as the two variables represent different quantities. (cherry picked from commit 36a4616fc218479b09607b1c15e61a36f12d13ad) --- base/multidimensional.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 730a165cde18f..96b5582406e60 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -443,12 +443,12 @@ module IteratorsMD end @inline function __inc(state::Tuple{Int,Int,Vararg{Int}}, indices::Tuple{OrdinalRangeInt,OrdinalRangeInt,Vararg{OrdinalRangeInt}}) rng = indices[1] - I = state[1] + step(rng) if state[1] != last(rng) + I = state[1] + step(rng) return true, (I, tail(state)...) end - valid, I = __inc(tail(state), tail(indices)) - return valid, (first(rng), I...) + valid, Itail = __inc(tail(state), tail(indices)) + return valid, (first(rng), Itail...) end # 0-d cartesian ranges are special-cased to iterate once and only once From af00075a51036175e45c47cd9fe141817ec796d2 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 20 Jun 2025 12:39:54 -0400 Subject: [PATCH 24/46] Test: Hide REPL internals in backtraces (#58732) (cherry picked from commit 7b6065e9f25bca60f5ed21532a0163d760a7dbf8) --- stdlib/Test/src/Test.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index c01ab21ef6637..59fb4cd316c92 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -230,7 +230,8 @@ struct Error <: Result end if test_type === :test_error || test_type === :nontest_error bt_str = try # try the latest world for this, since we might have eval'd new code for show - Base.invokelatest(sprint, Base.show_exception_stack, bt; context=stdout) + # Apply REPL backtrace scrubbing to hide REPL internals, similar to how REPL.jl handles it + Base.invokelatest(sprint, Base.show_exception_stack, Base.scrub_repl_backtrace(bt); context=stdout) catch ex "#=ERROR showing exception stack=# " * try From 56f233fe369adf687ac61bc2db20e943b4443822 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 10 Jun 2025 16:49:55 -0300 Subject: [PATCH 25/46] Add 0 predecessor to entry basic block and handle it in inlining (#58683) (cherry picked from commit da004516e7b53a5cfd264170fcb3ea33f9ea0bc6) --- base/compiler/ssair/inlining.jl | 19 +++++++++++-------- base/compiler/ssair/ir.jl | 3 +++ test/compiler/ssair.jl | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index a355e0b45c629..282021793fb47 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -128,10 +128,11 @@ function cfg_inline_item!(ir::IRCode, idx::Int, todo::InliningTodo, state::CFGIn block = block_for_inst(ir, idx) inline_into_block!(state, block) - if !isempty(inlinee_cfg.blocks[1].preds) + if length(inlinee_cfg.blocks[1].preds) > 1 need_split_before = true + else + @assert inlinee_cfg.blocks[1].preds[1] == 0 end - last_block_idx = last(state.cfg.blocks[block].stmts) if false # TODO: ((idx+1) == last_block_idx && isa(ir[SSAValue(last_block_idx)], GotoNode)) need_split = false @@ -168,12 +169,18 @@ function cfg_inline_item!(ir::IRCode, idx::Int, todo::InliningTodo, state::CFGIn end new_block_range = (length(state.new_cfg_blocks)-length(inlinee_cfg.blocks)+1):length(state.new_cfg_blocks) - # Fixup the edges of the newely added blocks + # Fixup the edges of the newly added blocks for (old_block, new_block) in enumerate(bb_rename_range) if old_block != 1 || need_split_before p = state.new_cfg_blocks[new_block].preds let bb_rename_range = bb_rename_range map!(p, p) do old_pred_block + # the meaning of predecessor 0 depends on the block we encounter it: + # - in the first block, it represents the function entry and so needs to be re-mapped + if old_block == 1 && old_pred_block == 0 + return first(bb_rename_range) - 1 + end + # - elsewhere, it represents external control-flow from a caught exception which is un-affected by inlining return old_pred_block == 0 ? 0 : bb_rename_range[old_pred_block] end end @@ -188,10 +195,6 @@ function cfg_inline_item!(ir::IRCode, idx::Int, todo::InliningTodo, state::CFGIn end end - if need_split_before - push!(state.new_cfg_blocks[first(bb_rename_range)].preds, first(bb_rename_range)-1) - end - any_edges = false for (old_block, new_block) in enumerate(bb_rename_range) if (length(state.new_cfg_blocks[new_block].succs) == 0) @@ -437,7 +440,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector else bb_offset, post_bb_id = popfirst!(todo_bbs) # This implements the need_split_before flag above - need_split_before = !isempty(item.ir.cfg.blocks[1].preds) + need_split_before = length(item.ir.cfg.blocks[1].preds) > 1 if need_split_before finish_current_bb!(compact, 0) end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index ca658ad101f7f..534387f6a8692 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -106,6 +106,9 @@ function compute_basic_blocks(stmts::Vector{Any}) end # Compute successors/predecessors for (num, b) in enumerate(blocks) + if b.stmts.start == 1 + push!(b.preds, 0) # the entry block has a virtual predecessor + end terminator = stmts[last(b.stmts)] if isa(terminator, ReturnNode) # return never has any successors diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 9250a5d46989f..354f46877ca7f 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -752,3 +752,23 @@ end end end end + +#57153 check that the CFG has a #0 block predecessor and that we don't fail to compile code that observes that +function _worker_task57153() + while true + r = let + try + if @noinline rand(Bool) + return nothing + end + q, m + finally + missing + end + end + r[1]::Bool + end +end +let ir = Base.code_ircode(_worker_task57153, (), optimize_until="CC: COMPACT_2")[1].first + @test findfirst(x->x==0, ir.cfg.blocks[1].preds) !== nothing +end From 010a829c342600de0c5e33f2af5d071c5c173bd9 Mon Sep 17 00:00:00 2001 From: Sam Schweigel <33556084+xal-0@users.noreply.github.com> Date: Thu, 29 May 2025 08:08:40 -0700 Subject: [PATCH 26/46] Make more types jl_static_show unambiguously (#58512) Makes more types survive `jl_static_show` unambiguously: - Symbols - Symbols printed in the `:var"foo"` form use raw string escaping, fixing `:var"a\b"`, `:var"a\\"`, `:var"$a"`, etc. - Symbols that require parens use parens (`:(=)`, ...) - Signed integers: Except for `Int`, signed integers print like `Int8(1)`. - Floats: floats are printed in a naive but reversible (TODO: double check) way. `Inf(16|32|)` and `NaN(16|32|)` are printed, and `Float16`/`Float32` print the type (`Float32(1.5)`). `Float64`s are printed with a trailing `.0` if it is necessary to disambiguate from `Int`. Fixes #52677, https://github.com/JuliaLang/julia/issues/58484#issuecomment-2902468354, https://github.com/JuliaLang/julia/issues/58484#issuecomment-2898733637, and the specific case mentioned in #58484. Improves the situation for round-trip (inexhaustive list): - Non-canonical NaNs - BFloat16 - User-defined primitive types. This one is tricky, because they can have a size different from any type we have literals for. (cherry picked from commit b03ef6b680703267e48d56b1e7b2dbbe81e52be2) --- src/rtutils.c | 123 ++++++++++++++++++++++++++++++++++++++++++-------- test/show.jl | 54 +++++++++++++++++++++- 2 files changed, 157 insertions(+), 20 deletions(-) diff --git a/src/rtutils.c b/src/rtutils.c index f628f927f782d..bb3ab73c970c1 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -5,6 +5,8 @@ */ #include "platform.h" +#include +#include #include #include #include @@ -669,12 +671,12 @@ static int is_globfunction(jl_value_t *v, jl_datatype_t *dv, jl_sym_t **globname return 0; } -static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, int wrap) JL_NOTSAFEPOINT +static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, int wrap, int raw) JL_NOTSAFEPOINT { size_t n = 0; if (wrap) n += jl_printf(out, "\""); - if (!u8_isvalid(str, len)) { + if (!raw && !u8_isvalid(str, len)) { // alternate print algorithm that preserves data if it's not UTF-8 static const char hexdig[] = "0123456789abcdef"; for (size_t i = 0; i < len; i++) { @@ -691,7 +693,11 @@ static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, int special = 0; for (size_t i = 0; i < len; i++) { uint8_t c = str[i]; - if (c < 32 || c == 0x7f || c == '\\' || c == '"' || c == '$') { + if (raw && ((c == '\\' && i == len-1) || c == '"')) { + special = 1; + break; + } + else if (!raw && (c < 32 || c == 0x7f || c == '\\' || c == '"' || c == '$')) { special = 1; break; } @@ -700,6 +706,25 @@ static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, jl_uv_puts(out, str, len); n += len; } + else if (raw) { + // REF: Base.escape_raw_string + int escapes = 0; + for (size_t i = 0; i < len; i++) { + uint8_t c = str[i]; + if (c == '\\') { + escapes++; + } + else { + if (c == '"') + for (escapes++; escapes > 0; escapes--) + n += jl_printf(out, "\\"); + escapes = 0; + } + n += jl_printf(out, "%c", str[i]); + } + for (; escapes > 0; escapes--) + n += jl_printf(out, "\\"); + } else { char buf[512]; size_t i = 0; @@ -715,18 +740,28 @@ static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, return n; } +static int jl_is_quoted_sym(const char *sn) +{ + static const char *const quoted_syms[] = {":", "::", ":=", "=", "==", "===", "=>", "`"}; + for (int i = 0; i < sizeof quoted_syms / sizeof *quoted_syms; i++) + if (!strcmp(sn, quoted_syms[i])) + return 1; + return 0; +} + +// TODO: in theory, we need a separate function for showing symbols in an +// expression context (where `Symbol("foo\x01bar")` is ok) and a syntactic +// context (where var"" must be used). static size_t jl_static_show_symbol(JL_STREAM *out, jl_sym_t *name) JL_NOTSAFEPOINT { size_t n = 0; const char *sn = jl_symbol_name(name); - int quoted = !jl_is_identifier(sn) && !jl_is_operator(sn); - if (quoted) { - n += jl_printf(out, "var"); - // TODO: this is not quite right, since repr uses String escaping rules, and Symbol uses raw string rules - n += jl_static_show_string(out, sn, strlen(sn), 1); + if (jl_is_identifier(sn) || (jl_is_operator(sn) && !jl_is_quoted_sym(sn))) { + n += jl_printf(out, "%s", sn); } else { - n += jl_printf(out, "%s", sn); + n += jl_printf(out, "var"); + n += jl_static_show_string(out, sn, strlen(sn), 1, 1); } return n; } @@ -755,6 +790,51 @@ static int jl_static_is_function_(jl_datatype_t *vt) JL_NOTSAFEPOINT { return 0; } +static size_t jl_static_show_float(JL_STREAM *out, double v, + jl_datatype_t *vt) JL_NOTSAFEPOINT +{ + size_t n = 0; + // TODO: non-canonical NaNs do not round-trip + // TOOD: BFloat16 + const char *size_suffix = vt == jl_float16_type ? "16" : + vt == jl_float32_type ? "32" : + ""; + // Requires minimum 1 (sign) + 17 (sig) + 1 (dot) + 5 ("e-123") + 1 (null) + char buf[32]; + // Base B significand digits required to print n base-b significand bits + // (including leading 1): N = 2 + floor(n/log(b, B)) + // Float16 5 + // Float32 9 + // Float64 17 + // REF: https://dl.acm.org/doi/pdf/10.1145/93542.93559 + if (isnan(v)) { + n += jl_printf(out, "NaN%s", size_suffix); + } + else if (isinf(v)) { + n += jl_printf(out, "%sInf%s", v < 0 ? "-" : "", size_suffix); + } + else if (vt == jl_float64_type) { + n += jl_printf(out, "%#.17g", v); + } + else if (vt == jl_float32_type) { + size_t m = snprintf(buf, sizeof buf, "%.9g", v); + // If the exponent was printed, replace it with 'f' + char *p = (char *)memchr(buf, 'e', m); + if (p) + *p = 'f'; + jl_uv_puts(out, buf, m); + n += m; + // If no exponent was printed, we must add one + if (!p) + n += jl_printf(out, "f0"); + } + else { + assert(vt == jl_float16_type); + n += jl_printf(out, "Float16(%#.5g)", v); + } + return n; +} + // `v` might be pointing to a field inlined in a structure therefore // `jl_typeof(v)` may not be the same with `vt` and only `vt` should be // used to determine the type of the value. @@ -920,17 +1000,21 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt int f = *(uint32_t*)jl_data_ptr(v); n += jl_printf(out, "#", f, jl_intrinsic_name(f)); } + else if (vt == jl_long_type) { + // Avoid unnecessary Int64(x)/Int32(x) + n += jl_printf(out, "%" PRIdPTR, *(intptr_t*)v); + } else if (vt == jl_int64_type) { - n += jl_printf(out, "%" PRId64, *(int64_t*)v); + n += jl_printf(out, "Int64(%" PRId64 ")", *(int64_t*)v); } else if (vt == jl_int32_type) { - n += jl_printf(out, "%" PRId32, *(int32_t*)v); + n += jl_printf(out, "Int32(%" PRId32 ")", *(int32_t*)v); } else if (vt == jl_int16_type) { - n += jl_printf(out, "%" PRId16, *(int16_t*)v); + n += jl_printf(out, "Int16(%" PRId16 ")", *(int16_t*)v); } else if (vt == jl_int8_type) { - n += jl_printf(out, "%" PRId8, *(int8_t*)v); + n += jl_printf(out, "Int8(%" PRId8 ")", *(int8_t*)v); } else if (vt == jl_uint64_type) { n += jl_printf(out, "0x%016" PRIx64, *(uint64_t*)v); @@ -951,11 +1035,14 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "0x%08" PRIx32, *(uint32_t*)v); #endif } + else if (vt == jl_float16_type) { + n += jl_static_show_float(out, julia_half_to_float(*(uint16_t *)v), vt); + } else if (vt == jl_float32_type) { - n += jl_printf(out, "%gf", *(float*)v); + n += jl_static_show_float(out, *(float *)v, vt); } else if (vt == jl_float64_type) { - n += jl_printf(out, "%g", *(double*)v); + n += jl_static_show_float(out, *(double *)v, vt); } else if (vt == jl_bool_type) { n += jl_printf(out, "%s", *(uint8_t*)v ? "true" : "false"); @@ -964,7 +1051,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "nothing"); } else if (vt == jl_string_type) { - n += jl_static_show_string(out, jl_string_data(v), jl_string_len(v), 1); + n += jl_static_show_string(out, jl_string_data(v), jl_string_len(v), 1, 0); } else if (v == jl_bottom_type) { n += jl_printf(out, "Union{}"); @@ -1504,10 +1591,10 @@ void jl_log(int level, jl_value_t *module, jl_value_t *group, jl_value_t *id, } jl_printf(str, "\n@ "); if (jl_is_string(file)) { - jl_static_show_string(str, jl_string_data(file), jl_string_len(file), 0); + jl_static_show_string(str, jl_string_data(file), jl_string_len(file), 0, 0); } else if (jl_is_symbol(file)) { - jl_static_show_string(str, jl_symbol_name((jl_sym_t*)file), strlen(jl_symbol_name((jl_sym_t*)file)), 0); + jl_static_show_string(str, jl_symbol_name((jl_sym_t*)file), strlen(jl_symbol_name((jl_sym_t*)file)), 0, 0); } jl_printf(str, ":"); jl_static_show(str, line); diff --git a/test/show.jl b/test/show.jl index 66286f4df12ba..779ba512969ed 100644 --- a/test/show.jl +++ b/test/show.jl @@ -703,7 +703,7 @@ let oldout = stdout, olderr = stderr redirect_stderr(olderr) close(wrout) close(wrerr) - @test fetch(out) == "Int64 <: Signed\nTESTA\nTESTB\nΑ1Β2\"A\"\nA\n123\"C\"\n" + @test fetch(out) == "Int64 <: Signed\nTESTA\nTESTB\nΑ1Β2\"A\"\nA\n123.0000000000000000\"C\"\n" @test fetch(err) == "TESTA\nTESTB\nΑ1Β2\"A\"\n" finally redirect_stdout(oldout) @@ -1505,8 +1505,58 @@ struct var"%X%" end # Invalid name without '#' typeof(+), var"#f#", typeof(var"#f#"), + + # Integers should round-trip (#52677) + 1, UInt(1), + Int8(1), Int16(1), Int32(1), Int64(1), + UInt8(1), UInt16(1), UInt32(1), UInt64(1), + + # Float round-trip + Float16(1), Float32(1), Float64(1), + Float16(1.5), Float32(1.5), Float64(1.5), + Float16(0.4893243538921085), Float32(0.4893243538921085), Float64(0.4893243538921085), + # Examples that require the full 5, 9, and 17 digits of precision + Float16(0.00010014), Float32(1.00000075f-36), Float64(-1.561051336605761e-182), + floatmax(Float16), floatmax(Float32), floatmax(Float64), + floatmin(Float16), floatmin(Float32), floatmin(Float64), + Float16(0.0), 0.0f0, 0.0, + Float16(-0.0), -0.0f0, -0.0, + Inf16, Inf32, Inf, + -Inf16, -Inf32, -Inf, + nextfloat(Float16(0)), nextfloat(Float32(0)), nextfloat(Float64(0)), + NaN16, NaN32, NaN, + Float16(1e3), 1f7, 1e16, + Float16(-1e3), -1f7, -1e16, + Float16(1e4), 1f8, 1e17, + Float16(-1e4), -1f8, -1e17, + + # :var"" escaping rules differ from strings (#58484) + :foo, + :var"bar baz", + :var"a $b", # No escaping for $ in raw string + :var"a\b", # No escaping for backslashes in middle + :var"a\\", # Backslashes must be escaped at the end + :var"a\\\\", + :var"a\"b", + :var"a\"", + :var"\\\"", + :+, :var"+-", + :(=), :(:), :(::), # Requires quoting + Symbol("a\nb"), + + Val(Float16(1.0)), Val(1f0), Val(1.0), + Val(:abc), Val(:(=)), Val(:var"a\b"), + + Val(1), Val(Int8(1)), Val(Int16(1)), Val(Int32(1)), Val(Int64(1)), Val(Int128(1)), + Val(UInt(1)), Val(UInt8(1)), Val(UInt16(1)), Val(UInt32(1)), Val(UInt64(1)), Val(UInt128(1)), + + # BROKEN + # Symbol("a\xffb"), + # User-defined primitive types + # Non-canonical NaNs + # BFloat16 ) - @test v == eval(Meta.parse(static_shown(v))) + @test v === eval(Meta.parse(static_shown(v))) end end From 17457e76cca9cf8db2a1b63c7b86b7e724bb60ea Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 21 May 2025 23:46:59 -0300 Subject: [PATCH 27/46] Fix tbaa usage when storing into heap allocated immutable structs (#58483) (cherry picked from commit e3982cd84030e4baf7b8e97119bbeec0fa982935) --- src/cgutils.cpp | 4 ++-- test/compiler/codegen.jl | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index a3eb2df3c7574..405103076bd75 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -3764,7 +3764,7 @@ static jl_cgval_t emit_setfield(jl_codectx_t &ctx, emit_bitcast(ctx, addr, getInt8PtrTy(ctx.builder.getContext())), ConstantInt::get(ctx.types().T_size, fsz1)); setNameWithField(ctx.emission_context, ptindex, get_objname, sty, idx0, Twine(".tindex_ptr")); - return union_store(ctx, addr, ptindex, rhs, cmp, jfty, tbaa, ctx.tbaa().tbaa_unionselbyte, + return union_store(ctx, addr, ptindex, rhs, cmp, jfty, tbaa, strct.tbaa, Order, FailOrder, needlock, issetfield, isreplacefield, isswapfield, ismodifyfield, issetfieldonce, modifyop, fname); @@ -4009,7 +4009,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg undef_derived_strct(ctx, strct, sty, strctinfo.tbaa); for (size_t i = nargs; i < nf; i++) { if (!jl_field_isptr(sty, i) && jl_is_uniontype(jl_field_type(sty, i))) { - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_unionselbyte); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, strctinfo.tbaa); ai.decorateInst(ctx.builder.CreateAlignedStore( ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0), ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), emit_bitcast(ctx, strct, getInt8PtrTy(ctx.builder.getContext())), diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index e63ea5588cad9..ac93fb453d071 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -936,5 +936,24 @@ end struct Vec56937 x::NTuple{8, VecElement{Int}} end -x56937 = Ref(Vec56937(ntuple(_->VecElement(1),8))) -@test x56937[].x[1] == VecElement{Int}(1) # shouldn't crash +# 58470 tbaa for unionselbyte of heap allocated mutables +mutable struct Wrapper58470 + x::Union{Nothing,Int} +end + +function findsomething58470(dict, inds) + default = Wrapper58470(nothing) + for i in inds + x = get(dict, i, default).x + if !isnothing(x) + return x + end + end + return nothing +end + +let io = IOBuffer() + code_llvm(io, findsomething58470, Tuple{Dict{Int64, Wrapper58470}, Vector{Int}}, dump_module=true, raw=true, optimize=false) + str = String(take!(io)) + @test !occursin("jtbaa_unionselbyte", str) +end From f97790dce622200645870ae0a96ad3245f1e0958 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 19 May 2025 12:15:46 -0300 Subject: [PATCH 28/46] Fix layout flags for types that have oddly sized primitive type fields (#58435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is caused because for LLVMs sake we have to say that the oddly typed field is smaller than it actually is. (I wonder if we could represent it as an iN field in a struct and have it work but the result would be the same for now) Fix #58434, fix #49318, close #49362. --------- Co-authored-by: Mosè Giordano <765740+giordano@users.noreply.github.com> Co-authored-by: Sukera <11753998+Seelengrab@users.noreply.github.com> (cherry picked from commit 1b0b02869daef1d2a1b8e8105075132357ee6897) --- src/datatype.c | 5 +++++ test/core.jl | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/datatype.c b/src/datatype.c index 5fbc4cd109521..7076c003cfc40 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -696,12 +696,17 @@ void jl_compute_field_offsets(jl_datatype_t *st) // Should never happen throw_ovf(should_malloc, desc, st, fsz); desc[i].isptr = 0; + if (jl_is_uniontype(fld)) { haspadding = 1; fsz += 1; // selector byte zeroinit = 1; } else { + if (fsz > jl_datatype_size(fld)) { + // We have to pad the size to integer size class, but it means this has some padding + haspadding = 1; + } uint32_t fld_npointers = ((jl_datatype_t*)fld)->layout->npointers; if (((jl_datatype_t*)fld)->layout->flags.haspadding) haspadding = 1; diff --git a/test/core.jl b/test/core.jl index 3e934da49f694..b60ac31eae159 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8176,3 +8176,9 @@ myfun57023a(::Type{T}) where {T} = (x = @ccall mycfun()::Ptr{T}; x) @test only(code_lowered(myfun57023a)).has_fcall myfun57023b(::Type{T}) where {T} = (x = @cfunction myfun57023a Ptr{T} (Ref{T},); x) @test only(code_lowered(myfun57023b)).has_fcall + +#58434 bitsegal comparison of oddly sized fields +primitive type ByteString58434 (18 * 8) end + +@test Base.datatype_isbitsegal(Tuple{ByteString58434}) == false +@test Base.datatype_haspadding(Tuple{ByteString58434}) == (length(Base.padding(Tuple{ByteString58434})) > 0) From a1c6607cea3adf1a97f25e07188c6246535b9c85 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 14 May 2025 06:15:38 +0200 Subject: [PATCH 29/46] check that hashing of types does not foreigncall (`jl_type_hash` is concrete evaluated) (#58401) (cherry picked from commit 86a3e1a90b39e01fffb0441fb1262d25c9a69b86) --- base/hashing.jl | 3 ++- test/hashing.jl | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/base/hashing.jl b/base/hashing.jl index d4a6217de6edb..c364fd5e3a1c1 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -29,7 +29,8 @@ hash(x::Any) = hash(x, zero(UInt)) hash(w::WeakRef, h::UInt) = hash(w.value, h) # Types can't be deleted, so marking as total allows the compiler to look up the hash -hash(T::Type, h::UInt) = hash_uint(3h - @assume_effects :total ccall(:jl_type_hash, UInt, (Any,), T)) +@noinline _jl_type_hash(T::Type) = @assume_effects :total ccall(:jl_type_hash, UInt, (Any,), T) +hash(T::Type, h::UInt) = hash_uint(3h - _jl_type_hash(T)) ## hashing general objects ## diff --git a/test/hashing.jl b/test/hashing.jl index 173a31d10a6a9..3eec25f4ce5b4 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -303,4 +303,10 @@ struct AUnionParam{T<:Union{Nothing,Float32,Float64}} end @test hash(5//3) == hash(big(5)//3) end -@test Core.Compiler.is_foldable_nothrow(Base.infer_effects(hash, Tuple{Type{Int}, UInt})) +@testset "concrete eval type hash" begin + @test Core.Compiler.is_foldable_nothrow(Base.infer_effects(hash, Tuple{Type{Int}, UInt})) + + f(h...) = hash(Char, h...); + src = only(code_typed(f, Tuple{UInt}))[1] + @test count(stmt -> Meta.isexpr(stmt, :foreigncall), src.code) == 0 +end From da8a55ae0b69b602293f82c140b2ebec8e7f4853 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 29 Apr 2025 11:34:49 -0400 Subject: [PATCH 30/46] clarify that time_ns is monotonic (#57129) (cherry picked from commit b9a0497e96286fa3e43740294e2c5f6b17b70ae2) --- base/Base.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/base/Base.jl b/base/Base.jl index ab8493cd6fd32..5e5ffd25b04e0 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -151,7 +151,12 @@ end """ time_ns() -> UInt64 -Get the time in nanoseconds. The time corresponding to 0 is undefined, and wraps every 5.8 years. +Get the time in nanoseconds relative to some machine-specific arbitrary time in the past. +The primary use is for measuring elapsed times during program execution. The return value is guaranteed to +be monotonic (mod 2⁶⁴) while the system is running, and is unaffected by clock drift or changes to local calendar time, +but it may change arbitrarily across system reboots or suspensions. + +(Although the returned time is always in nanoseconds, the timing resolution is platform-dependent.) """ time_ns() = ccall(:jl_hrtime, UInt64, ()) From 81650296dc735f6abb423370d95c7d6fa5d41228 Mon Sep 17 00:00:00 2001 From: gitboy16 <82724369+gitboy16@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:38:34 +0100 Subject: [PATCH 31/46] Update command line options (#54066) Update command line option formatting. (cherry picked from commit 8c28ac6880e8b6ccc7fe393a7630cdc2692efac7) --- src/jloptions.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/jloptions.c b/src/jloptions.c index 634099c66864b..0cb4981854d99 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -112,24 +112,27 @@ static const char opts[] = " --help-hidden Print uncommon options not shown by `-h`\n\n" // startup options - " --project[={|@.}] Set as the active project/environment.\n" - " The default @. option will search through parent directories\n" - " until a Project.toml or JuliaProject.toml file is found.\n" - " -J, --sysimage Start up with the given system image file\n" - " -H, --home Set location of `julia` executable\n" - " --startup-file={yes*|no} Load `JULIA_DEPOT_PATH/config/startup.jl`; if `JULIA_DEPOT_PATH`\n" - " environment variable is unset, load `~/.julia/config/startup.jl`\n" - " --handle-signals={yes*|no} Enable or disable Julia's default signal handlers\n" - " --sysimage-native-code={yes*|no}\n" - " Use native code from system image if available\n" - " --compiled-modules={yes*|no|existing|strict}\n" - " Enable or disable incremental precompilation of modules\n" - " The `existing` option allows use of existing compiled modules that were\n" - " previously precompiled, but disallows creation of new precompile files.\n" - " The `strict` option is similar, but will error if no precompile file is found.\n" - " --pkgimages={yes*|no|existing}\n" - " Enable or disable usage of native code caching in the form of pkgimages\n" - " The `existing` option allows use of existing pkgimages but disallows creation of new ones ($)\n\n" + " --project[={|@.}] Set as the active project/environment.\n" + " The default @. option will search through parent\n" + " directories until a Project.toml or JuliaProject.toml\n" + " file is found.\n" + " -J, --sysimage Start up with the given system image file\n" + " -H, --home Set location of `julia` executable\n" + " --startup-file={yes*|no} Load `JULIA_DEPOT_PATH/config/startup.jl`; \n" + " if `JULIA_DEPOT_PATH` environment variable is unset,\n" + " load `~/.julia/config/startup.jl`\n" + " --handle-signals={yes*|no} Enable or disable Julia's default signal handlers\n" + " --sysimage-native-code={yes*|no} Use native code from system image if available\n" + " --compiled-modules={yes*|no|existing|strict} Enable or disable incremental precompilation of\n" + " modules. The `existing` option allows use of existing\n" + " compiled modules that were previously precompiled,\n" + " but disallows creation of new precompile files.\n" + " The `strict` option is similar, but will error if no\n" + " precompile file is found.\n" + " --pkgimages={yes*|no|existing} Enable or disable usage of native code caching in the\n" + " form of pkgimages. The `existing` option allows use\n" + " of existing pkgimages but disallows creation of new\n" + " ones ($)\n\n" // actions " -e, --eval Evaluate \n" @@ -894,6 +897,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) "This is a bug, please report it.", c); } } + jl_options.code_coverage = codecov; jl_options.malloc_log = malloclog; int proc_args = *argcp < optind ? *argcp : optind; From 973d116fc6a24d60fb76fe6d3fde5c3307652806 Mon Sep 17 00:00:00 2001 From: Alex Wadell Date: Mon, 14 Apr 2025 22:12:26 -0400 Subject: [PATCH 32/46] Fix `--project=@script` when outside script directory (#56351) (cherry picked from commit d9764d68aed9d7bfe0715afa5ba0229c905fba98) --- base/initdefs.jl | 24 +++++++------------ base/options.jl | 1 + src/jloptions.c | 9 ++++--- src/jloptions.h | 1 + test/cmdlineargs.jl | 10 ++++++++ test/project/ScriptProject/Project.toml | 2 ++ .../ScriptProject/SubProject/Project.toml | 2 ++ test/project/ScriptProject/bin/script.jl | 1 + 8 files changed, 31 insertions(+), 19 deletions(-) create mode 100644 test/project/ScriptProject/Project.toml create mode 100644 test/project/ScriptProject/SubProject/Project.toml create mode 100644 test/project/ScriptProject/bin/script.jl diff --git a/base/initdefs.jl b/base/initdefs.jl index 950c09663a1bf..ecff2bb09b035 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -276,22 +276,14 @@ function load_path_expand(env::AbstractString)::Union{String, Nothing} env == "@." && return current_project() env == "@stdlib" && return Sys.STDLIB if startswith(env, "@script") - if @isdefined(PROGRAM_FILE) - dir = dirname(PROGRAM_FILE) - else - cmds = unsafe_load_commands(opts.commands) - if any((cmd, arg)->cmd_suppresses_program(cmd), cmds) - # Usage error. The user did not pass a script. - return nothing - end - dir = dirname(ARGS[1]) - end - if env == "@script" # complete match, not startswith, so search upwards - return current_project(dir) - else - # starts with, so assume relative path is after - return abspath(replace(env, "@script" => dir)) - end + program_file = JLOptions().program_file + program_file = program_file != C_NULL ? unsafe_string(program_file) : nothing + isnothing(program_file) && return nothing # User did not pass a script + + # Expand trailing relative path + dir = dirname(program_file) + dir = env != "@script" ? (dir * env[length("@script")+1:end]) : dir + return current_project(dir) end env = replace(env, '#' => VERSION.major, count=1) env = replace(env, '#' => VERSION.minor, count=1) diff --git a/base/options.jl b/base/options.jl index a94936391fa8d..18fa2ad92654d 100644 --- a/base/options.jl +++ b/base/options.jl @@ -17,6 +17,7 @@ struct JLOptions nprocs::Int32 machine_file::Ptr{UInt8} project::Ptr{UInt8} + program_file::Ptr{UInt8} isinteractive::Int8 color::Int8 historyfile::Int8 diff --git a/src/jloptions.c b/src/jloptions.c index 0cb4981854d99..ad13151703531 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -56,6 +56,7 @@ JL_DLLEXPORT void jl_init_options(void) 0, // nprocs NULL, // machine_file NULL, // project + NULL, // program_file 0, // isinteractive 0, // color JL_OPTIONS_HISTORYFILE_ON, // history file @@ -112,10 +113,12 @@ static const char opts[] = " --help-hidden Print uncommon options not shown by `-h`\n\n" // startup options - " --project[={|@.}] Set as the active project/environment.\n" + " --project[={|@.|@script[]}] Set as the active project/environment.\n" + " Or, create a temporary environment with `@temp`\n" " The default @. option will search through parent\n" " directories until a Project.toml or JuliaProject.toml\n" - " file is found.\n" + " file is found. @script is similar, but searches up from\n" + " the programfile or a path relative to programfile.\n" " -J, --sysimage Start up with the given system image file\n" " -H, --home Set location of `julia` executable\n" " --startup-file={yes*|no} Load `JULIA_DEPOT_PATH/config/startup.jl`; \n" @@ -897,7 +900,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) "This is a bug, please report it.", c); } } - + jl_options.program_file = optind < argc ? strdup(argv[optind]) : ""; jl_options.code_coverage = codecov; jl_options.malloc_log = malloclog; int proc_args = *argcp < optind ? *argcp : optind; diff --git a/src/jloptions.h b/src/jloptions.h index 8649c405112d7..f72e72b462066 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -21,6 +21,7 @@ typedef struct { int32_t nprocs; const char *machine_file; const char *project; + const char *program_file; int8_t isinteractive; int8_t color; int8_t historyfile; diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 47537b1cc9a5d..43cdf5e2696a6 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -257,6 +257,16 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test expanded == readchomp(addenv(`$exename -e 'println(Base.active_project())'`, "JULIA_PROJECT" => "@foo", "HOME" => homedir())) end + # --project=@script handling + let expanded = abspath(joinpath(@__DIR__, "project", "ScriptProject")) + script = joinpath(expanded, "bin", "script.jl") + # Check running julia with --project=@script both within and outside the script directory + @testset "--@script from $name" for (name, dir) in [("project", expanded), ("outside", pwd())] + @test joinpath(expanded, "Project.toml") == readchomp(Cmd(`$exename --project=@script $script`; dir)) + @test joinpath(expanded, "SubProject", "Project.toml") == readchomp(Cmd(`$exename --project=@script/../SubProject $script`; dir)) + end + end + # --quiet, --banner let p = "print((Base.JLOptions().quiet, Base.JLOptions().banner))" @test read(`$exename -e $p`, String) == "(0, -1)" diff --git a/test/project/ScriptProject/Project.toml b/test/project/ScriptProject/Project.toml new file mode 100644 index 0000000000000..3301f2b79da83 --- /dev/null +++ b/test/project/ScriptProject/Project.toml @@ -0,0 +1,2 @@ +name = "ScriptProject" +uuid = "6646321a-c4de-46ad-9761-435e5bb1f223" diff --git a/test/project/ScriptProject/SubProject/Project.toml b/test/project/ScriptProject/SubProject/Project.toml new file mode 100644 index 0000000000000..e6c472c7a33f6 --- /dev/null +++ b/test/project/ScriptProject/SubProject/Project.toml @@ -0,0 +1,2 @@ +name = "SubProject" +uuid = "50d58d6a-5ae2-46f7-9677-83c51ca667d5" diff --git a/test/project/ScriptProject/bin/script.jl b/test/project/ScriptProject/bin/script.jl new file mode 100644 index 0000000000000..e38351c9ab9a8 --- /dev/null +++ b/test/project/ScriptProject/bin/script.jl @@ -0,0 +1 @@ +println(Base.active_project()) From 5d0d0e9206fcbf7880508c52ad828ffb12cf0149 Mon Sep 17 00:00:00 2001 From: Arno Strouwen Date: Wed, 18 Dec 2024 15:39:08 +0100 Subject: [PATCH 33/46] docs: fix scope type of a `struct` to hard (#56755) Is struct not a hard scope? ```jl julia> b = 1 struct Tester tester Tester(tester) = new(tester) b = 2 Tester() = new(b) end b 1 ``` (cherry picked from commit 522624c35146f4a1ac05fb5d9234324dc0ff15a2) --- doc/src/manual/variables-and-scoping.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index e921ed8931e84..ead0806faa6ca 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -23,7 +23,7 @@ The constructs introducing scope blocks are: | Construct | Scope type | Allowed within | |:----------|:-----------|:---------------| | [`module`](@ref), [`baremodule`](@ref) | global | global | -| [`struct`](@ref) | local (soft) | global | +| [`struct`](@ref) | local (hard) | global | | [`for`](@ref), [`while`](@ref), [`try`](@ref try) | local (soft) | global, local | | [`macro`](@ref) | local (hard) | global | | functions, [`do`](@ref) blocks, [`let`](@ref) blocks, comprehensions, generators | local (hard) | global, local | @@ -128,10 +128,10 @@ that location: 1. **Existing local:** If `x` is *already a local variable*, then the existing local `x` is assigned; 2. **Hard scope:** If `x` is *not already a local variable* and assignment occurs inside of any - hard scope construct (i.e. within a `let` block, function or macro body, comprehension, or + hard scope construct (i.e. within a `let` block, function, struct or macro body, comprehension, or generator), a new local named `x` is created in the scope of the assignment; 3. **Soft scope:** If `x` is *not already a local variable* and all of the scope constructs - containing the assignment are soft scopes (loops, `try`/`catch` blocks, or `struct` blocks), the + containing the assignment are soft scopes (loops, `try`/`catch` blocks), the behavior depends on whether the global variable `x` is defined: * if global `x` is *undefined*, a new local named `x` is created in the scope of the assignment; From 99c3affbaa0f133c2825cd04472f2f640931a7eb Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 10 Apr 2025 11:48:16 -0400 Subject: [PATCH 34/46] Remove try-finally scope from `@time_imports` `@trace_compile` `@trace_dispatch` (#58011) Matches `@time` `@profile` etc. (cherry picked from commit 1117df66f2f199e7224697d110d023add4231579) --- base/timing.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index 0e0420195b678..bdf3c3267cb7d 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -603,11 +603,12 @@ end # here so it's possible to time all imports, including InteractiveUtils and its deps macro time_imports(ex) quote - try - Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) - $(esc(ex)) - finally + Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) + @__tryfinally( + # try + $(esc(ex)), + # finally Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1) - end + ) end end From 9707342c2e3f9b23602cf5b022e6c733b143ad73 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Mon, 14 Apr 2025 13:38:35 -0400 Subject: [PATCH 35/46] remove unnecessary edge from `exp_impl` to `pow` (#58062) This was just intended to be a constant and we have binary Float64 constants (cherry picked from commit f3474b5591b6dda800a88d1819d87b0c34edbfb1) --- base/special/exp.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/special/exp.jl b/base/special/exp.jl index 32de6b9be296d..30cb7d8ae273c 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -224,7 +224,7 @@ end twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end - #k == 1024 && return (small_part * 2.0) * 2.0^1023 + #k == 1024 && return (small_part * 2.0) * 0x1p1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) @@ -252,7 +252,7 @@ end twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end - #k == 1024 && return (small_part * 2.0) * 2.0^1023 + #k == 1024 && return (small_part * 2.0) * 0x1p1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) From 647b7bfde3a5581c4eab733f9e53496d2920a2af Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Tue, 15 Apr 2025 16:00:59 -0400 Subject: [PATCH 36/46] [DOC] Update installation docs: /downloads/ => /install/ (#58127) I've created an [Install](https://julialang.org/install/) page separate from the [Downloads](https://julialang.org/downloads/) page on the website. This updates various references to point to the correct pages. (cherry picked from commit 48660a617318c0907ed0b2a260c4c46672d975ed) --- README.md | 29 +++++++++++++++++------------ doc/man/julia.1 | 2 +- doc/src/devdocs/build/windows.md | 2 +- doc/src/index.md | 2 +- doc/src/manual/faq.md | 3 +-- doc/src/manual/getting-started.md | 2 +- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index c5c4f3f2730ac..21584e96652f9 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ and installing Julia, below. ## Resources - **Homepage:** -- **Binaries:** +- **Install:** - **Source code:** - **Documentation:** - **Packages:** @@ -65,17 +65,22 @@ helpful to start contributing to the Julia codebase. ## Binary Installation -If you would rather not compile the latest Julia from source, -platform-specific tarballs with pre-compiled binaries are also -[available for download](https://julialang.org/downloads/). The -downloads page also provides details on the -[different tiers of support](https://julialang.org/downloads/#supported_platforms) -for OS and platform combinations. - -If everything works correctly, you will see a Julia banner and an -interactive prompt into which you can enter expressions for -evaluation. You can read about [getting -started](https://docs.julialang.org/en/v1/manual/getting-started/) in the manual. +The recommended way of installing Julia is to use `juliaup` which will install +the latest stable `julia` for you and help keep it up to date. It can also let +you install and run different Julia versions simultaneously. Instructions for +this can be find [here](https://julialang.org/install/). If you want to manually +download specific Julia binaries, you can find those on the [downloads +page](https://julialang.org/downloads/). The downloads page also provides +details on the [different tiers of +support](https://julialang.org/downloads/#supported_platforms) for OS and +platform combinations. + +If everything works correctly, you will get a `julia` program and when you run +it in a terminal or command prompt, you will see a Julia banner and an +interactive prompt into which you can enter expressions for evaluation. You can +read about [getting +started](https://docs.julialang.org/en/v1/manual/getting-started/) in the +manual. **Note**: Although some OS package managers provide Julia, such installations are neither maintained nor endorsed by the Julia diff --git a/doc/man/julia.1 b/doc/man/julia.1 index 45ddf45412c32..b642324fb5c2c 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -308,7 +308,7 @@ Website: https://julialang.org/ .br Documentation: https://docs.julialang.org/ .br -Downloads: https://julialang.org/downloads/ +Install: https://julialang.org/install/ .SH LICENSING Julia is an open-source project. It is made available under the MIT license. diff --git a/doc/src/devdocs/build/windows.md b/doc/src/devdocs/build/windows.md index 8f8f0c8bc676a..ff6659056c0d4 100644 --- a/doc/src/devdocs/build/windows.md +++ b/doc/src/devdocs/build/windows.md @@ -32,7 +32,7 @@ or edit `%USERPROFILE%\.gitconfig` and add/edit the lines: ## Binary distribution For the binary distribution installation notes on Windows please see the instructions at -[https://julialang.org/downloads/platform/#windows](https://julialang.org/downloads/platform/#windows). +[https://julialang.org/downloads/platform/#windows](https://julialang.org/downloads/platform/#windows). Note, however, that on all platforms [using `juliaup`](https://julialang.org/install/) is recommended over manually installing binaries. ## Source distribution diff --git a/doc/src/index.md b/doc/src/index.md index 8c88af424e8e3..8342ff448625d 100644 --- a/doc/src/index.md +++ b/doc/src/index.md @@ -37,7 +37,7 @@ Markdown.parse(""" Below is a non-exhaustive list of links that will be useful as you learn and use the Julia programming language. - [Julia Homepage](https://julialang.org) -- [Download Julia](https://julialang.org/downloads/) +- [Install Julia](https://julialang.org/install/) - [Discussion forum](https://discourse.julialang.org) - [Julia YouTube](https://www.youtube.com/user/JuliaLanguage) - [Find Julia Packages](https://julialang.org/packages/) diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index c7f2325410a37..2ecb15841bdf4 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -1090,8 +1090,7 @@ You may wish to test against the nightly version to ensure that such regressions Finally, you may also consider building Julia from source for yourself. This option is mainly for those individuals who are comfortable at the command line, or interested in learning. If this describes you, you may also be interested in reading our [guidelines for contributing](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md). -Links to each of these download types can be found on the download page at [https://julialang.org/downloads/](https://julialang.org/downloads/). -Note that not all versions of Julia are available for all platforms. +The [`juliaup` install manager](https://julialang.org/install/) has pre-defined channels named `release` and `lts` for the latest stable release and the current LTS release, as well as version-specific channels. ### How can I transfer the list of installed packages after updating my version of Julia? diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index 36d54650388cd..eb07d8f931133 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -1,7 +1,7 @@ # [Getting Started](@id man-getting-started) Julia installation is straightforward, whether using precompiled binaries or compiling from source. -Download and install Julia by following the instructions at [https://julialang.org/downloads/](https://julialang.org/downloads/). +Download and install Julia by following the instructions at [https://julialang.org/install/](https://julialang.org/install/). If you are coming to Julia from one of the following languages, then you should start by reading the section on noteworthy differences from [MATLAB](@ref Noteworthy-differences-from-MATLAB), [R](@ref Noteworthy-differences-from-R), [Python](@ref Noteworthy-differences-from-Python), [C/C++](@ref Noteworthy-differences-from-C/C) or [Common Lisp](@ref Noteworthy-differences-from-Common-Lisp). This will help you avoid some common pitfalls since Julia differs from those languages in many subtle ways. From f3cd329088e6c9b65d5ec55a58b3cb6f64b4dc1e Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 22 Apr 2025 08:54:53 +0200 Subject: [PATCH 37/46] add showing a string to REPL precompile workload (#58157) (cherry picked from commit 4766133f6c1e7825f3fb35243e353a380eb425d5) --- stdlib/REPL/src/precompile.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index f5d02b42f0cd6..64f8d2ace200a 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -43,6 +43,7 @@ print("") printstyled("a", "b") display([1]) display([1 2; 3 4]) +display("a string") foo(x) = 1 @time @eval foo(1) ; pwd From 8b02eb4cb30a5189487e7da1ed6f237a8ac4f990 Mon Sep 17 00:00:00 2001 From: GHTaarn <62629455+GHTaarn@users.noreply.github.com> Date: Thu, 8 May 2025 02:28:37 +0200 Subject: [PATCH 38/46] Base.get_extension & Dates.format made public (#58108) `Base.get_extension` and `Dates.format` both appear in the manual and should therefore be `public` symbols according to [51335](https://github.com/JuliaLang/julia/issues/51335#issuecomment-1743091402). They appear in the manual [here](https://docs.julialang.org/en/v1/base/base/#Base.get_extension) and [here](https://docs.julialang.org/en/v1/stdlib/Dates/#Dates.format-Tuple%7BTimeType,%20AbstractString%7D). Please also consider back porting this to version 1.12 (cherry picked from commit 963eaa744c211e5a5500c860f6fc745a3cf6c098) --- base/exports.jl | 1 + stdlib/Dates/src/Dates.jl | 2 ++ 2 files changed, 3 insertions(+) diff --git a/base/exports.jl b/base/exports.jl index 7762eef18313d..a2b92e0f6ca51 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1121,6 +1121,7 @@ public DL_LOAD_PATH, load_path, active_project, + get_extension, # Reflection and introspection isambiguous, diff --git a/stdlib/Dates/src/Dates.jl b/stdlib/Dates/src/Dates.jl index bbfcdbee3f3d1..01164eea50977 100644 --- a/stdlib/Dates/src/Dates.jl +++ b/stdlib/Dates/src/Dates.jl @@ -81,4 +81,6 @@ export Period, DatePeriod, TimePeriod, # io.jl ISODateTimeFormat, ISODateFormat, ISOTimeFormat, DateFormat, RFC1123Format, @dateformat_str +public format + end # module From 346ff93da8b781c0a626b613782fb7801e00ac0c Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 27 May 2025 09:00:44 -0400 Subject: [PATCH 39/46] Don't filter `Core` methods from newly-inferred list (#58510) This allows constructors like `Tuple{Type{Vector{Foo}}, UndefInitializer, Tuple{Int}}` to precompile as usual Appears to have a minimal effect on the stdlib pkgimages: ```julia --- before.txt 2025-05-23 08:36:20.171870043 -0400 +++ after.txt 2025-05-22 14:48:49.003869097 -0400 @@ -47,7 +47,7 @@ 20K ../julia/usr/share/julia/compiled/v1.13/Logging/pkgimage.so 20K ../julia/usr/share/julia/compiled/v1.13/Logging/pkgimage.so 3.5M ../julia/usr/share/julia/compiled/v1.13/Markdown/pkgimage.so -3.5M ../julia/usr/share/julia/compiled/v1.13/Markdown/pkgimage.so +3.6M ../julia/usr/share/julia/compiled/v1.13/Markdown/pkgimage.so 184K ../julia/usr/share/julia/compiled/v1.13/Mmap/pkgimage.so 184K ../julia/usr/share/julia/compiled/v1.13/Mmap/pkgimage.so 28K ../julia/usr/share/julia/compiled/v1.13/MozillaCACerts_jll/pkgimage.so ``` Resolves #58497. (cherry picked from commit f8ece052b0ad98ed27316b63218b32c0877e69da) --- base/compiler/typeinfer.jl | 2 +- test/precompile.jl | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index f3f6c38125628..6880a4c270f1b 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -435,7 +435,7 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult) code_cache(interp)[mi] = ci = CodeInstance(interp, result, valid_worlds) if track_newly_inferred[] m = mi.def - if isa(m, Method) && m.module != Core + if isa(m, Method) ccall(:jl_push_newly_inferred, Cvoid, (Any,), ci) end end diff --git a/test/precompile.jl b/test/precompile.jl index b23cb168797cc..7db9c7e37e7d7 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -2174,6 +2174,29 @@ if Base.get_bool_env("CI", false) && (Sys.ARCH === :x86_64 || Sys.ARCH === :aarc end end +precompile_test_harness("Pre-compile Core methods") do load_path + # Core methods should support pre-compilation as external CI's like anything else + # https://github.com/JuliaLang/julia/issues/58497 + write(joinpath(load_path, "CorePrecompilation.jl"), + """ + module CorePrecompilation + struct Foo end + precompile(Tuple{Type{Vector{Foo}}, UndefInitializer, Tuple{Int}}) + end + """) + ji, ofile = Base.compilecache(Base.PkgId("CorePrecompilation")) + @eval using CorePrecompilation + invokelatest() do + let tt = Tuple{Type{Vector{CorePrecompilation.Foo}}, UndefInitializer, Tuple{Int}}, + match = first(Base._methods_by_ftype(tt, -1, Base.get_world_counter())), + mi = Base.specialize_method(match) + @test isdefined(mi, :cache) + @test mi.cache.max_world === typemax(UInt) + @test mi.cache.invoke != C_NULL + end + end +end + precompile_test_harness("Issue #52063") do load_path fname = joinpath(load_path, "i_do_not_exist.jl") @test try From 600efd6ebb2f48570a4c510474e8abc764760754 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 30 May 2025 17:32:18 -0400 Subject: [PATCH 40/46] Make `Ptr` values static-show w/ type-information (#58584) Small follow-up to https://github.com/JuliaLang/julia/pull/58512/ (cherry picked from commit 36bd3ad86b2962e2ffe497e756d68e0aa432d867) --- src/rtutils.c | 7 ------- test/show.jl | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/rtutils.c b/src/rtutils.c index bb3ab73c970c1..a074af9129425 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -1028,13 +1028,6 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt else if (vt == jl_uint8_type) { n += jl_printf(out, "0x%02" PRIx8, *(uint8_t*)v); } - else if (jl_pointer_type && jl_is_cpointer_type((jl_value_t*)vt)) { -#ifdef _P64 - n += jl_printf(out, "0x%016" PRIx64, *(uint64_t*)v); -#else - n += jl_printf(out, "0x%08" PRIx32, *(uint32_t*)v); -#endif - } else if (vt == jl_float16_type) { n += jl_static_show_float(out, julia_half_to_float(*(uint16_t *)v), vt); } diff --git a/test/show.jl b/test/show.jl index 779ba512969ed..e51de7e1e3d59 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1530,6 +1530,9 @@ struct var"%X%" end # Invalid name without '#' Float16(1e4), 1f8, 1e17, Float16(-1e4), -1f8, -1e17, + # Pointers should round-trip + Ptr{Cvoid}(0), Ptr{Cvoid}(typemax(UInt)), Ptr{Any}(0), Ptr{Any}(typemax(UInt)), + # :var"" escaping rules differ from strings (#58484) :foo, :var"bar baz", From e765e469ba3ed6149627ceac7842ff1e8b13f994 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Sat, 31 May 2025 18:58:56 +0200 Subject: [PATCH 41/46] relax dispatch for the `IteratorSize` method for `Generator` (#58110) Fixes #58109 (cherry picked from commit 805f85f6e957af6d8ecdfd1ef0f5887ae4c44467) --- base/generator.jl | 2 +- test/iterators.jl | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/base/generator.jl b/base/generator.jl index 1f981de8dc788..86daefd13b378 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -98,7 +98,7 @@ IteratorSize(::Type{Any}) = SizeUnknown() IteratorSize(::Type{<:Tuple}) = HasLength() IteratorSize(::Type{<:AbstractArray{<:Any,N}}) where {N} = HasShape{N}() -IteratorSize(::Type{Generator{I,F}}) where {I,F} = IteratorSize(I) +IteratorSize(::Type{<:Generator{I}}) where {I} = (@isdefined I) ? IteratorSize(I) : SizeUnknown() haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength} diff --git a/test/iterators.jl b/test/iterators.jl index 3616a17b31d31..3a30822619802 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -978,11 +978,10 @@ end @test accumulate(+, (x^2 for x in 1:3); init=100) == [101, 105, 114] end - -@testset "Iterators.tail_if_any" begin - @test Iterators.tail_if_any(()) == () - @test Iterators.tail_if_any((1, 2)) == (2,) - @test Iterators.tail_if_any((1,)) == () +@testset "issue #58109" begin + i = Iterators.map(identity, 3) + j = Iterators.map(sqrt, 7) + @test (@inferred Base.IteratorSize(i)) === @inferred Base.IteratorSize(eltype([i, j])) end @testset "IteratorSize trait for zip" begin From 803bb01a814a49ac855ccf3a5b3d74be0391e5ba Mon Sep 17 00:00:00 2001 From: KristofferC Date: Wed, 25 Jun 2025 14:34:06 +0200 Subject: [PATCH 42/46] fixup! Fix layout flags for types that have oddly sized primitive type fields (#58435) --- test/core.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/core.jl b/test/core.jl index b60ac31eae159..4a5d84b3bfdc3 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8180,5 +8180,4 @@ myfun57023b(::Type{T}) where {T} = (x = @cfunction myfun57023a Ptr{T} (Ref{T},); #58434 bitsegal comparison of oddly sized fields primitive type ByteString58434 (18 * 8) end -@test Base.datatype_isbitsegal(Tuple{ByteString58434}) == false @test Base.datatype_haspadding(Tuple{ByteString58434}) == (length(Base.padding(Tuple{ByteString58434})) > 0) From 8e497ad593c04068633b05c8f068a1a801a4ef8f Mon Sep 17 00:00:00 2001 From: KristofferC Date: Wed, 25 Jun 2025 14:48:18 +0200 Subject: [PATCH 43/46] fixup! Don't filter `Core` methods from newly-inferred list (#58510) --- test/precompile.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/precompile.jl b/test/precompile.jl index 7db9c7e37e7d7..5abf917ccd640 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -2189,7 +2189,7 @@ precompile_test_harness("Pre-compile Core methods") do load_path invokelatest() do let tt = Tuple{Type{Vector{CorePrecompilation.Foo}}, UndefInitializer, Tuple{Int}}, match = first(Base._methods_by_ftype(tt, -1, Base.get_world_counter())), - mi = Base.specialize_method(match) + mi = Core.Compiler.specialize_method(match) @test isdefined(mi, :cache) @test mi.cache.max_world === typemax(UInt) @test mi.cache.invoke != C_NULL From 587622228b7fd1e184065e81a2c073f695ddf42e Mon Sep 17 00:00:00 2001 From: KristofferC Date: Wed, 25 Jun 2025 16:29:49 +0200 Subject: [PATCH 44/46] add julia half functions to header --- src/julia_internal.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/julia_internal.h b/src/julia_internal.h index 05a2f1e677d60..72d9dfdad5c7e 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1439,6 +1439,10 @@ JL_DLLEXPORT jl_array_t *jl_array_copy(jl_array_t *ary); JL_DLLEXPORT uintptr_t jl_object_id_(uintptr_t tv, jl_value_t *v) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_set_next_task(jl_task_t *task) JL_NOTSAFEPOINT; +JL_DLLEXPORT uint16_t julia_double_to_half(double param) JL_NOTSAFEPOINT; +JL_DLLEXPORT uint16_t julia_float_to_half(float param) JL_NOTSAFEPOINT; +JL_DLLEXPORT float julia_half_to_float(uint16_t param) JL_NOTSAFEPOINT; + // -- synchronization utilities -- // extern jl_mutex_t typecache_lock; From 32ac370b680ff25e71f0554cf29bbd6a893390ac Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 25 Jun 2025 17:10:13 -0300 Subject: [PATCH 45/46] Fix llvmpasses error --- test/llvmpasses/remove-addrspaces.ll | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index 6511a6671e449..d9b25d28ce6c7 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -14,10 +14,6 @@ ; TYPED-SAME: {}* ({}***, {}*, [1 x i64]*)* null ; OPAQUE-SAME: ptr null -; COM: check that the addrspace of the global itself is removed -; OPAQUE: @ejl_enz_runtime_exc = external global {} -@ejl_enz_runtime_exc = external addrspace(10) global {} - define i64 @getindex({} addrspace(10)* nonnull align 16 dereferenceable(40)) { ; CHECK-LABEL: @getindex top: @@ -128,16 +124,10 @@ define void @byval_type([1 x {} addrspace(10)*] addrspace(11)* byval([1 x {} add ret void } -define private fastcc void @diffejulia__mapreduce_97() { -L6: -; OPAQUE: store atomic ptr @ejl_enz_runtime_exc, ptr null unordered - store atomic {} addrspace(10)* @ejl_enz_runtime_exc, {} addrspace(10)* addrspace(10)* null unordered, align 8 - unreachable -} - define private fastcc void @diffejulia__mapreduce_97() { L6: +; CHECK-LABEL: @diffejulia__mapreduce_97 ; OPAQUE: store atomic ptr @ejl_enz_runtime_exc, ptr null unordered store atomic {} addrspace(10)* @ejl_enz_runtime_exc, {} addrspace(10)* addrspace(10)* null unordered, align 8 unreachable From ca6e7bd5b0780a5fef93173f862be21d697284b9 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 27 Jun 2025 09:44:23 +0200 Subject: [PATCH 46/46] remove allocation test that doesn't work on 1.11 --- stdlib/Unicode/test/runtests.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/Unicode/test/runtests.jl b/stdlib/Unicode/test/runtests.jl index 2af7015afa249..7fa57508cffbf 100644 --- a/stdlib/Unicode/test/runtests.jl +++ b/stdlib/Unicode/test/runtests.jl @@ -284,8 +284,6 @@ end @test_throws BoundsError graphemes("äöüx", 2:5) @test_throws BoundsError graphemes("äöüx", 5:5) @test_throws ArgumentError graphemes("äöüx", 0:1) - - @test @allocated(length(graphemes("äöüx"))) == 0 end @testset "#3721, #6939 up-to-date character widths" begin