Skip to content

Commit df39cee

Browse files
authored
replace many unsafe_convert methods with safer cconvert ones (#51764)
This seems the correct way to define most conversions, since the unsafe logic should typically be isolated to a few areas, and everywhere else just defines conversions to it. This ensures the root is preserved even if the user later makes unexpected changes to the parent object (although the length might have later become inconsistent at that point, so it is not a guaranteed fix for memory issues).
1 parent 2d8325b commit df39cee

File tree

18 files changed

+61
-44
lines changed

18 files changed

+61
-44
lines changed

base/abstractarray.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,10 +1229,10 @@ end
12291229
# note: the following type definitions don't mean any AbstractArray is convertible to
12301230
# a data Ref. they just map the array element type to the pointer type for
12311231
# convenience in cases that work.
1232-
pointer(x::AbstractArray{T}) where {T} = unsafe_convert(Ptr{T}, x)
1232+
pointer(x::AbstractArray{T}) where {T} = unsafe_convert(Ptr{T}, cconvert(Ptr{T}, x))
12331233
function pointer(x::AbstractArray{T}, i::Integer) where T
12341234
@inline
1235-
unsafe_convert(Ptr{T}, x) + Int(_memory_offset(x, i))::Int
1235+
pointer(x) + Int(_memory_offset(x, i))::Int
12361236
end
12371237

12381238
# The distance from pointer(x) to the element at x[I...] in bytes

base/c.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ cconvert(::Type{Cstring}, s::AbstractString) =
200200
function cconvert(::Type{Cwstring}, s::AbstractString)
201201
v = transcode(Cwchar_t, String(s))
202202
push!(v, 0)
203-
return v
203+
return cconvert(Cwstring, v)
204204
end
205205

206206
eltype(::Type{Cstring}) = Cchar
@@ -218,16 +218,19 @@ function unsafe_convert(::Type{Cstring}, s::String)
218218
return Cstring(p)
219219
end
220220

221-
function unsafe_convert(::Type{Cwstring}, v::Vector{Cwchar_t})
221+
unsafe_convert(::Type{Cstring}, s::Union{Vector{UInt8},Vector{Int8}}) = Cstring(unsafe_convert(Ptr{Cvoid}, s))
222+
223+
function cconvert(::Type{Cwstring}, v::Vector{Cwchar_t})
222224
for i = 1:length(v)-1
223225
v[i] == 0 &&
224226
throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(v))"))
225227
end
226228
v[end] == 0 ||
227229
throw(ArgumentError("C string data must be NUL terminated: $(repr(v))"))
228-
p = unsafe_convert(Ptr{Cwchar_t}, v)
229-
return Cwstring(p)
230+
return cconvert(Ptr{Cwchar_t}, v)
230231
end
232+
unsafe_convert(::Type{Cwstring}, s) = Cwstring(unsafe_convert(Ptr{Cwchar_t}, s))
233+
unsafe_convert(::Type{Cwstring}, s::Cwstring) = s
231234

232235
# symbols are guaranteed not to contain embedded NUL
233236
cconvert(::Type{Cstring}, s::Symbol) = s

