Skip to content

Commit

Permalink
Add aliasing warnings to docstrings for mutating functions in Base (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
gdalle authored Oct 27, 2023
1 parent 841d54a commit 58030da
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 4 deletions.
6 changes: 6 additions & 0 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ time_ns() = ccall(:jl_hrtime, UInt64, ())

start_base_include = time_ns()

# A warning to be interpolated in the docstring of every dangerous mutating function in Base, see PR #50824
const _DOCS_ALIASING_WARNING = """
!!! warning
Behavior can be unexpected when any mutated argument shares memory with any other argument.
"""

## Load essential files and libraries
include("essentials.jl")
include("ctypes.jl")
Expand Down
6 changes: 6 additions & 0 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,8 @@ If `dst` and `src` are of the same type, `dst == src` should hold after
the call. If `dst` and `src` are multidimensional arrays, they must have
equal [`axes`](@ref).
$(_DOCS_ALIASING_WARNING)
See also [`copyto!`](@ref).
!!! compat "Julia 1.1"
Expand Down Expand Up @@ -1391,6 +1393,8 @@ _unsafe_ind2sub(sz, i) = (@inline; _ind2sub(sz, i))
Store values from array `X` within some subset of `A` as specified by `inds`.
The syntax `A[inds...] = X` is equivalent to `(setindex!(A, X, inds...); X)`.
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> A = zeros(2,2);
Expand Down Expand Up @@ -3365,6 +3369,8 @@ end
Like [`map`](@ref), but stores the result in `destination` rather than a new
collection. `destination` must be at least as large as the smallest collection.
$(_DOCS_ALIASING_WARNING)
See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref).
# Examples
Expand Down
8 changes: 8 additions & 0 deletions base/abstractset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ const ∪ = union
Construct the [`union`](@ref) of passed in sets and overwrite `s` with the result.
Maintain order with arrays.
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> a = Set([3, 4, 5]);
Expand Down Expand Up @@ -182,6 +184,8 @@ const ∩ = intersect
Intersect all passed in sets and overwrite `s` with the result.
Maintain order with arrays.
$(_DOCS_ALIASING_WARNING)
"""
function intersect!(s::AbstractSet, itrs...)
for x in itrs
Expand Down Expand Up @@ -218,6 +222,8 @@ setdiff(s) = union(s)
Remove from set `s` (in-place) each element of each iterable from `itrs`.
Maintain order with arrays.
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> a = Set([1, 3, 4, 5]);
Expand Down Expand Up @@ -272,6 +278,8 @@ symdiff(s) = symdiff!(copy(s))
Construct the symmetric difference of the passed in sets, and overwrite `s` with the result.
When `s` is an array, the order is maintained.
Note that in this case the multiplicity of elements matters.
$(_DOCS_ALIASING_WARNING)
"""
function symdiff!(s::AbstractSet, itrs...)
for x in itrs
Expand Down
8 changes: 8 additions & 0 deletions base/accumulate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ end
cumsum!(B, A; dims::Integer)
Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref).
$(_DOCS_ALIASING_WARNING)
"""
cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} =
accumulate!(add_sum, B, A, dims=dims)
Expand Down Expand Up @@ -150,6 +152,8 @@ cumsum(itr) = accumulate(add_sum, itr)
Cumulative product of `A` along the dimension `dims`, storing the result in `B`.
See also [`cumprod`](@ref).
$(_DOCS_ALIASING_WARNING)
"""
cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} =
accumulate!(mul_prod, B, A, dims=dims)
Expand All @@ -159,6 +163,8 @@ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} =
Cumulative product of a vector `x`, storing the result in `y`.
See also [`cumprod`](@ref).
$(_DOCS_ALIASING_WARNING)
"""
cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1)

Expand Down Expand Up @@ -301,6 +307,8 @@ Cumulative operation `op` on `A` along the dimension `dims`, storing the result
Providing `dims` is optional for vectors. If the keyword argument `init` is given, its
value is used to instantiate the accumulation.
$(_DOCS_ALIASING_WARNING)
See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref).
# Examples
Expand Down
6 changes: 6 additions & 0 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ source and `do` in the destination (1-indexed).
The `unsafe` prefix on this function indicates that no validation is performed to ensure
that N is inbounds on either array. Incorrect usage may corrupt or segfault your program, in
the same manner as C.
$(_DOCS_ALIASING_WARNING)
"""
function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T
t1 = @_gc_preserve_begin dest
Expand Down Expand Up @@ -1784,6 +1786,8 @@ place of the removed items; in this case, `indices` must be a `AbstractUnitRange
To insert `replacement` before an index `n` without removing any items, use
`splice!(collection, n:n-1, replacement)`.
$(_DOCS_ALIASING_WARNING)
!!! compat "Julia 1.5"
Prior to Julia 1.5, `indices` must always be a `UnitRange`.
Expand Down Expand Up @@ -2762,6 +2766,8 @@ Remove the items at all the indices which are not given by `inds`,
and return the modified `a`.
Items which are kept are shifted to fill the resulting gaps.
$(_DOCS_ALIASING_WARNING)
`inds` must be an iterator of sorted and unique integer indices.
See also [`deleteat!`](@ref).
Expand Down
2 changes: 2 additions & 0 deletions base/asyncmap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ length(itr::AsyncGenerator) = length(itr.collector.enumerator)
Like [`asyncmap`](@ref), but stores output in `results` rather than
returning a collection.
$(_DOCS_ALIASING_WARNING)
"""
function asyncmap!(f, r, c1, c...; ntasks=0, batch_size=nothing)
foreach(identity, AsyncCollector(f, r, c1, c...; ntasks=ntasks, batch_size=batch_size))
Expand Down
4 changes: 4 additions & 0 deletions base/combinatorics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ it is even faster to write into a pre-allocated output array with `u .= @view v[
(Even though `permute!` overwrites `v` in-place, it internally requires some allocation
to keep track of which elements have been moved.)
$(_DOCS_ALIASING_WARNING)
See also [`invpermute!`](@ref).
# Examples
Expand Down Expand Up @@ -176,6 +178,8 @@ Note that if you have a pre-allocated output array (e.g. `u = similar(v)`),
it is quicker to instead employ `u[p] = v`. (`invpermute!` internally
allocates a copy of the data.)
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> A = [1, 1, 3, 4];
Expand Down
5 changes: 3 additions & 2 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1169,8 +1169,7 @@ circshift!(dest::AbstractArray, src, ::Tuple{}) = copyto!(dest, src)
Circularly shift, i.e. rotate, the data in `src`, storing the result in
`dest`. `shifts` specifies the amount to shift in each dimension.
The `dest` array must be distinct from the `src` array (they cannot
alias each other).
$(_DOCS_ALIASING_WARNING)
See also [`circshift`](@ref).
"""
Expand Down Expand Up @@ -1228,6 +1227,8 @@ their indices; any offset results in a (circular) wraparound. If the
arrays have overlapping indices, then on the domain of the overlap
`dest` agrees with `src`.
$(_DOCS_ALIASING_WARNING)
See also: [`circshift`](@ref).
# Examples
Expand Down
22 changes: 20 additions & 2 deletions base/reducedim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ _count(f, A::AbstractArrayOrBroadcasted, dims, init) = mapreduce(_bool(f), add_s
Count the number of elements in `A` for which `f` returns `true` over the
singleton dimensions of `r`, writing the result into `r` in-place.
$(_DOCS_ALIASING_WARNING)
!!! compat "Julia 1.5"
inplace `count!` was added in Julia 1.5.
Expand Down Expand Up @@ -525,8 +527,8 @@ sum(f, A::AbstractArray; dims)
sum!(r, A)
Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`.
Note that since the sum! function is intended to operate without making any allocations,
the target should not alias with the source.
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
Expand Down Expand Up @@ -601,6 +603,8 @@ prod(f, A::AbstractArray; dims)
Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`.
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> A = [1 2; 3 4]
Expand Down Expand Up @@ -678,6 +682,8 @@ maximum(f, A::AbstractArray; dims)
Compute the maximum value of `A` over the singleton dimensions of `r`, and write results to `r`.
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> A = [1 2; 3 4]
Expand Down Expand Up @@ -755,6 +761,8 @@ minimum(f, A::AbstractArray; dims)
Compute the minimum value of `A` over the singleton dimensions of `r`, and write results to `r`.
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> A = [1 2; 3 4]
Expand Down Expand Up @@ -820,6 +828,8 @@ extrema(f, A::AbstractArray; dims)
Compute the minimum and maximum value of `A` over the singleton dimensions of `r`, and write results to `r`.
$(_DOCS_ALIASING_WARNING)
!!! compat "Julia 1.8"
This method requires Julia 1.8 or later.
Expand Down Expand Up @@ -895,6 +905,8 @@ all(::Function, ::AbstractArray; dims)
Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`.
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> A = [true false; true false]
Expand Down Expand Up @@ -968,6 +980,8 @@ any(::Function, ::AbstractArray; dims)
Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write
results to `r`.
$(_DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> A = [true false; true false]
Expand Down Expand Up @@ -1085,6 +1099,8 @@ end
Find the minimum of `A` and the corresponding linear index along singleton
dimensions of `rval` and `rind`, and store the results in `rval` and `rind`.
`NaN` is treated as less than all other values except `missing`.
$(_DOCS_ALIASING_WARNING)
"""
function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray;
init::Bool=true)
Expand Down Expand Up @@ -1156,6 +1172,8 @@ end
Find the maximum of `A` and the corresponding linear index along singleton
dimensions of `rval` and `rind`, and store the results in `rval` and `rind`.
`NaN` is treated as greater than all other values except `missing`.
$(_DOCS_ALIASING_WARNING)
"""
function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray;
init::Bool=true)
Expand Down
4 changes: 4 additions & 0 deletions base/sort.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,8 @@ v[ix[k]] == partialsort(v, k)
The return value is the `k`th element of `ix` if `k` is an integer, or view into `ix` if `k` is
a range.
$(Base._DOCS_ALIASING_WARNING)
# Examples
```jldoctest
julia> v = [3, 1, 2, 1];
Expand Down Expand Up @@ -1711,6 +1713,8 @@ end
Like [`sortperm`](@ref), but accepts a preallocated index vector or array `ix` with the same `axes` as `A`.
`ix` is initialized to contain the values `LinearIndices(A)`.
$(Base._DOCS_ALIASING_WARNING)
!!! compat "Julia 1.9"
The method accepting `dims` requires at least Julia 1.9.
Expand Down
3 changes: 3 additions & 0 deletions doc/src/manual/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ As a common convention in Julia (not a syntactic requirement), such a function w
[typically be named `f!(x, y)`](@ref man-punctuation) rather than `f(x, y)`, as a visual reminder at
the call site that at least one of the arguments (often the first one) is being mutated.

!!! warning "Shared memory between arguments"
The behavior of a mutating function can be unexpected when a mutated argument shares memory with another argument, a situation known as aliasing (e.g. when one is a view of the other).
Unless the function docstring explicitly indicates that aliasing produces the expected result, it is the responsibility of the caller to ensure proper behavior on such inputs.

## Argument-type declarations

Expand Down

2 comments on commit 58030da

@vtjnash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nanosoldier runbenchmarks(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here.

Please sign in to comment.