From 32965574c6050cef1535534c657d70cae0a6003c Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:52:18 +0200 Subject: [PATCH 01/17] Add some aliasing warnings to docstrings for mutating functions --- base/abstractarray.jl | 2 ++ base/accumulate.jl | 8 ++++++++ base/reducedim.jl | 12 ++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 8571cc8ee90a5..0e6416d7b0a44 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3340,6 +3340,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. +Note that since the `map!` function is intended to operate without making any allocations, the target `destination` should not alias with any of the sources in `collection...`. + See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). # Examples diff --git a/base/accumulate.jl b/base/accumulate.jl index eeb9759e125c7..3ee45c32339be 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -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). + +Note that since the `cumsum!` function is intended to operate without making any allocations, the target `B` should not alias with the source `A`. """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) @@ -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). + +Note that since the `cumprod!` function is intended to operate without making any allocations, the target `B` should not alias with the source `A`. """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) @@ -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). + +Note that since the `cumprod!` function is intended to operate without making any allocations, the target `y` should not alias with the source `x`. """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) @@ -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. +Note that since the `accumulate!` function is intended to operate without making any allocations, the target `B` should not alias with the source `A`. + See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). # Examples diff --git a/base/reducedim.jl b/base/reducedim.jl index c1c58ccdfefed..27a2c4e54b70d 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -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. +Note that since the `count!` function is intended to operate without making any allocations, the target `r` should not alias with the source `A`. + !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. @@ -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. + +Note that since the `sum!` function is intended to operate without making any allocations, the target `r` should not alias with the source `A`. # Examples ```jldoctest @@ -601,6 +603,8 @@ prod(f, A::AbstractArray; dims) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. +Note that since the `prod!` function is intended to operate without making any allocations, the target `r` should not alias with the source `A`. + # Examples ```jldoctest julia> A = [1 2; 3 4] @@ -895,6 +899,8 @@ all(::Function, ::AbstractArray; dims) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. +Note that since the `all!` function is intended to operate without making any allocations, the target `r` should not alias with the source `A`. + # Examples ```jldoctest julia> A = [true false; true false] @@ -968,6 +974,8 @@ any(::Function, ::AbstractArray; dims) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. +Note that since the `any!` function is intended to operate without making any allocations, the target `r` should not alias with the source `A`. + # Examples ```jldoctest julia> A = [true false; true false] From 8f0a98cbf26c6d5dceb8851be80666b3e8fa23e5 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Mon, 7 Aug 2023 22:07:18 +0200 Subject: [PATCH 02/17] Replace should with must --- base/abstractarray.jl | 2 +- base/accumulate.jl | 8 ++++---- base/reducedim.jl | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 0e6416d7b0a44..8f26993a3c747 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3340,7 +3340,7 @@ 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. -Note that since the `map!` function is intended to operate without making any allocations, the target `destination` should not alias with any of the sources in `collection...`. +Note that since the `map!` function is intended to operate without making any allocations, the target `destination` must not alias with any of the sources in `collection...`. See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). diff --git a/base/accumulate.jl b/base/accumulate.jl index 3ee45c32339be..dfda2577c1cad 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -43,7 +43,7 @@ end Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). -Note that since the `cumsum!` function is intended to operate without making any allocations, the target `B` should not alias with the source `A`. +Note that since the `cumsum!` function is intended to operate without making any allocations, the target `B` must not alias with the source `A`. """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) @@ -153,7 +153,7 @@ cumsum(itr) = accumulate(add_sum, itr) Cumulative product of `A` along the dimension `dims`, storing the result in `B`. See also [`cumprod`](@ref). -Note that since the `cumprod!` function is intended to operate without making any allocations, the target `B` should not alias with the source `A`. +Note that since the `cumprod!` function is intended to operate without making any allocations, the target `B` must not alias with the source `A`. """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) @@ -164,7 +164,7 @@ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). -Note that since the `cumprod!` function is intended to operate without making any allocations, the target `y` should not alias with the source `x`. +Note that since the `cumprod!` function is intended to operate without making any allocations, the target `y` must not alias with the source `x`. """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) @@ -307,7 +307,7 @@ 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. -Note that since the `accumulate!` function is intended to operate without making any allocations, the target `B` should not alias with the source `A`. +Note that since the `accumulate!` function is intended to operate without making any allocations, the target `B` must not alias with the source `A`. See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). diff --git a/base/reducedim.jl b/base/reducedim.jl index 27a2c4e54b70d..499c8bf50e028 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -448,7 +448,7 @@ _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. -Note that since the `count!` function is intended to operate without making any allocations, the target `r` should not alias with the source `A`. +Note that since the `count!` function is intended to operate without making any allocations, the target `r` must not alias with the source `A`. !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. @@ -528,7 +528,7 @@ sum(f, A::AbstractArray; dims) 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 `r` should not alias with the source `A`. +Note that since the `sum!` function is intended to operate without making any allocations, the target `r` must not alias with the source `A`. # Examples ```jldoctest @@ -603,7 +603,7 @@ prod(f, A::AbstractArray; dims) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that since the `prod!` function is intended to operate without making any allocations, the target `r` should not alias with the source `A`. +Note that since the `prod!` function is intended to operate without making any allocations, the target `r` must not alias with the source `A`. # Examples ```jldoctest @@ -899,7 +899,7 @@ all(::Function, ::AbstractArray; dims) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that since the `all!` function is intended to operate without making any allocations, the target `r` should not alias with the source `A`. +Note that since the `all!` function is intended to operate without making any allocations, the target `r` must not alias with the source `A`. # Examples ```jldoctest @@ -974,7 +974,7 @@ any(::Function, ::AbstractArray; dims) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that since the `any!` function is intended to operate without making any allocations, the target `r` should not alias with the source `A`. +Note that since the `any!` function is intended to operate without making any allocations, the target `r` must not alias with the source `A`. # Examples ```jldoctest From bab1234593bd8794b0a1b4a0dd5876fc4d3dd2b1 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Mon, 7 Aug 2023 22:28:33 +0200 Subject: [PATCH 03/17] Replace "alias" with "share memory" --- base/abstractarray.jl | 2 +- base/accumulate.jl | 8 ++++---- base/reducedim.jl | 10 +++++----- stdlib/LinearAlgebra/src/matmul.jl | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 8f26993a3c747..b77681fde2a55 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3340,7 +3340,7 @@ 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. -Note that since the `map!` function is intended to operate without making any allocations, the target `destination` must not alias with any of the sources in `collection...`. +Note that since the `map!` function is intended to operate without making any allocations, the target `destination` must not share memory with any of the sources in `collection...`. See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). diff --git a/base/accumulate.jl b/base/accumulate.jl index dfda2577c1cad..0483b74af7180 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -43,7 +43,7 @@ end Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). -Note that since the `cumsum!` function is intended to operate without making any allocations, the target `B` must not alias with the source `A`. +Note that since the `cumsum!` function is intended to operate without making any allocations, the target `B` must not share memory with the source `A`. """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) @@ -153,7 +153,7 @@ cumsum(itr) = accumulate(add_sum, itr) Cumulative product of `A` along the dimension `dims`, storing the result in `B`. See also [`cumprod`](@ref). -Note that since the `cumprod!` function is intended to operate without making any allocations, the target `B` must not alias with the source `A`. +Note that since the `cumprod!` function is intended to operate without making any allocations, the target `B` must not share memory with the source `A`. """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) @@ -164,7 +164,7 @@ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). -Note that since the `cumprod!` function is intended to operate without making any allocations, the target `y` must not alias with the source `x`. +Note that since the `cumprod!` function is intended to operate without making any allocations, the target `y` must not share memory with the source `x`. """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) @@ -307,7 +307,7 @@ 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. -Note that since the `accumulate!` function is intended to operate without making any allocations, the target `B` must not alias with the source `A`. +Note that since the `accumulate!` function is intended to operate without making any allocations, the target `B` must not share memory with the source `A`. See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). diff --git a/base/reducedim.jl b/base/reducedim.jl index 499c8bf50e028..85fe8a741e25f 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -448,7 +448,7 @@ _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. -Note that since the `count!` function is intended to operate without making any allocations, the target `r` must not alias with the source `A`. +Note that since the `count!` function is intended to operate without making any allocations, the target `r` must not share memory with the source `A`. !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. @@ -528,7 +528,7 @@ sum(f, A::AbstractArray; dims) 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 `r` must not alias with the source `A`. +Note that since the `sum!` function is intended to operate without making any allocations, the target `r` must not share memory with the source `A`. # Examples ```jldoctest @@ -603,7 +603,7 @@ prod(f, A::AbstractArray; dims) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that since the `prod!` function is intended to operate without making any allocations, the target `r` must not alias with the source `A`. +Note that since the `prod!` function is intended to operate without making any allocations, the target `r` must not share memory with the source `A`. # Examples ```jldoctest @@ -899,7 +899,7 @@ all(::Function, ::AbstractArray; dims) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that since the `all!` function is intended to operate without making any allocations, the target `r` must not alias with the source `A`. +Note that since the `all!` function is intended to operate without making any allocations, the target `r` must not share memory with the source `A`. # Examples ```jldoctest @@ -974,7 +974,7 @@ any(::Function, ::AbstractArray; dims) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that since the `any!` function is intended to operate without making any allocations, the target `r` must not alias with the source `A`. +Note that since the `any!` function is intended to operate without making any allocations, the target `r` must not share memory with the source `A`. # Examples ```jldoctest diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 018ad20e538c8..649ac3fbce6aa 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -214,9 +214,9 @@ end """ mul!(Y, A, B) -> Y -Calculates the matrix-matrix or matrix-vector product ``AB`` and stores the result in `Y`, -overwriting the existing value of `Y`. Note that `Y` must not be aliased with either `A` or -`B`. +Calculates the matrix-matrix or matrix-vector product `AB` and stores the result in `Y`, overwriting the existing value of `Y`. + +Note that `Y` must not share memory with either `A` or `B`. # Examples ```jldoctest @@ -578,7 +578,7 @@ function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar end if C === A || B === C - throw(ArgumentError("output matrix must not be aliased with input matrix")) + throw(ArgumentError("output matrix must not share memory with input matrix")) end if mA == 0 || nA == 0 || nB == 0 || iszero(_add.alpha) @@ -618,7 +618,7 @@ function gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::Abs end if C === A || B === C - throw(ArgumentError("output matrix must not be aliased with input matrix")) + throw(ArgumentError("output matrix must not share memory with input matrix")) end if mA == 0 || nA == 0 || nB == 0 || iszero(_add.alpha) From ea896d199b68e6676d40c8f50aa40c191dbfdba7 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Tue, 8 Aug 2023 08:50:08 +0200 Subject: [PATCH 04/17] Remove redundance and add iteration order warning to map! --- base/abstractarray.jl | 2 +- base/accumulate.jl | 8 ++++---- base/reducedim.jl | 10 +++++----- stdlib/LinearAlgebra/src/matmul.jl | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index b77681fde2a55..7930ffeb39fa8 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3340,7 +3340,7 @@ 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. -Note that since the `map!` function is intended to operate without making any allocations, the target `destination` must not share memory with any of the sources in `collection...`. +Note that the order in which the `collection`s are iterated is undefined. This means you should be careful whenever the target `destination` shares memory with any of the `collection`s, because you may get an incorrect result. See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). diff --git a/base/accumulate.jl b/base/accumulate.jl index 0483b74af7180..b60fc2604ec97 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -43,7 +43,7 @@ end Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). -Note that since the `cumsum!` function is intended to operate without making any allocations, the target `B` must not share memory with the source `A`. +Note that the target `B` must not share memory with the source `A`, otherwise the behavior is undefined. """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) @@ -153,7 +153,7 @@ cumsum(itr) = accumulate(add_sum, itr) Cumulative product of `A` along the dimension `dims`, storing the result in `B`. See also [`cumprod`](@ref). -Note that since the `cumprod!` function is intended to operate without making any allocations, the target `B` must not share memory with the source `A`. +Note that the target `B` must not share memory with the source `A`, otherwise the behavior is undefined. """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) @@ -164,7 +164,7 @@ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). -Note that since the `cumprod!` function is intended to operate without making any allocations, the target `y` must not share memory with the source `x`. +Note that the target `y` must not share memory with the source `x`, otherwise the behavior is undefined. """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) @@ -307,7 +307,7 @@ 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. -Note that since the `accumulate!` function is intended to operate without making any allocations, the target `B` must not share memory with the source `A`. +Note that the target `B` must not share memory with the source `A`, otherwise the behavior is undefined. See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). diff --git a/base/reducedim.jl b/base/reducedim.jl index 85fe8a741e25f..258b38b15c393 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -448,7 +448,7 @@ _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. -Note that since the `count!` function is intended to operate without making any allocations, the target `r` must not share memory with the source `A`. +Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. @@ -528,7 +528,7 @@ sum(f, A::AbstractArray; dims) 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 `r` must not share memory with the source `A`. +Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. # Examples ```jldoctest @@ -603,7 +603,7 @@ prod(f, A::AbstractArray; dims) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that since the `prod!` function is intended to operate without making any allocations, the target `r` must not share memory with the source `A`. +Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. # Examples ```jldoctest @@ -899,7 +899,7 @@ all(::Function, ::AbstractArray; dims) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that since the `all!` function is intended to operate without making any allocations, the target `r` must not share memory with the source `A`. +Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. # Examples ```jldoctest @@ -974,7 +974,7 @@ any(::Function, ::AbstractArray; dims) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that since the `any!` function is intended to operate without making any allocations, the target `r` must not share memory with the source `A`. +Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. # Examples ```jldoctest diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 649ac3fbce6aa..58f4beaaeb371 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -216,7 +216,7 @@ end Calculates the matrix-matrix or matrix-vector product `AB` and stores the result in `Y`, overwriting the existing value of `Y`. -Note that `Y` must not share memory with either `A` or `B`. +Note that the target `Y` must not share memory with either `A` or `B`, otherwise the behavior is undefined. # Examples ```jldoctest From cc6587cb18cf53e652811e79ed2982517f82f4e6 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Wed, 9 Aug 2023 10:37:42 +0200 Subject: [PATCH 05/17] Replace behavior with result --- base/accumulate.jl | 8 ++++---- base/reducedim.jl | 10 +++++----- stdlib/LinearAlgebra/src/matmul.jl | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/base/accumulate.jl b/base/accumulate.jl index b60fc2604ec97..4a06a4c622016 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -43,7 +43,7 @@ end Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). -Note that the target `B` must not share memory with the source `A`, otherwise the behavior is undefined. +Note that the target `B` must not share memory with the source `A`, otherwise the result is undefined. """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) @@ -153,7 +153,7 @@ cumsum(itr) = accumulate(add_sum, itr) Cumulative product of `A` along the dimension `dims`, storing the result in `B`. See also [`cumprod`](@ref). -Note that the target `B` must not share memory with the source `A`, otherwise the behavior is undefined. +Note that the target `B` must not share memory with the source `A`, otherwise the result is undefined. """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) @@ -164,7 +164,7 @@ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). -Note that the target `y` must not share memory with the source `x`, otherwise the behavior is undefined. +Note that the target `y` must not share memory with the source `x`, otherwise the result is undefined. """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) @@ -307,7 +307,7 @@ 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. -Note that the target `B` must not share memory with the source `A`, otherwise the behavior is undefined. +Note that the target `B` must not share memory with the source `A`, otherwise the result is undefined. See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). diff --git a/base/reducedim.jl b/base/reducedim.jl index 258b38b15c393..372b9de69c266 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -448,7 +448,7 @@ _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. -Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. +Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. @@ -528,7 +528,7 @@ sum(f, A::AbstractArray; dims) Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. +Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. # Examples ```jldoctest @@ -603,7 +603,7 @@ prod(f, A::AbstractArray; dims) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. +Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. # Examples ```jldoctest @@ -899,7 +899,7 @@ all(::Function, ::AbstractArray; dims) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. +Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. # Examples ```jldoctest @@ -974,7 +974,7 @@ any(::Function, ::AbstractArray; dims) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the behavior is undefined. +Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. # Examples ```jldoctest diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 58f4beaaeb371..8075b148d81ed 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -216,7 +216,7 @@ end Calculates the matrix-matrix or matrix-vector product `AB` and stores the result in `Y`, overwriting the existing value of `Y`. -Note that the target `Y` must not share memory with either `A` or `B`, otherwise the behavior is undefined. +Note that the target `Y` must not share memory with either `A` or `B`, otherwise the result is undefined. # Examples ```jldoctest From 62abce1b6c4f9d2d9d6ef8e799c45f3204428358 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Wed, 9 Aug 2023 11:06:45 +0200 Subject: [PATCH 06/17] Parse the whole list of bang functions --- base/abstractarray.jl | 2 ++ base/abstractset.jl | 8 ++++++++ base/array.jl | 4 ++++ base/asyncmap.jl | 2 ++ base/combinatorics.jl | 4 ++++ base/multidimensional.jl | 5 +++-- base/reducedim.jl | 10 ++++++++++ base/sort.jl | 4 ++++ stdlib/LinearAlgebra/src/dense.jl | 2 ++ stdlib/LinearAlgebra/src/matmul.jl | 10 +++++----- 10 files changed, 44 insertions(+), 7 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 7930ffeb39fa8..b0d58acc15cc3 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1369,6 +1369,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)`. +Note that the target `A` must not share memory with the source `X` or the indices `inds`, otherwise the result is undefined. + # Examples ```jldoctest julia> A = zeros(2,2); diff --git a/base/abstractset.jl b/base/abstractset.jl index 5d0d65dad2de6..91f47187a9af2 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -65,6 +65,8 @@ const ∪ = union Construct the [`union`](@ref) of passed in sets and overwrite `s` with the result. Maintain order with arrays. +Note that the target `s` must not share memory with any of the sources `itrs`, otherwise the result is undefined. + # Examples ```jldoctest julia> a = Set([3, 4, 5]); @@ -182,6 +184,8 @@ const ∩ = intersect Intersect all passed in sets and overwrite `s` with the result. Maintain order with arrays. + +Note that the target `s` must not share memory with any of the sources `itrs`, otherwise the result is undefined. """ function intersect!(s::AbstractSet, itrs...) for x in itrs @@ -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. +Note that the target `s` must not share memory with any of the sources `itrs`, otherwise the result is undefined. + # Examples ```jldoctest julia> a = Set([1, 3, 4, 5]); @@ -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. + +Note that the target `s` must not share memory with any of the sources `itrs`, otherwise the result is undefined. """ function symdiff!(s::AbstractSet, itrs...) for x in itrs diff --git a/base/array.jl b/base/array.jl index d3d4750743a91..5950c557fefea 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1781,6 +1781,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)`. +Note that the target `a` must not share memory with the sources `indices` or `replacement`, otherwise the result is undefined. + !!! compat "Julia 1.5" Prior to Julia 1.5, `indices` must always be a `UnitRange`. @@ -2782,6 +2784,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. +Note that the target `a` must not share memory with the indices `inds`, otherwise the result is undefined. + `inds` must be an iterator of sorted and unique integer indices. See also [`deleteat!`](@ref). diff --git a/base/asyncmap.jl b/base/asyncmap.jl index be16ba1b27610..ad356f088cff8 100644 --- a/base/asyncmap.jl +++ b/base/asyncmap.jl @@ -394,6 +394,8 @@ length(itr::AsyncGenerator) = length(itr.collector.enumerator) Like [`asyncmap`](@ref), but stores output in `results` rather than returning a collection. + +Note that the target `result` must not share memory with any of the sources `c`, otherwise the result is undefined. """ function asyncmap!(f, r, c1, c...; ntasks=0, batch_size=nothing) foreach(identity, AsyncCollector(f, r, c1, c...; ntasks=ntasks, batch_size=batch_size)) diff --git a/base/combinatorics.jl b/base/combinatorics.jl index d09a5b6c0ce83..e19867a43cd4a 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -169,6 +169,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.) +Note that the target `v` must not share memory with the permutation `p`, otherwise the result is undefined. + See also [`invpermute!`](@ref). # Examples @@ -222,6 +224,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.) +Note that the target `v` must not share memory with the permutation `p`, otherwise the result is undefined. + # Examples ```jldoctest julia> A = [1, 1, 3, 4]; diff --git a/base/multidimensional.jl b/base/multidimensional.jl index f793df068ec5a..6298d483052fe 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1179,8 +1179,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). +Note that the target `dest` must not share memory with the source `src`, otherwise the result is undefined. See also [`circshift`](@ref). """ @@ -1238,6 +1237,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`. +Note that the target `dest` must not share memory with the source `src`, otherwise the result is undefined. + See also: [`circshift`](@ref). # Examples diff --git a/base/reducedim.jl b/base/reducedim.jl index 372b9de69c266..d2bd06994c8bd 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -682,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`. +Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. + # Examples ```jldoctest julia> A = [1 2; 3 4] @@ -759,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`. +Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. + # Examples ```jldoctest julia> A = [1 2; 3 4] @@ -824,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`. +Note that the target `dest` must not share memory with the source `src`, otherwise the result is undefined. + !!! compat "Julia 1.8" This method requires Julia 1.8 or later. @@ -1093,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`. + +Note that the targets `rval` and `rind` must not share memory with the source `A`, otherwise the result is undefined. """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) @@ -1164,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`. + +Note that the targets `rval` and `rind` must not share memory with the source `A`, otherwise the result is undefined. """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) diff --git a/base/sort.jl b/base/sort.jl index abf0b9ed07682..24e07697003b3 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1583,6 +1583,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. +Note that the target `ix` must not share memory with the source `v`, otherwise the result is undefined. + # Examples ```jldoctest julia> v = [3, 1, 2, 1]; @@ -1707,6 +1709,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)`. +Note that the target `ix` must not share memory with the source `A`, otherwise the result is undefined. + !!! compat "Julia 1.9" The method accepting `dims` requires at least Julia 1.9. diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index b8a44159de8bd..8404e4480d147 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -360,6 +360,8 @@ _kronsize(A::AbstractVector, B::AbstractMatrix) = (length(A)*size(B, 1), size(B, Computes the Kronecker product of `A` and `B` and stores the result in `C`, overwriting the existing content of `C`. This is the in-place version of [`kron`](@ref). +Note that the target `C` must not share memory with any of the sources `A` and `B`, otherwise the result is undefined. + !!! compat "Julia 1.6" This function requires Julia 1.6 or later. """ diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 8075b148d81ed..9ccd5ad45eeeb 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -214,9 +214,9 @@ end """ mul!(Y, A, B) -> Y -Calculates the matrix-matrix or matrix-vector product `AB` and stores the result in `Y`, overwriting the existing value of `Y`. - -Note that the target `Y` must not share memory with either `A` or `B`, otherwise the result is undefined. +Calculates the matrix-matrix or matrix-vector product ``AB`` and stores the result in `Y`, overwriting the existing value of `Y`, +overwriting the existing value of `Y`. Note that `Y` must not be aliased with either `A` or +`B`. # Examples ```jldoctest @@ -578,7 +578,7 @@ function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar end if C === A || B === C - throw(ArgumentError("output matrix must not share memory with input matrix")) + throw(ArgumentError("output matrix must not be aliased with input matrix")) end if mA == 0 || nA == 0 || nB == 0 || iszero(_add.alpha) @@ -618,7 +618,7 @@ function gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::Abs end if C === A || B === C - throw(ArgumentError("output matrix must not share memory with input matrix")) + throw(ArgumentError("output matrix must not be aliased with input matrix")) end if mA == 0 || nA == 0 || nB == 0 || iszero(_add.alpha) From fdddbfec85ee5d92524a71a9858611841c5fbd7f Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Wed, 9 Aug 2023 17:00:57 +0200 Subject: [PATCH 07/17] Update base/abstractarray.jl Co-authored-by: Cameron Bieganek <8310743+CameronBieganek@users.noreply.github.com> --- base/abstractarray.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index b0d58acc15cc3..dcd91a58481b4 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3342,7 +3342,7 @@ 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. -Note that the order in which the `collection`s are iterated is undefined. This means you should be careful whenever the target `destination` shares memory with any of the `collection`s, because you may get an incorrect result. +The order in which the `collection`s are iterated is undefined. If `destination` shares memory with any of the `collection`s, the result might be incorrect. See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). From 3d2ab603c2260c58c82b57d29e086f4931ed6b5e Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Tue, 29 Aug 2023 13:36:14 +0200 Subject: [PATCH 08/17] Add warnings to copyto! and unsafe_copyto! --- base/abstractarray.jl | 2 ++ base/array.jl | 2 ++ 2 files changed, 4 insertions(+) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index dcd91a58481b4..a1ab3968c79b7 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -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). +Note that `dst` must not share memory with `src`, otherwise the result is undefined. + See also [`copyto!`](@ref). !!! compat "Julia 1.1" diff --git a/base/array.jl b/base/array.jl index 5950c557fefea..a0b29496c7061 100644 --- a/base/array.jl +++ b/base/array.jl @@ -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. + +Note that `dest` must not share memory with `src`, otherwise the result is undefined. """ function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T t1 = @_gc_preserve_begin dest From 189620fc48c6f88a38d97117ea7b8864b584d192 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Fri, 15 Sep 2023 08:08:24 +0200 Subject: [PATCH 09/17] Clarify map! docstring --- base/abstractarray.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index a1ab3968c79b7..da1e470de69f6 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3344,7 +3344,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. -The order in which the `collection`s are iterated is undefined. If `destination` shares memory with any of the `collection`s, the result might be incorrect. +The order in which the elements of individual `collection[i]` arguments are iterated is undefined. +If `destination` shares memory with any of the `collection[i]` arguments, the result is undefined. See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). From 9887e8b6614ed183e92352eb1ca557cb17275949 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Fri, 29 Sep 2023 08:46:41 +0200 Subject: [PATCH 10/17] Change wording for warning in base, remove stdlib modifications --- base/abstractarray.jl | 7 +++---- base/abstractset.jl | 8 ++++---- base/accumulate.jl | 8 ++++---- base/array.jl | 6 +++--- base/asyncmap.jl | 2 +- base/combinatorics.jl | 4 ++-- base/multidimensional.jl | 4 ++-- base/reducedim.jl | 20 ++++++++++---------- base/sort.jl | 4 ++-- stdlib/LinearAlgebra/src/dense.jl | 2 -- stdlib/LinearAlgebra/src/matmul.jl | 2 +- 11 files changed, 32 insertions(+), 35 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index da1e470de69f6..f3b61fa0960dc 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -905,7 +905,7 @@ 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). -Note that `dst` must not share memory with `src`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. See also [`copyto!`](@ref). @@ -1371,7 +1371,7 @@ _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)`. -Note that the target `A` must not share memory with the source `X` or the indices `inds`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -3344,8 +3344,7 @@ 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. -The order in which the elements of individual `collection[i]` arguments are iterated is undefined. -If `destination` shares memory with any of the `collection[i]` arguments, the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). diff --git a/base/abstractset.jl b/base/abstractset.jl index 91f47187a9af2..05e4695e19c0a 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -65,7 +65,7 @@ const ∪ = union Construct the [`union`](@ref) of passed in sets and overwrite `s` with the result. Maintain order with arrays. -Note that the target `s` must not share memory with any of the sources `itrs`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -185,7 +185,7 @@ const ∩ = intersect Intersect all passed in sets and overwrite `s` with the result. Maintain order with arrays. -Note that the target `s` must not share memory with any of the sources `itrs`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. """ function intersect!(s::AbstractSet, itrs...) for x in itrs @@ -222,7 +222,7 @@ setdiff(s) = union(s) Remove from set `s` (in-place) each element of each iterable from `itrs`. Maintain order with arrays. -Note that the target `s` must not share memory with any of the sources `itrs`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -279,7 +279,7 @@ Construct the symmetric difference of the passed in sets, and overwrite `s` with When `s` is an array, the order is maintained. Note that in this case the multiplicity of elements matters. -Note that the target `s` must not share memory with any of the sources `itrs`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. """ function symdiff!(s::AbstractSet, itrs...) for x in itrs diff --git a/base/accumulate.jl b/base/accumulate.jl index 4a06a4c622016..c97799f0a56fd 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -43,7 +43,7 @@ end Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). -Note that the target `B` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) @@ -153,7 +153,7 @@ cumsum(itr) = accumulate(add_sum, itr) Cumulative product of `A` along the dimension `dims`, storing the result in `B`. See also [`cumprod`](@ref). -Note that the target `B` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) @@ -164,7 +164,7 @@ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). -Note that the target `y` must not share memory with the source `x`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) @@ -307,7 +307,7 @@ 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. -Note that the target `B` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). diff --git a/base/array.jl b/base/array.jl index a0b29496c7061..043f2d27095a1 100644 --- a/base/array.jl +++ b/base/array.jl @@ -323,7 +323,7 @@ The `unsafe` prefix on this function indicates that no validation is performed t that N is inbounds on either array. Incorrect usage may corrupt or segfault your program, in the same manner as C. -Note that `dest` must not share memory with `src`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. """ function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T t1 = @_gc_preserve_begin dest @@ -1783,7 +1783,7 @@ 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)`. -Note that the target `a` must not share memory with the sources `indices` or `replacement`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. !!! compat "Julia 1.5" Prior to Julia 1.5, `indices` must always be a `UnitRange`. @@ -2786,7 +2786,7 @@ 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. -Note that the target `a` must not share memory with the indices `inds`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. `inds` must be an iterator of sorted and unique integer indices. See also [`deleteat!`](@ref). diff --git a/base/asyncmap.jl b/base/asyncmap.jl index ad356f088cff8..e248ccc2d72a1 100644 --- a/base/asyncmap.jl +++ b/base/asyncmap.jl @@ -395,7 +395,7 @@ length(itr::AsyncGenerator) = length(itr.collector.enumerator) Like [`asyncmap`](@ref), but stores output in `results` rather than returning a collection. -Note that the target `result` must not share memory with any of the sources `c`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. """ function asyncmap!(f, r, c1, c...; ntasks=0, batch_size=nothing) foreach(identity, AsyncCollector(f, r, c1, c...; ntasks=ntasks, batch_size=batch_size)) diff --git a/base/combinatorics.jl b/base/combinatorics.jl index e19867a43cd4a..b0b32379bf24c 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -169,7 +169,7 @@ 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.) -Note that the target `v` must not share memory with the permutation `p`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. See also [`invpermute!`](@ref). @@ -224,7 +224,7 @@ 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.) -Note that the target `v` must not share memory with the permutation `p`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 6298d483052fe..06cb6737ccbbb 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1179,7 +1179,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. -Note that the target `dest` must not share memory with the source `src`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. See also [`circshift`](@ref). """ @@ -1237,7 +1237,7 @@ 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`. -Note that the target `dest` must not share memory with the source `src`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. See also: [`circshift`](@ref). diff --git a/base/reducedim.jl b/base/reducedim.jl index d2bd06994c8bd..ea674a06acea4 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -448,7 +448,7 @@ _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. -Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. @@ -528,7 +528,7 @@ sum(f, A::AbstractArray; dims) Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -603,7 +603,7 @@ prod(f, A::AbstractArray; dims) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -682,7 +682,7 @@ maximum(f, A::AbstractArray; dims) Compute the maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -761,7 +761,7 @@ minimum(f, A::AbstractArray; dims) Compute the minimum value of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -828,7 +828,7 @@ extrema(f, A::AbstractArray; dims) Compute the minimum and maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that the target `dest` must not share memory with the source `src`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. !!! compat "Julia 1.8" This method requires Julia 1.8 or later. @@ -905,7 +905,7 @@ all(::Function, ::AbstractArray; dims) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -980,7 +980,7 @@ any(::Function, ::AbstractArray; dims) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Note that the target `r` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -1100,7 +1100,7 @@ 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`. -Note that the targets `rval` and `rind` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) @@ -1173,7 +1173,7 @@ 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`. -Note that the targets `rval` and `rind` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) diff --git a/base/sort.jl b/base/sort.jl index 24e07697003b3..91982e1dc3267 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1583,7 +1583,7 @@ 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. -Note that the target `ix` must not share memory with the source `v`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -1709,7 +1709,7 @@ 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)`. -Note that the target `ix` must not share memory with the source `A`, otherwise the result is undefined. +Behavior is undefined when any mutated argument shares memory with any other argument. !!! compat "Julia 1.9" The method accepting `dims` requires at least Julia 1.9. diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 8404e4480d147..b8a44159de8bd 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -360,8 +360,6 @@ _kronsize(A::AbstractVector, B::AbstractMatrix) = (length(A)*size(B, 1), size(B, Computes the Kronecker product of `A` and `B` and stores the result in `C`, overwriting the existing content of `C`. This is the in-place version of [`kron`](@ref). -Note that the target `C` must not share memory with any of the sources `A` and `B`, otherwise the result is undefined. - !!! compat "Julia 1.6" This function requires Julia 1.6 or later. """ diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 9ccd5ad45eeeb..018ad20e538c8 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -214,7 +214,7 @@ end """ mul!(Y, A, B) -> Y -Calculates the matrix-matrix or matrix-vector product ``AB`` and stores the result in `Y`, overwriting the existing value of `Y`, +Calculates the matrix-matrix or matrix-vector product ``AB`` and stores the result in `Y`, overwriting the existing value of `Y`. Note that `Y` must not be aliased with either `A` or `B`. From f472600fc85a2193b4a31cdc25a17d97e992788b Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Fri, 29 Sep 2023 08:52:07 +0200 Subject: [PATCH 11/17] Add warning to the manual page on functions --- doc/src/manual/functions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index a724f450dccfa..1c79a81cea256 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -102,6 +102,8 @@ 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 undefined when a mutated argument shares memory with another argument (e.g. when one is a view of the other). ## Argument-type declarations From 8ec5f57958ca8d146fcda36aa1f65e5dc9b9e2d1 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sat, 30 Sep 2023 21:42:50 +0200 Subject: [PATCH 12/17] Replace undefined with unexpected --- base/abstractarray.jl | 6 +++--- base/abstractset.jl | 8 ++++---- base/accumulate.jl | 8 ++++---- base/array.jl | 6 +++--- base/asyncmap.jl | 2 +- base/combinatorics.jl | 4 ++-- base/multidimensional.jl | 4 ++-- base/reducedim.jl | 20 ++++++++++---------- base/sort.jl | 4 ++-- doc/src/manual/functions.md | 2 +- 10 files changed, 32 insertions(+), 32 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index f3b61fa0960dc..8fba1d4526e4d 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -905,7 +905,7 @@ 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). -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. See also [`copyto!`](@ref). @@ -1371,7 +1371,7 @@ _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)`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -3344,7 +3344,7 @@ 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. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). diff --git a/base/abstractset.jl b/base/abstractset.jl index 05e4695e19c0a..d66cb3ec2abb2 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -65,7 +65,7 @@ const ∪ = union Construct the [`union`](@ref) of passed in sets and overwrite `s` with the result. Maintain order with arrays. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -185,7 +185,7 @@ const ∩ = intersect Intersect all passed in sets and overwrite `s` with the result. Maintain order with arrays. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. """ function intersect!(s::AbstractSet, itrs...) for x in itrs @@ -222,7 +222,7 @@ setdiff(s) = union(s) Remove from set `s` (in-place) each element of each iterable from `itrs`. Maintain order with arrays. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -279,7 +279,7 @@ Construct the symmetric difference of the passed in sets, and overwrite `s` with When `s` is an array, the order is maintained. Note that in this case the multiplicity of elements matters. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. """ function symdiff!(s::AbstractSet, itrs...) for x in itrs diff --git a/base/accumulate.jl b/base/accumulate.jl index c97799f0a56fd..ee476f23b4e04 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -43,7 +43,7 @@ end Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) @@ -153,7 +153,7 @@ cumsum(itr) = accumulate(add_sum, itr) Cumulative product of `A` along the dimension `dims`, storing the result in `B`. See also [`cumprod`](@ref). -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) @@ -164,7 +164,7 @@ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) @@ -307,7 +307,7 @@ 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. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). diff --git a/base/array.jl b/base/array.jl index 043f2d27095a1..0ac8184f73cb1 100644 --- a/base/array.jl +++ b/base/array.jl @@ -323,7 +323,7 @@ The `unsafe` prefix on this function indicates that no validation is performed t that N is inbounds on either array. Incorrect usage may corrupt or segfault your program, in the same manner as C. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. """ function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T t1 = @_gc_preserve_begin dest @@ -1783,7 +1783,7 @@ 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)`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. !!! compat "Julia 1.5" Prior to Julia 1.5, `indices` must always be a `UnitRange`. @@ -2786,7 +2786,7 @@ 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. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. `inds` must be an iterator of sorted and unique integer indices. See also [`deleteat!`](@ref). diff --git a/base/asyncmap.jl b/base/asyncmap.jl index e248ccc2d72a1..4d81bc9ea2f02 100644 --- a/base/asyncmap.jl +++ b/base/asyncmap.jl @@ -395,7 +395,7 @@ length(itr::AsyncGenerator) = length(itr.collector.enumerator) Like [`asyncmap`](@ref), but stores output in `results` rather than returning a collection. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. """ function asyncmap!(f, r, c1, c...; ntasks=0, batch_size=nothing) foreach(identity, AsyncCollector(f, r, c1, c...; ntasks=ntasks, batch_size=batch_size)) diff --git a/base/combinatorics.jl b/base/combinatorics.jl index b0b32379bf24c..1fbaa58267ee3 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -169,7 +169,7 @@ 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.) -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. See also [`invpermute!`](@ref). @@ -224,7 +224,7 @@ 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.) -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 06cb6737ccbbb..47a8b70cc5a74 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1179,7 +1179,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. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. See also [`circshift`](@ref). """ @@ -1237,7 +1237,7 @@ 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`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. See also: [`circshift`](@ref). diff --git a/base/reducedim.jl b/base/reducedim.jl index ea674a06acea4..5404a6d139f2d 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -448,7 +448,7 @@ _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. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. @@ -528,7 +528,7 @@ sum(f, A::AbstractArray; dims) Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -603,7 +603,7 @@ prod(f, A::AbstractArray; dims) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -682,7 +682,7 @@ maximum(f, A::AbstractArray; dims) Compute the maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -761,7 +761,7 @@ minimum(f, A::AbstractArray; dims) Compute the minimum value of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -828,7 +828,7 @@ extrema(f, A::AbstractArray; dims) Compute the minimum and maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. !!! compat "Julia 1.8" This method requires Julia 1.8 or later. @@ -905,7 +905,7 @@ all(::Function, ::AbstractArray; dims) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -980,7 +980,7 @@ any(::Function, ::AbstractArray; dims) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -1100,7 +1100,7 @@ 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`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) @@ -1173,7 +1173,7 @@ 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`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) diff --git a/base/sort.jl b/base/sort.jl index 91982e1dc3267..39a9b88865eed 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1583,7 +1583,7 @@ 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. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. # Examples ```jldoctest @@ -1709,7 +1709,7 @@ 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)`. -Behavior is undefined when any mutated argument shares memory with any other argument. +Behavior can be unexpected when any mutated argument shares memory with any other argument. !!! compat "Julia 1.9" The method accepting `dims` requires at least Julia 1.9. diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 1c79a81cea256..c8ae72af6549e 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -103,7 +103,7 @@ As a common convention in Julia (not a syntactic requirement), such a function w 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 undefined when a mutated argument shares memory with another argument (e.g. when one is a view of the other). + The behavior of a mutating function can be unexpected when a mutated argument shares memory with another argument (e.g. when one is a view of the other). ## Argument-type declarations From da87f47d15cabbad4a787bff06257c4e8ec78775 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sat, 30 Sep 2023 21:51:11 +0200 Subject: [PATCH 13/17] Add responsibility warning --- doc/src/manual/functions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index c8ae72af6549e..a4a59bc838f5c 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -103,7 +103,8 @@ As a common convention in Julia (not a syntactic requirement), such a function w 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 (e.g. when one is a view of the other). + 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 is safe, it is the responsibility of the caller to ensure proper behavior on such inputs. ## Argument-type declarations From 31a791f917747caf703fa0ec70f2e5a3aa2caf3d Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sun, 1 Oct 2023 08:07:39 +0200 Subject: [PATCH 14/17] Interpolate warning --- base/Base.jl | 5 +++++ base/abstractarray.jl | 6 +++--- base/abstractset.jl | 8 ++++---- base/accumulate.jl | 8 ++++---- base/array.jl | 6 +++--- base/asyncmap.jl | 2 +- base/combinatorics.jl | 4 ++-- base/multidimensional.jl | 4 ++-- base/reducedim.jl | 20 ++++++++++---------- base/sort.jl | 4 ++-- 10 files changed, 36 insertions(+), 31 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index ecc0f0e5522ed..e7700fa75eeaa 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -115,6 +115,11 @@ 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 = """ +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") diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 8fba1d4526e4d..779ec52f15291 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -905,7 +905,7 @@ 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). -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) See also [`copyto!`](@ref). @@ -1371,7 +1371,7 @@ _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)`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -3344,7 +3344,7 @@ 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. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). diff --git a/base/abstractset.jl b/base/abstractset.jl index d66cb3ec2abb2..a6b123ceab007 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -65,7 +65,7 @@ const ∪ = union Construct the [`union`](@ref) of passed in sets and overwrite `s` with the result. Maintain order with arrays. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -185,7 +185,7 @@ const ∩ = intersect Intersect all passed in sets and overwrite `s` with the result. Maintain order with arrays. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) """ function intersect!(s::AbstractSet, itrs...) for x in itrs @@ -222,7 +222,7 @@ setdiff(s) = union(s) Remove from set `s` (in-place) each element of each iterable from `itrs`. Maintain order with arrays. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -279,7 +279,7 @@ Construct the symmetric difference of the passed in sets, and overwrite `s` with When `s` is an array, the order is maintained. Note that in this case the multiplicity of elements matters. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) """ function symdiff!(s::AbstractSet, itrs...) for x in itrs diff --git a/base/accumulate.jl b/base/accumulate.jl index ee476f23b4e04..a2d8a1d368d86 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -43,7 +43,7 @@ end Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) @@ -153,7 +153,7 @@ cumsum(itr) = accumulate(add_sum, itr) Cumulative product of `A` along the dimension `dims`, storing the result in `B`. See also [`cumprod`](@ref). -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) @@ -164,7 +164,7 @@ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) @@ -307,7 +307,7 @@ 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. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). diff --git a/base/array.jl b/base/array.jl index 0ac8184f73cb1..69712d48e276b 100644 --- a/base/array.jl +++ b/base/array.jl @@ -323,7 +323,7 @@ The `unsafe` prefix on this function indicates that no validation is performed t that N is inbounds on either array. Incorrect usage may corrupt or segfault your program, in the same manner as C. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) """ function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T t1 = @_gc_preserve_begin dest @@ -1783,7 +1783,7 @@ 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)`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) !!! compat "Julia 1.5" Prior to Julia 1.5, `indices` must always be a `UnitRange`. @@ -2786,7 +2786,7 @@ 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. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) `inds` must be an iterator of sorted and unique integer indices. See also [`deleteat!`](@ref). diff --git a/base/asyncmap.jl b/base/asyncmap.jl index 4d81bc9ea2f02..c81afbb7e9115 100644 --- a/base/asyncmap.jl +++ b/base/asyncmap.jl @@ -395,7 +395,7 @@ length(itr::AsyncGenerator) = length(itr.collector.enumerator) Like [`asyncmap`](@ref), but stores output in `results` rather than returning a collection. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_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)) diff --git a/base/combinatorics.jl b/base/combinatorics.jl index 1fbaa58267ee3..4a46e95113840 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -169,7 +169,7 @@ 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.) -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) See also [`invpermute!`](@ref). @@ -224,7 +224,7 @@ 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.) -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 47a8b70cc5a74..cba6748daf900 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1179,7 +1179,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. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) See also [`circshift`](@ref). """ @@ -1237,7 +1237,7 @@ 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`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) See also: [`circshift`](@ref). diff --git a/base/reducedim.jl b/base/reducedim.jl index 5404a6d139f2d..f5a22940310cf 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -448,7 +448,7 @@ _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. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. @@ -528,7 +528,7 @@ sum(f, A::AbstractArray; dims) Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -603,7 +603,7 @@ prod(f, A::AbstractArray; dims) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -682,7 +682,7 @@ maximum(f, A::AbstractArray; dims) Compute the maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -761,7 +761,7 @@ minimum(f, A::AbstractArray; dims) Compute the minimum value of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -828,7 +828,7 @@ extrema(f, A::AbstractArray; dims) Compute the minimum and maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) !!! compat "Julia 1.8" This method requires Julia 1.8 or later. @@ -905,7 +905,7 @@ all(::Function, ::AbstractArray; dims) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -980,7 +980,7 @@ any(::Function, ::AbstractArray; dims) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -1100,7 +1100,7 @@ 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`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) @@ -1173,7 +1173,7 @@ 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`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) diff --git a/base/sort.jl b/base/sort.jl index 39a9b88865eed..455a03ece8e53 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1583,7 +1583,7 @@ 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. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -1709,7 +1709,7 @@ 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)`. -Behavior can be unexpected when any mutated argument shares memory with any other argument. +$(_DOCS_ALIASING_WARNING) !!! compat "Julia 1.9" The method accepting `dims` requires at least Julia 1.9. From e0b38e7a8e0856e94ced6171877c1039ce92546e Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Thu, 26 Oct 2023 21:49:20 +0200 Subject: [PATCH 15/17] Replace safe with expected result --- doc/src/manual/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index a4a59bc838f5c..97f84f35f3eef 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -104,7 +104,7 @@ the call site that at least one of the arguments (often the first one) is being !!! 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 is safe, it is the responsibility of the caller to ensure proper behavior on such inputs. + 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 From 0580e946e3bec8d5e5ef041dde6296a007227cdb Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Thu, 26 Oct 2023 21:52:31 +0200 Subject: [PATCH 16/17] Warning formatting --- base/Base.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/Base.jl b/base/Base.jl index e7700fa75eeaa..16a4a42842501 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -117,7 +117,8 @@ 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 = """ -Behavior can be unexpected when any mutated argument shares memory with any other argument. +!!! warning + Behavior can be unexpected when any mutated argument shares memory with any other argument. """ ## Load essential files and libraries From ac006d00efddc7c138b491927693405c6c0bf527 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Thu, 26 Oct 2023 15:48:09 -0500 Subject: [PATCH 17/17] Qualify use of `_DOCS_ALIASING_WARNING` in `Base.Sort`. --- base/sort.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 455a03ece8e53..b6a2d664e39f4 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1583,7 +1583,7 @@ 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. -$(_DOCS_ALIASING_WARNING) +$(Base._DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -1709,7 +1709,7 @@ 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)`. -$(_DOCS_ALIASING_WARNING) +$(Base._DOCS_ALIASING_WARNING) !!! compat "Julia 1.9" The method accepting `dims` requires at least Julia 1.9.