base/permuteddimsarray.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,8 @@ Base.parent(A::PermutedDimsArray) = A.parent
4949
Base.size(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(size(parent(A)), perm)
5050
Base.axes(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(axes(parent(A)), perm)
5151
Base.has_offset_axes(A::PermutedDimsArray) = Base.has_offset_axes(A.parent)
52-
5352
Base.similar(A::PermutedDimsArray, T::Type, dims::Base.Dims) = similar(parent(A), T, dims)
54-
55-
Base.unsafe_convert(::Type{Ptr{T}}, A::PermutedDimsArray{T}) where {T} = Base.unsafe_convert(Ptr{T}, parent(A))
53+
Base.cconvert(::Type{Ptr{T}}, A::PermutedDimsArray{T}) where {T} = Base.cconvert(Ptr{T}, parent(A))
5654

5755
# It's OK to return a pointer to the first element, and indeed quite
5856
# useful for wrapping C routines that require a different storage

base/pointer.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,20 @@ See also [`cconvert`](@ref)
5454
"""
5555
function unsafe_convert end
5656

57+
# convert strings to String etc. to pass as pointers
58+
cconvert(::Type{Ptr{UInt8}}, s::AbstractString) = String(s)
59+
cconvert(::Type{Ptr{Int8}}, s::AbstractString) = String(s)
5760
unsafe_convert(::Type{Ptr{UInt8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{UInt8}, (Any,), x)
5861
unsafe_convert(::Type{Ptr{Int8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{Int8}, (Any,), x)
5962
unsafe_convert(::Type{Ptr{UInt8}}, s::String) = ccall(:jl_string_ptr, Ptr{UInt8}, (Any,), s)
6063
unsafe_convert(::Type{Ptr{Int8}}, s::String) = ccall(:jl_string_ptr, Ptr{Int8}, (Any,), s)
61-
# convert strings to String etc. to pass as pointers
62-
cconvert(::Type{Ptr{UInt8}}, s::AbstractString) = String(s)
63-
cconvert(::Type{Ptr{Int8}}, s::AbstractString) = String(s)
6464

6565
unsafe_convert(::Type{Ptr{T}}, a::Array{T}) where {T} = ccall(:jl_array_ptr, Ptr{T}, (Any,), a)
6666
unsafe_convert(::Type{Ptr{S}}, a::AbstractArray{T}) where {S,T} = convert(Ptr{S}, unsafe_convert(Ptr{T}, a))
6767
unsafe_convert(::Type{Ptr{T}}, a::AbstractArray{T}) where {T} = error("conversion to pointer not defined for $(typeof(a))")
68+
# TODO: add this deprecation to give a better error:
69+
# cconvert(::Type{<:Ptr}, a::AbstractArray) = error("conversion to pointer not defined for $(typeof(a))")
70+
# unsafe_convert(::Type{Ptr{T}}, a::AbstractArray{T}) where {T} = error("missing call to cconvert for call to unsafe_convert for AbstractArray")
6871

6972
# unsafe pointer to array conversions
7073
"""

base/refpointer.jl

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,12 @@ if is_primary_base_module
148148
Ref(x::Ptr{T}, i::Integer) where {T} = x + (i - 1) * Core.sizeof(T)
149149

150150
# convert Arrays to pointer arrays for ccall
151-
function Ref{P}(a::Array{<:Union{Ptr,Cwstring,Cstring}}) where P<:Union{Ptr,Cwstring,Cstring}
152-
return RefArray(a) # effectively a no-op
153-
end
151+
# For example `["a", "b"]` to Ptr{Cstring} for `char **argv`
154152
function Ref{P}(a::Array{T}) where P<:Union{Ptr,Cwstring,Cstring} where T
155-
if (!isbitstype(T) && T <: eltype(P))
153+
if (isbitstype(T) ? T <: Ptr || T <: Union{Cwstring,Cstring} : T <: eltype(P))
156154
# this Array already has the right memory layout for the requested Ref
157-
return RefArray(a,1,false) # root something, so that this function is type-stable
155+
# but the wrong eltype for the constructor
156+
return RefArray{P,typeof(a),Nothing}(a, 1, nothing) # effectively a no-op
158157
else
159158
ptrs = Vector{P}(undef, length(a)+1)
160159
roots = Vector{Any}(undef, length(a))
@@ -164,14 +163,14 @@ if is_primary_base_module
164163
roots[i] = root
165164
end
166165
ptrs[length(a)+1] = C_NULL
167-
return RefArray(ptrs,1,roots)
166+
return RefArray{P,typeof(ptrs),typeof(roots)}(ptrs, 1, roots)
168167
end
169168
end
170169
Ref(x::AbstractArray, i::Integer) = RefArray(x, i)
171170
end
172171

173-
cconvert(::Type{Ptr{P}}, a::Array{<:Ptr}) where {P<:Ptr} = a
174-
cconvert(::Type{Ref{P}}, a::Array{<:Ptr}) where {P<:Ptr} = a
172+
cconvert(::Type{Ptr{P}}, a::Array{P}) where {P<:Union{Ptr,Cwstring,Cstring}} = a
173+
cconvert(::Type{Ref{P}}, a::Array{P}) where {P<:Union{Ptr,Cwstring,Cstring}} = a
175174
cconvert(::Type{Ptr{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a)
176175
cconvert(::Type{Ref{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a)
177176

base/reinterpretarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ axes(a::NonReshapedReinterpretArray{T,0}) where {T} = ()
350350
has_offset_axes(a::ReinterpretArray) = has_offset_axes(a.parent)
351351

352352
elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T)
353-
unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = Ptr{T}(unsafe_convert(Ptr{S},a.parent))
353+
cconvert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = cconvert(Ptr{S}, a.parent)
354354

355355
@inline @propagate_inbounds function getindex(a::NonReshapedReinterpretArray{T,0,S}) where {T,S}
356356
if isprimitivetype(T) && isprimitivetype(S)

base/reshapedarray.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ setindex!(A::ReshapedRange, val, index::ReshapedIndex) = _rs_setindex!_err()
293293

294294
@noinline _rs_setindex!_err() = error("indexed assignment fails for a reshaped range; consider calling collect")
295295

296-
unsafe_convert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = unsafe_convert(Ptr{T}, parent(a))
296+
cconvert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = cconvert(Ptr{T}, parent(a))
297297

298298
# Add a few handy specializations to further speed up views of reshaped ranges
299299
const ReshapedUnitRange{T,N,A<:AbstractUnitRange} = ReshapedArray{T,N,A,Tuple{}}
@@ -304,9 +304,13 @@ compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{ReshapedRange
304304
(@inline; first(I[1]) - first(axes1(I[1]))*stride1)
305305
substrides(strds::NTuple{N,Int}, I::Tuple{ReshapedUnitRange, Vararg{Any}}) where N =
306306
(size_to_strides(strds[1], size(I[1])...)..., substrides(tail(strds), tail(I))...)
307-
unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{Union{RangeIndex,ReshapedUnitRange}}}}) where {T,N,P} =
308-
unsafe_convert(Ptr{T}, V.parent) + (first_index(V)-1)*sizeof(T)
309307

308+
# cconvert(::Type{<:Ptr}, V::SubArray{T,N,P,<:Tuple{Vararg{Union{RangeIndex,ReshapedUnitRange}}}}) where {T,N,P} = V
309+
function unsafe_convert(::Type{Ptr{S}}, V::SubArray{T,N,P,<:Tuple{Vararg{Union{RangeIndex,ReshapedUnitRange}}}}) where {S,T,N,P}
310+
parent = V.parent
311+
p = cconvert(Ptr{T}, parent) # XXX: this should occur in cconvert, the result is not GC-rooted
312+
return Ptr{S}(unsafe_convert(Ptr{T}, p) + (first_index(V)-1)*sizeof(T))
313+
end
310314

311315
_checkcontiguous(::Type{Bool}, A::AbstractArray) = false
312316
# `strides(A::DenseArray)` calls `size_to_strides` by default.

base/secretbuffer.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,7 @@ function write(io::IO, s::SecretBuffer)
140140
return nb
141141
end
142142

143-
cconvert(::Type{Cstring}, s::SecretBuffer) = unsafe_convert(Cstring, s)
144-
function unsafe_convert(::Type{Cstring}, s::SecretBuffer)
143+
function cconvert(::Type{Cstring}, s::SecretBuffer)
145144
# Ensure that no nuls appear in the valid region
146145
if any(==(0x00), s.data[i] for i in 1:s.size)
147146
throw(ArgumentError("`SecretBuffers` containing nul bytes cannot be converted to C strings"))
@@ -152,8 +151,10 @@ function unsafe_convert(::Type{Cstring}, s::SecretBuffer)
152151
write(s, '\0')
153152
s.ptr = p
154153
s.size -= 1
155-
return Cstring(unsafe_convert(Ptr{Cchar}, s.data))
154+
return s.data
156155
end
156+
# optional shim for manual calls to unsafe_convert:
157+
# unsafe_convert(::Type{Cstring}, s::SecretBuffer) = unsafe_convert(Cstring, cconvert(Cstring, s))
157158

158159
seek(io::SecretBuffer, n::Integer) = (io.ptr = max(min(n+1, io.size+1), 1); io)
159160
seekend(io::SecretBuffer) = seek(io, io.size+1)

base/strings/basic.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -800,8 +800,8 @@ IndexStyle(::Type{<:CodeUnits}) = IndexLinear()
800800

801801
write(io::IO, s::CodeUnits) = write(io, s.s)
802802

803-
unsafe_convert(::Type{Ptr{T}}, s::CodeUnits{T}) where {T} = unsafe_convert(Ptr{T}, s.s)
804-
unsafe_convert(::Type{Ptr{Int8}}, s::CodeUnits{UInt8}) = unsafe_convert(Ptr{Int8}, s.s)
803+
cconvert(::Type{Ptr{T}}, s::CodeUnits{T}) where {T} = cconvert(Ptr{T}, s.s)
804+
cconvert(::Type{Ptr{Int8}}, s::CodeUnits{UInt8}) = cconvert(Ptr{Int8}, s.s)
805805

806806
"""
807807
codeunits(s::AbstractString)

base/subarray.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,8 +466,11 @@ find_extended_inds(::ScalarIndex, I...) = (@inline; find_extended_inds(I...))
466466
find_extended_inds(i1, I...) = (@inline; (i1, find_extended_inds(I...)...))
467467
find_extended_inds() = ()
468468

469-
function unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {T,N,P}
470-
return unsafe_convert(Ptr{T}, V.parent) + _memory_offset(V.parent, map(first, V.indices)...)
469+
# cconvert(::Type{<:Ptr}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {T,N,P} = V
470+
function unsafe_convert(::Type{Ptr{S}}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {S,T,N,P}
471+
parent = V.parent
472+
p = cconvert(Ptr{T}, parent) # XXX: this should occur in cconvert, the result is not GC-rooted
473+
return Ptr{S}(unsafe_convert(Ptr{T}, p) + _memory_offset(parent, map(first, V.indices)...))
471474
end
472475

473476
pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i)

0 commit comments

Comments
 (0)