Skip to content

Commit aa30518

Browse files
authored
Merge branch 'master' into annotated-stacktraces
2 parents 7dfbc95 + c23be9a commit aa30518

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1616
-358
lines changed

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Multi-threading changes
7373
-----------------------
7474

7575
* `Threads.@threads` now supports the `:greedy` scheduler, intended for non-uniform workloads ([#52096]).
76+
* A new exported struct `Lockable{T, L<:AbstractLock}` makes it easy to bundle a resource and its lock together ([#52898]).
7677

7778
Build system changes
7879
--------------------
@@ -96,6 +97,7 @@ New library features
9697
* `invmod(n)` is an abbreviation for `invmod(n, typeof(n))` for native integer types ([#52180]).
9798
* `replace(string, pattern...)` now supports an optional `IO` argument to
9899
write the output to a stream rather than returning a string ([#48625]).
100+
* New methods `allequal(f, itr)` and `allunique(f, itr)` taking a predicate function ([#47679]).
99101
* `sizehint!(s, n)` now supports an optional `shrink` argument to disable shrinking ([#51929]).
100102
* New function `Docs.hasdoc(module, symbol)` tells whether a name has a docstring ([#52139]).
101103
* New function `Docs.undocumented_names(module)` returns a module's undocumented public names ([#52413]).
@@ -109,6 +111,8 @@ New library features
109111
automatically.
110112
* `@timed` now additionally returns the elapsed compilation and recompilation time ([#52889])
111113
* `filter` can now act on a `NamedTuple` ([#50795]).
114+
* `Iterators.cycle(iter, n)` runs over `iter` a fixed number of times, instead of forever ([#47354])
115+
* `zero(::AbstractArray)` now applies recursively, so `zero([[1,2],[3,4,5]])` now produces the additive identity `[[0,0],[0,0,0]]` rather than erroring ([#38064]).
112116

113117
Standard library changes
114118
------------------------

base/abstractarray.jl

+71-3
Original file line numberDiff line numberDiff line change
@@ -1100,9 +1100,9 @@ function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle:
11001100
iterdest, itersrc = eachindex(dest), eachindex(src)
11011101
if iterdest == itersrc
11021102
# Shared-iterator implementation
1103-
for I in iterdest
1103+
@inbounds for I in iterdest
11041104
if isassigned(src, I)
1105-
@inbounds dest[I] = src[I]
1105+
dest[I] = src[I]
11061106
else
11071107
_unsetindex!(dest, I)
11081108
end
@@ -1217,7 +1217,8 @@ function copymutable(a::AbstractArray)
12171217
end
12181218
copymutable(itr) = collect(itr)
12191219

1220-
zero(x::AbstractArray{T}) where {T} = fill!(similar(x, typeof(zero(T))), zero(T))
1220+
zero(x::AbstractArray{T}) where {T<:Number} = fill!(similar(x, typeof(zero(T))), zero(T))
1221+
zero(x::AbstractArray) = map(zero, x)
12211222

12221223
## iteration support for arrays by iterating over `eachindex` in the array ##
12231224
# Allows fast iteration by default for both IndexLinear and IndexCartesian arrays
@@ -1989,24 +1990,91 @@ The keyword also accepts `Val(dims)`.
19891990
For multiple dimensions `dims = Val(::Tuple)` was added in Julia 1.8.
19901991
19911992
# Examples
1993+
1994+
Concatenate two arrays in different dimensions:
1995+
```jldoctest
1996+
julia> a = [1 2 3]
1997+
1×3 Matrix{Int64}:
1998+
1 2 3
1999+
2000+
julia> b = [4 5 6]
2001+
1×3 Matrix{Int64}:
2002+
4 5 6
2003+
2004+
julia> cat(a, b; dims=1)
2005+
2×3 Matrix{Int64}:
2006+
1 2 3
2007+
4 5 6
2008+
2009+
julia> cat(a, b; dims=2)
2010+
1×6 Matrix{Int64}:
2011+
1 2 3 4 5 6
2012+
2013+
julia> cat(a, b; dims=(1, 2))
2014+
2×6 Matrix{Int64}:
2015+
1 2 3 0 0 0
2016+
0 0 0 4 5 6
2017+
```
2018+
2019+
# Extended Help
2020+
2021+
Concatenate 3D arrays:
2022+
```jldoctest
2023+
julia> a = ones(2, 2, 3);
2024+
2025+
julia> b = ones(2, 2, 4);
2026+
2027+
julia> c = cat(a, b; dims=3);
2028+
2029+
julia> size(c) == (2, 2, 7)
2030+
true
2031+
```
2032+
2033+
Concatenate arrays of different sizes:
19922034
```jldoctest
19932035
julia> cat([1 2; 3 4], [pi, pi], fill(10, 2,3,1); dims=2) # same as hcat
19942036
2×6×1 Array{Float64, 3}:
19952037
[:, :, 1] =
19962038
1.0 2.0 3.14159 10.0 10.0 10.0
19972039
3.0 4.0 3.14159 10.0 10.0 10.0
2040+
```
19982041
2042+
Construct a block diagonal matrix:
2043+
```
19992044
julia> cat(true, trues(2,2), trues(4)', dims=(1,2)) # block-diagonal
20002045
4×7 Matrix{Bool}:
20012046
1 0 0 0 0 0 0
20022047
0 1 1 0 0 0 0
20032048
0 1 1 0 0 0 0
20042049
0 0 0 1 1 1 1
2050+
```
20052051
2052+
```
20062053
julia> cat(1, [2], [3;;]; dims=Val(2))
20072054
1×3 Matrix{Int64}:
20082055
1 2 3
20092056
```
2057+
2058+
!!! note
2059+
`cat` does not join two strings, you may want to use `*`.
2060+
2061+
```jldoctest
2062+
julia> a = "aaa";
2063+
2064+
julia> b = "bbb";
2065+
2066+
julia> cat(a, b; dims=1)
2067+
2-element Vector{String}:
2068+
"aaa"
2069+
"bbb"
2070+
2071+
julia> cat(a, b; dims=2)
2072+
1×2 Matrix{String}:
2073+
"aaa" "bbb"
2074+
2075+
julia> a * b
2076+
"aaabbb"
2077+
```
20102078
"""
20112079
@inline cat(A...; dims) = _cat(dims, A...)
20122080
# `@constprop :aggressive` allows `catdims` to be propagated as constant improving return type inference

base/abstractarraymath.jl

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ julia> A
119119
"""
120120
conj!(A::AbstractArray{<:Number}) = (@inbounds broadcast!(conj, A, A); A)
121121
conj!(x::AbstractArray{<:Real}) = x
122+
conj!(A::AbstractArray) = (foreach(conj!, A); A)
122123

123124
"""
124125
conj(A::AbstractArray)

base/array.jl

+48-26
Original file line numberDiff line numberDiff line change
@@ -672,30 +672,34 @@ _array_for(::Type{T}, itr, isz) where {T} = _array_for(T, isz, _similar_shape(it
672672
collect(collection)
673673
674674
Return an `Array` of all items in a collection or iterator. For dictionaries, returns
675-
`Vector{Pair{KeyType, ValType}}`. If the argument is array-like or is an iterator with the
676-
[`HasShape`](@ref IteratorSize) trait, the result will have the same shape
675+
a `Vector` of `key=>value` [Pair](@ref Pair)s. If the argument is array-like or is an iterator
676+
with the [`HasShape`](@ref IteratorSize) trait, the result will have the same shape
677677
and number of dimensions as the argument.
678678
679-
Used by comprehensions to turn a generator into an `Array`.
679+
Used by [comprehensions](@ref man-comprehensions) to turn a [generator expression](@ref man-generators)
680+
into an `Array`. Thus, *on generators*, the square-brackets notation may be used instead of calling `collect`,
681+
see second example.
680682
681683
# Examples
684+
685+
Collect items from a `UnitRange{Int64}` collection:
686+
682687
```jldoctest
683-
julia> collect(1:2:13)
684-
7-element Vector{Int64}:
685-
1
686-
3
687-
5
688-
7
689-
9
690-
11
691-
13
688+
julia> collect(1:3)
689+
3-element Vector{Int64}:
690+
1
691+
2
692+
3
693+
```
692694
693-
julia> [x^2 for x in 1:8 if isodd(x)]
694-
4-element Vector{Int64}:
695-
1
696-
9
697-
25
698-
49
695+
Collect items from a generator (same output as `[x^2 for x in 1:3]`):
696+
697+
```jldoctest
698+
julia> collect(x^2 for x in 1:3)
699+
3-element Vector{Int64}:
700+
1
701+
4
702+
9
699703
```
700704
"""
701705
collect(itr) = _collect(1:1 #= Array =#, itr, IteratorEltype(itr), IteratorSize(itr))
@@ -3067,7 +3071,8 @@ of [`unsafe_wrap`](@ref) utilizing `Memory` or `MemoryRef` instead of raw pointe
30673071
"""
30683072
function wrap end
30693073

3070-
@eval @propagate_inbounds function wrap(::Type{Array}, ref::MemoryRef{T}, dims::NTuple{N, Integer}) where {T, N}
3074+
# validity checking for _wrap calls, separate from allocation of Array so that it can be more likely to inline into the caller
3075+
function _wrap(ref::MemoryRef{T}, dims::NTuple{N, Int}) where {T, N}
30713076
mem = ref.mem
30723077
mem_len = length(mem) + 1 - memoryrefoffset(ref)
30733078
len = Core.checked_dims(dims...)
@@ -3076,18 +3081,35 @@ function wrap end
30763081
mem = ccall(:jl_genericmemory_slice, Memory{T}, (Any, Ptr{Cvoid}, Int), mem, ref.ptr_or_offset, len)
30773082
ref = MemoryRef(mem)
30783083
end
3079-
$(Expr(:new, :(Array{T, N}), :ref, :dims))
3084+
return ref
30803085
end
30813086

30823087
@noinline invalid_wrap_err(len, dims, proddims) = throw(DimensionMismatch(
30833088
"Attempted to wrap a MemoryRef of length $len with an Array of size dims=$dims, which is invalid because prod(dims) = $proddims > $len, so that the array would have more elements than the underlying memory can store."))
30843089

3085-
function wrap(::Type{Array}, m::Memory{T}, dims::NTuple{N, Integer}) where {T, N}
3086-
wrap(Array, MemoryRef(m), dims)
3090+
@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, dims::NTuple{N, Integer}) where {T, N}
3091+
dims = convert(Dims, dims)
3092+
ref = _wrap(m, dims)
3093+
$(Expr(:new, :(Array{T, N}), :ref, :dims))
3094+
end
3095+
3096+
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, dims::NTuple{N, Integer}) where {T, N}
3097+
dims = convert(Dims, dims)
3098+
ref = _wrap(MemoryRef(m), dims)
3099+
$(Expr(:new, :(Array{T, N}), :ref, :dims))
3100+
end
3101+
@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, l::Integer) where {T}
3102+
dims = (Int(l),)
3103+
ref = _wrap(m, dims)
3104+
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
30873105
end
3088-
function wrap(::Type{Array}, m::MemoryRef{T}, l::Integer) where {T}
3089-
wrap(Array, m, (l,))
3106+
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, l::Integer) where {T}
3107+
dims = (Int(l),)
3108+
ref = _wrap(MemoryRef(m), (l,))
3109+
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
30903110
end
3091-
function wrap(::Type{Array}, m::Memory{T}, l::Integer) where {T}
3092-
wrap(Array, MemoryRef(m), (l,))
3111+
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}) where {T}
3112+
ref = MemoryRef(m)
3113+
dims = (length(m),)
3114+
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
30933115
end

base/compiler/abstractinterpretation.jl

+35-27
Original file line numberDiff line numberDiff line change
@@ -1221,45 +1221,53 @@ function semi_concrete_eval_call(interp::AbstractInterpreter,
12211221
return nothing
12221222
end
12231223

1224+
const_prop_result(inf_result::InferenceResult) =
1225+
ConstCallResults(inf_result.result, inf_result.exc_result, ConstPropResult(inf_result),
1226+
inf_result.ipo_effects, inf_result.linfo)
1227+
1228+
# return cached constant analysis result
1229+
return_cached_result(::AbstractInterpreter, inf_result::InferenceResult, ::AbsIntState) =
1230+
const_prop_result(inf_result)
1231+
12241232
function const_prop_call(interp::AbstractInterpreter,
12251233
mi::MethodInstance, result::MethodCallResult, arginfo::ArgInfo, sv::AbsIntState,
12261234
concrete_eval_result::Union{Nothing, ConstCallResults}=nothing)
12271235
inf_cache = get_inference_cache(interp)
12281236
𝕃ᵢ = typeinf_lattice(interp)
12291237
inf_result = cache_lookup(𝕃ᵢ, mi, arginfo.argtypes, inf_cache)
1230-
if inf_result === nothing
1231-
# fresh constant prop'
1232-
argtypes = has_conditional(𝕃ᵢ, sv) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes)
1233-
inf_result = InferenceResult(mi, argtypes, typeinf_lattice(interp))
1234-
if !any(inf_result.overridden_by_const)
1235-
add_remark!(interp, sv, "[constprop] Could not handle constant info in matching_cache_argtypes")
1236-
return nothing
1237-
end
1238-
frame = InferenceState(inf_result, #=cache_mode=#:local, interp)
1239-
if frame === nothing
1240-
add_remark!(interp, sv, "[constprop] Could not retrieve the source")
1241-
return nothing # this is probably a bad generated function (unsound), but just ignore it
1242-
end
1243-
frame.parent = sv
1244-
if !typeinf(interp, frame)
1245-
add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle")
1246-
return nothing
1247-
end
1248-
@assert inf_result.result !== nothing
1249-
if concrete_eval_result !== nothing
1250-
# override return type and effects with concrete evaluation result if available
1251-
inf_result.result = concrete_eval_result.rt
1252-
inf_result.ipo_effects = concrete_eval_result.effects
1253-
end
1254-
else
1238+
if inf_result !== nothing
12551239
# found the cache for this constant prop'
12561240
if inf_result.result === nothing
12571241
add_remark!(interp, sv, "[constprop] Found cached constant inference in a cycle")
12581242
return nothing
12591243
end
1244+
@assert inf_result.linfo === mi "MethodInstance for cached inference result does not match"
1245+
return return_cached_result(interp, inf_result, sv)
1246+
end
1247+
# perform fresh constant prop'
1248+
argtypes = has_conditional(𝕃ᵢ, sv) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes)
1249+
inf_result = InferenceResult(mi, argtypes, typeinf_lattice(interp))
1250+
if !any(inf_result.overridden_by_const)
1251+
add_remark!(interp, sv, "[constprop] Could not handle constant info in matching_cache_argtypes")
1252+
return nothing
1253+
end
1254+
frame = InferenceState(inf_result, #=cache_mode=#:local, interp)
1255+
if frame === nothing
1256+
add_remark!(interp, sv, "[constprop] Could not retrieve the source")
1257+
return nothing # this is probably a bad generated function (unsound), but just ignore it
1258+
end
1259+
frame.parent = sv
1260+
if !typeinf(interp, frame)
1261+
add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle")
1262+
return nothing
1263+
end
1264+
@assert inf_result.result !== nothing
1265+
if concrete_eval_result !== nothing
1266+
# override return type and effects with concrete evaluation result if available
1267+
inf_result.result = concrete_eval_result.rt
1268+
inf_result.ipo_effects = concrete_eval_result.effects
12601269
end
1261-
return ConstCallResults(inf_result.result, inf_result.exc_result,
1262-
ConstPropResult(inf_result), inf_result.ipo_effects, mi)
1270+
return const_prop_result(inf_result)
12631271
end
12641272

12651273
# TODO implement MustAlias forwarding

base/compiler/typeinfer.jl

+13-7
Original file line numberDiff line numberDiff line change
@@ -818,23 +818,29 @@ struct EdgeCallResult
818818
end
819819
end
820820

821+
# return cached regular inference result
822+
function return_cached_result(::AbstractInterpreter, codeinst::CodeInstance, caller::AbsIntState)
823+
rt = cached_return_type(codeinst)
824+
effects = ipo_effects(codeinst)
825+
update_valid_age!(caller, WorldRange(min_world(codeinst), max_world(codeinst)))
826+
return EdgeCallResult(rt, codeinst.exctype, codeinst.def, effects)
827+
end
828+
821829
# compute (and cache) an inferred AST and return the current best estimate of the result type
822830
function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState)
823831
mi = specialize_method(method, atype, sparams)::MethodInstance
824-
code = get(code_cache(interp), mi, nothing)
832+
codeinst = get(code_cache(interp), mi, nothing)
825833
force_inline = is_stmt_inline(get_curr_ssaflag(caller))
826-
if code isa CodeInstance # return existing rettype if the code is already inferred
827-
inferred = @atomic :monotonic code.inferred
834+
if codeinst isa CodeInstance # return existing rettype if the code is already inferred
835+
inferred = @atomic :monotonic codeinst.inferred
828836
if inferred === nothing && force_inline
829837
# we already inferred this edge before and decided to discard the inferred code,
830838
# nevertheless we re-infer it here again in order to propagate the re-inferred
831839
# source to the inliner as a volatile result
832840
cache_mode = CACHE_MODE_VOLATILE
833841
else
834-
rt = cached_return_type(code)
835-
effects = ipo_effects(code)
836-
update_valid_age!(caller, WorldRange(min_world(code), max_world(code)))
837-
return EdgeCallResult(rt, code.exctype, mi, effects)
842+
@assert codeinst.def === mi "MethodInstance for cached edge does not match"
843+
return return_cached_result(interp, codeinst, caller)
838844
end
839845
else
840846
cache_mode = CACHE_MODE_GLOBAL # cache edge targets globally by default

0 commit comments

Comments
 (0)