Skip to content

Commit

Permalink
Merge branch 'master' into annotated-stacktraces
Browse files Browse the repository at this point in the history
  • Loading branch information
fingolfin authored Feb 15, 2024
2 parents 7dfbc95 + c23be9a commit aa30518
Show file tree
Hide file tree
Showing 77 changed files with 1,616 additions and 358 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Multi-threading changes
-----------------------

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

Build system changes
--------------------
Expand All @@ -96,6 +97,7 @@ New library features
* `invmod(n)` is an abbreviation for `invmod(n, typeof(n))` for native integer types ([#52180]).
* `replace(string, pattern...)` now supports an optional `IO` argument to
write the output to a stream rather than returning a string ([#48625]).
* New methods `allequal(f, itr)` and `allunique(f, itr)` taking a predicate function ([#47679]).
* `sizehint!(s, n)` now supports an optional `shrink` argument to disable shrinking ([#51929]).
* New function `Docs.hasdoc(module, symbol)` tells whether a name has a docstring ([#52139]).
* New function `Docs.undocumented_names(module)` returns a module's undocumented public names ([#52413]).
Expand All @@ -109,6 +111,8 @@ New library features
automatically.
* `@timed` now additionally returns the elapsed compilation and recompilation time ([#52889])
* `filter` can now act on a `NamedTuple` ([#50795]).
* `Iterators.cycle(iter, n)` runs over `iter` a fixed number of times, instead of forever ([#47354])
* `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]).

Standard library changes
------------------------
Expand Down
74 changes: 71 additions & 3 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1100,9 +1100,9 @@ function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle:
iterdest, itersrc = eachindex(dest), eachindex(src)
if iterdest == itersrc
# Shared-iterator implementation
for I in iterdest
@inbounds for I in iterdest
if isassigned(src, I)
@inbounds dest[I] = src[I]
dest[I] = src[I]
else
_unsetindex!(dest, I)
end
Expand Down Expand Up @@ -1217,7 +1217,8 @@ function copymutable(a::AbstractArray)
end
copymutable(itr) = collect(itr)

zero(x::AbstractArray{T}) where {T} = fill!(similar(x, typeof(zero(T))), zero(T))
zero(x::AbstractArray{T}) where {T<:Number} = fill!(similar(x, typeof(zero(T))), zero(T))
zero(x::AbstractArray) = map(zero, x)

## iteration support for arrays by iterating over `eachindex` in the array ##
# Allows fast iteration by default for both IndexLinear and IndexCartesian arrays
Expand Down Expand Up @@ -1989,24 +1990,91 @@ The keyword also accepts `Val(dims)`.
For multiple dimensions `dims = Val(::Tuple)` was added in Julia 1.8.
# Examples
Concatenate two arrays in different dimensions:
```jldoctest
julia> a = [1 2 3]
1×3 Matrix{Int64}:
1 2 3
julia> b = [4 5 6]
1×3 Matrix{Int64}:
4 5 6
julia> cat(a, b; dims=1)
2×3 Matrix{Int64}:
1 2 3
4 5 6
julia> cat(a, b; dims=2)
1×6 Matrix{Int64}:
1 2 3 4 5 6
julia> cat(a, b; dims=(1, 2))
2×6 Matrix{Int64}:
1 2 3 0 0 0
0 0 0 4 5 6
```
# Extended Help
Concatenate 3D arrays:
```jldoctest
julia> a = ones(2, 2, 3);
julia> b = ones(2, 2, 4);
julia> c = cat(a, b; dims=3);
julia> size(c) == (2, 2, 7)
true
```
Concatenate arrays of different sizes:
```jldoctest
julia> cat([1 2; 3 4], [pi, pi], fill(10, 2,3,1); dims=2) # same as hcat
2×6×1 Array{Float64, 3}:
[:, :, 1] =
1.0 2.0 3.14159 10.0 10.0 10.0
3.0 4.0 3.14159 10.0 10.0 10.0
```
Construct a block diagonal matrix:
```
julia> cat(true, trues(2,2), trues(4)', dims=(1,2)) # block-diagonal
4×7 Matrix{Bool}:
1 0 0 0 0 0 0
0 1 1 0 0 0 0
0 1 1 0 0 0 0
0 0 0 1 1 1 1
```
```
julia> cat(1, [2], [3;;]; dims=Val(2))
1×3 Matrix{Int64}:
1 2 3
```
!!! note
`cat` does not join two strings, you may want to use `*`.
```jldoctest
julia> a = "aaa";
julia> b = "bbb";
julia> cat(a, b; dims=1)
2-element Vector{String}:
"aaa"
"bbb"
julia> cat(a, b; dims=2)
1×2 Matrix{String}:
"aaa" "bbb"
julia> a * b
"aaabbb"
```
"""
@inline cat(A...; dims) = _cat(dims, A...)
# `@constprop :aggressive` allows `catdims` to be propagated as constant improving return type inference
Expand Down
1 change: 1 addition & 0 deletions base/abstractarraymath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ julia> A
"""
conj!(A::AbstractArray{<:Number}) = (@inbounds broadcast!(conj, A, A); A)
conj!(x::AbstractArray{<:Real}) = x
conj!(A::AbstractArray) = (foreach(conj!, A); A)

"""
conj(A::AbstractArray)
Expand Down
74 changes: 48 additions & 26 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -672,30 +672,34 @@ _array_for(::Type{T}, itr, isz) where {T} = _array_for(T, isz, _similar_shape(it
collect(collection)
Return an `Array` of all items in a collection or iterator. For dictionaries, returns
`Vector{Pair{KeyType, ValType}}`. If the argument is array-like or is an iterator with the
[`HasShape`](@ref IteratorSize) trait, the result will have the same shape
a `Vector` of `key=>value` [Pair](@ref Pair)s. If the argument is array-like or is an iterator
with the [`HasShape`](@ref IteratorSize) trait, the result will have the same shape
and number of dimensions as the argument.
Used by comprehensions to turn a generator into an `Array`.
Used by [comprehensions](@ref man-comprehensions) to turn a [generator expression](@ref man-generators)
into an `Array`. Thus, *on generators*, the square-brackets notation may be used instead of calling `collect`,
see second example.
# Examples
Collect items from a `UnitRange{Int64}` collection:
```jldoctest
julia> collect(1:2:13)
7-element Vector{Int64}:
1
3
5
7
9
11
13
julia> collect(1:3)
3-element Vector{Int64}:
1
2
3
```
julia> [x^2 for x in 1:8 if isodd(x)]
4-element Vector{Int64}:
1
9
25
49
Collect items from a generator (same output as `[x^2 for x in 1:3]`):
```jldoctest
julia> collect(x^2 for x in 1:3)
3-element Vector{Int64}:
1
4
9
```
"""
collect(itr) = _collect(1:1 #= Array =#, itr, IteratorEltype(itr), IteratorSize(itr))
Expand Down Expand Up @@ -3067,7 +3071,8 @@ of [`unsafe_wrap`](@ref) utilizing `Memory` or `MemoryRef` instead of raw pointe
"""
function wrap end

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

@noinline invalid_wrap_err(len, dims, proddims) = throw(DimensionMismatch(
"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."))

function wrap(::Type{Array}, m::Memory{T}, dims::NTuple{N, Integer}) where {T, N}
wrap(Array, MemoryRef(m), dims)
@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, dims::NTuple{N, Integer}) where {T, N}
dims = convert(Dims, dims)
ref = _wrap(m, dims)
$(Expr(:new, :(Array{T, N}), :ref, :dims))
end

@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, dims::NTuple{N, Integer}) where {T, N}
dims = convert(Dims, dims)
ref = _wrap(MemoryRef(m), dims)
$(Expr(:new, :(Array{T, N}), :ref, :dims))
end
@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, l::Integer) where {T}
dims = (Int(l),)
ref = _wrap(m, dims)
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
end
function wrap(::Type{Array}, m::MemoryRef{T}, l::Integer) where {T}
wrap(Array, m, (l,))
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, l::Integer) where {T}
dims = (Int(l),)
ref = _wrap(MemoryRef(m), (l,))
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
end
function wrap(::Type{Array}, m::Memory{T}, l::Integer) where {T}
wrap(Array, MemoryRef(m), (l,))
@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}) where {T}
ref = MemoryRef(m)
dims = (length(m),)
$(Expr(:new, :(Array{T, 1}), :ref, :dims))
end
62 changes: 35 additions & 27 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1221,45 +1221,53 @@ function semi_concrete_eval_call(interp::AbstractInterpreter,
return nothing
end

const_prop_result(inf_result::InferenceResult) =
ConstCallResults(inf_result.result, inf_result.exc_result, ConstPropResult(inf_result),
inf_result.ipo_effects, inf_result.linfo)

# return cached constant analysis result
return_cached_result(::AbstractInterpreter, inf_result::InferenceResult, ::AbsIntState) =
const_prop_result(inf_result)

function const_prop_call(interp::AbstractInterpreter,
mi::MethodInstance, result::MethodCallResult, arginfo::ArgInfo, sv::AbsIntState,
concrete_eval_result::Union{Nothing, ConstCallResults}=nothing)
inf_cache = get_inference_cache(interp)
𝕃ᵢ = typeinf_lattice(interp)
inf_result = cache_lookup(𝕃ᵢ, mi, arginfo.argtypes, inf_cache)
if inf_result === nothing
# fresh constant prop'
argtypes = has_conditional(𝕃ᵢ, sv) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes)
inf_result = InferenceResult(mi, argtypes, typeinf_lattice(interp))
if !any(inf_result.overridden_by_const)
add_remark!(interp, sv, "[constprop] Could not handle constant info in matching_cache_argtypes")
return nothing
end
frame = InferenceState(inf_result, #=cache_mode=#:local, interp)
if frame === nothing
add_remark!(interp, sv, "[constprop] Could not retrieve the source")
return nothing # this is probably a bad generated function (unsound), but just ignore it
end
frame.parent = sv
if !typeinf(interp, frame)
add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle")
return nothing
end
@assert inf_result.result !== nothing
if concrete_eval_result !== nothing
# override return type and effects with concrete evaluation result if available
inf_result.result = concrete_eval_result.rt
inf_result.ipo_effects = concrete_eval_result.effects
end
else
if inf_result !== nothing
# found the cache for this constant prop'
if inf_result.result === nothing
add_remark!(interp, sv, "[constprop] Found cached constant inference in a cycle")
return nothing
end
@assert inf_result.linfo === mi "MethodInstance for cached inference result does not match"
return return_cached_result(interp, inf_result, sv)
end
# perform fresh constant prop'
argtypes = has_conditional(𝕃ᵢ, sv) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes)
inf_result = InferenceResult(mi, argtypes, typeinf_lattice(interp))
if !any(inf_result.overridden_by_const)
add_remark!(interp, sv, "[constprop] Could not handle constant info in matching_cache_argtypes")
return nothing
end
frame = InferenceState(inf_result, #=cache_mode=#:local, interp)
if frame === nothing
add_remark!(interp, sv, "[constprop] Could not retrieve the source")
return nothing # this is probably a bad generated function (unsound), but just ignore it
end
frame.parent = sv
if !typeinf(interp, frame)
add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle")
return nothing
end
@assert inf_result.result !== nothing
if concrete_eval_result !== nothing
# override return type and effects with concrete evaluation result if available
inf_result.result = concrete_eval_result.rt
inf_result.ipo_effects = concrete_eval_result.effects
end
return ConstCallResults(inf_result.result, inf_result.exc_result,
ConstPropResult(inf_result), inf_result.ipo_effects, mi)
return const_prop_result(inf_result)
end

# TODO implement MustAlias forwarding
Expand Down
20 changes: 13 additions & 7 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -818,23 +818,29 @@ struct EdgeCallResult
end
end

# return cached regular inference result
function return_cached_result(::AbstractInterpreter, codeinst::CodeInstance, caller::AbsIntState)
rt = cached_return_type(codeinst)
effects = ipo_effects(codeinst)
update_valid_age!(caller, WorldRange(min_world(codeinst), max_world(codeinst)))
return EdgeCallResult(rt, codeinst.exctype, codeinst.def, effects)
end

# compute (and cache) an inferred AST and return the current best estimate of the result type
function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState)
mi = specialize_method(method, atype, sparams)::MethodInstance
code = get(code_cache(interp), mi, nothing)
codeinst = get(code_cache(interp), mi, nothing)
force_inline = is_stmt_inline(get_curr_ssaflag(caller))
if code isa CodeInstance # return existing rettype if the code is already inferred
inferred = @atomic :monotonic code.inferred
if codeinst isa CodeInstance # return existing rettype if the code is already inferred
inferred = @atomic :monotonic codeinst.inferred
if inferred === nothing && force_inline
# we already inferred this edge before and decided to discard the inferred code,
# nevertheless we re-infer it here again in order to propagate the re-inferred
# source to the inliner as a volatile result
cache_mode = CACHE_MODE_VOLATILE
else
rt = cached_return_type(code)
effects = ipo_effects(code)
update_valid_age!(caller, WorldRange(min_world(code), max_world(code)))
return EdgeCallResult(rt, code.exctype, mi, effects)
@assert codeinst.def === mi "MethodInstance for cached edge does not match"
return return_cached_result(interp, codeinst, caller)
end
else
cache_mode = CACHE_MODE_GLOBAL # cache edge targets globally by default
Expand Down
Loading

0 comments on commit aa30518

Please sign in to comment.