Skip to content

Commit

Permalink
avoid type-specialization in show-default (JuliaLang#37591)
Browse files Browse the repository at this point in the history
Refs JuliaLang#37582

Fixes the data pointer for conversion of Ref{Any} to Ptr{Cvoid} to point
at the data, instead of the pointer. Then use that in show_default to
avoid specializing the Ref(Value) types of each input (causing poor
inference of unsafe_convert later).
  • Loading branch information
vtjnash authored Sep 24, 2020
1 parent 978123f commit b55e250
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 37 deletions.
18 changes: 13 additions & 5 deletions base/refpointer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ If `T` is a bitstype, `isassigned(Ref{T}())` will always be true.
When passed as a `ccall` argument (either as a `Ptr` or `Ref` type), a `Ref`
object will be converted to a native pointer to the data it references.
For most `T`, or when converted to a `Ptr{Cvoid}`, this is a pointer to the
object data. When `T` is an `isbits` type, this value may be safely mutated,
otherwise mutation is strictly undefined behavior.
As a special case, setting `T = Any` will instead cause the creation of a
pointer to the reference itself when converted to a `Ptr{Any}`
(a `jl_value_t const* const*` if T is immutable, else a `jl_value_t *const *`).
When converted to a `Ptr{Cvoid}`, it will still return a pointer to the data
region as for any other `T`.
A `C_NULL` instance of `Ptr` can be passed to a `ccall` `Ref` argument to initialize it.
Expand Down Expand Up @@ -105,7 +114,7 @@ RefArray(x::AbstractArray{T}, i::Int, roots::Any) where {T} = RefArray{T,typeof(
RefArray(x::AbstractArray{T}, i::Int=1, roots::Nothing=nothing) where {T} = RefArray{T,typeof(x),Nothing}(x, i, nothing)
convert(::Type{Ref{T}}, x::AbstractArray{T}) where {T} = RefArray(x, 1)

function unsafe_convert(P::Type{Ptr{T}}, b::RefArray{T}) where T
function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefArray{T})::P where T
if allocatedinline(T)
p = pointer(b.x, b.i)
elseif isconcretetype(T) && T.mutable
Expand All @@ -114,12 +123,11 @@ function unsafe_convert(P::Type{Ptr{T}}, b::RefArray{T}) where T
# see comment on equivalent branch for RefValue
p = pointerref(Ptr{Ptr{Cvoid}}(pointer(b.x, b.i)), 1, Core.sizeof(Ptr{Cvoid}))
end
return convert(P, p)
return p
end
function unsafe_convert(P::Type{Ptr{Any}}, b::RefArray{Any})
return convert(P, pointer(b.x, b.i))
function unsafe_convert(::Type{Ptr{Any}}, b::RefArray{Any})::Ptr{Any}
return pointer(b.x, b.i)
end
unsafe_convert(::Type{Ptr{Cvoid}}, b::RefArray{T}) where {T} = convert(Ptr{Cvoid}, unsafe_convert(Ptr{T}, b))

###
if is_primary_base_module
Expand Down
9 changes: 4 additions & 5 deletions base/refvalue.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ true
"""
isassigned(x::RefValue) = isdefined(x, :x)

function unsafe_convert(P::Type{Ptr{T}}, b::RefValue{T}) where T
function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefValue{T})::P where T
if allocatedinline(T)
p = pointer_from_objref(b)
elseif isconcretetype(T) && T.mutable
Expand All @@ -47,12 +47,11 @@ function unsafe_convert(P::Type{Ptr{T}}, b::RefValue{T}) where T
# which also ensures this returns same pointer as the one rooted in the `RefValue` object.
p = pointerref(Ptr{Ptr{Cvoid}}(pointer_from_objref(b)), 1, Core.sizeof(Ptr{Cvoid}))
end
return convert(P, p)::Ptr{T}
return p
end
function unsafe_convert(P::Type{Ptr{Any}}, b::RefValue{Any})
return convert(P, pointer_from_objref(b))::Ptr{Any}
function unsafe_convert(::Type{Ptr{Any}}, b::RefValue{Any})::Ptr{Any}
return pointer_from_objref(b)
end
unsafe_convert(::Type{Ptr{Cvoid}}, b::RefValue{T}) where {T} = convert(Ptr{Cvoid}, unsafe_convert(Ptr{T}, b))::Ptr{Cvoid}

getindex(b::RefValue) = b.x
setindex!(b::RefValue, x) = (b.x = x; b)
2 changes: 1 addition & 1 deletion base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ function _show_default(io::IO, @nospecialize(x))
end
else
print(io, "0x")
r = Ref(x)
r = Ref{Any}(x)
GC.@preserve r begin
p = unsafe_convert(Ptr{Cvoid}, r)
for i in (nb - 1):-1:0
Expand Down
59 changes: 33 additions & 26 deletions doc/src/manual/calling-c-and-fortran-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,30 +365,31 @@ an `Int` in Julia).
| `unsigned char` | `CHARACTER` | `Cuchar` | `UInt8` |
| `bool` (_Bool in C99+) | | `Cuchar` | `UInt8` |
| `short` | `INTEGER*2`, `LOGICAL*2` | `Cshort` | `Int16` |
| `unsigned short` |   | `Cushort` | `UInt16` |
| `unsigned short` | | `Cushort` | `UInt16` |
| `int`, `BOOL` (C, typical) | `INTEGER*4`, `LOGICAL*4` | `Cint` | `Int32` |
| `unsigned int` |   | `Cuint` | `UInt32` |
| `unsigned int` | | `Cuint` | `UInt32` |
| `long long` | `INTEGER*8`, `LOGICAL*8` | `Clonglong` | `Int64` |
| `unsigned long long` |   | `Culonglong` | `UInt64` |
| `intmax_t` |   | `Cintmax_t` | `Int64` |
| `uintmax_t` |   | `Cuintmax_t` | `UInt64` |
| `unsigned long long` | | `Culonglong` | `UInt64` |
| `intmax_t` | | `Cintmax_t` | `Int64` |
| `uintmax_t` | | `Cuintmax_t` | `UInt64` |
| `float` | `REAL*4i` | `Cfloat` | `Float32` |
| `double` | `REAL*8` | `Cdouble` | `Float64` |
| `complex float` | `COMPLEX*8` | `ComplexF32` | `Complex{Float32}` |
| `complex float` | `COMPLEX*8` | `ComplexF32` | `Complex{Float32}` |
| `complex double` | `COMPLEX*16` | `ComplexF64` | `Complex{Float64}` |
| `ptrdiff_t` |   | `Cptrdiff_t` | `Int` |
| `ssize_t` |   | `Cssize_t` | `Int` |
| `size_t` |   | `Csize_t` | `UInt` |
| `void` |   |   | `Cvoid` |
| `void` and `[[noreturn]]` or `_Noreturn` |   |   | `Union{}` |
| `void*` |   |   | `Ptr{Cvoid}` |
| `T*` (where T represents an appropriately defined type) |   |   | `Ref{T}` |
| `char*` (or `char[]`, e.g. a string) | `CHARACTER*N` |   | `Cstring` if NUL-terminated, or `Ptr{UInt8}` if not |
| `char**` (or `*char[]`) |   |   | `Ptr{Ptr{UInt8}}` |
| `jl_value_t*` (any Julia Type) |   |   | `Any` |
| `jl_value_t**` (a reference to a Julia Type) |   |   | `Ref{Any}` |
| `va_arg` |   |   | Not supported |
| `...` (variadic function specification) |   |   | `T...` (where `T` is one of the above types, variadic functions of different argument types are not supported) |
| `ptrdiff_t` | | `Cptrdiff_t` | `Int` |
| `ssize_t` | | `Cssize_t` | `Int` |
| `size_t` | | `Csize_t` | `UInt` |
| `void` | | | `Cvoid` |
| `void` and `[[noreturn]]` or `_Noreturn` | | | `Union{}` |
| `void*` | | | `Ptr{Cvoid}` (or similarly `Ref{Cvoid}`) |
| `T*` (where T represents an appropriately defined type) | | | `Ref{T}` (T may be safely mutated only if T is an isbits type) |
| `char*` (or `char[]`, e.g. a string) | `CHARACTER*N` | | `Cstring` if NUL-terminated, or `Ptr{UInt8}` if not |
| `char**` (or `*char[]`) | | | `Ptr{Ptr{UInt8}}` |
| `jl_value_t*` (any Julia Type) | | | `Any` |
| `jl_value_t* const*` (a reference to a Julia value) | | | `Ref{Any}` (const, since mutation would require a write barrier, which is not possible to insert correctly) |
| `va_arg` | | | Not supported |
| `...` (variadic function specification) | | | `T...` (where `T` is one of the above types, when using the `ccall` function) |
| `...` (variadic function specification) | | | `; va_arg1::T, va_arg2::S, etc.` (only supported with `@ccall` macro) |

The [`Cstring`](@ref) type is essentially a synonym for `Ptr{UInt8}`, except the conversion to `Cstring`
throws an error if the Julia string contains any embedded NUL characters (which would cause the
Expand Down Expand Up @@ -651,21 +652,28 @@ For translating a C argument list to Julia:

* `Any`
* argument value must be a valid Julia object
* `jl_value_t**`
* `jl_value_t* const*`

* `Ref{Any}`
* argument value must be a valid Julia object (or `C_NULL`)
* argument list must be a valid Julia object (or `C_NULL`)
* cannot be used for an output parameter, unless the user is able to
manage to separate arrange for the object to be GC-preserved
* `T*`

* `Ref{T}`, where `T` is the Julia type corresponding to `T`
* argument value will be copied if it is an `isbits` type otherwise, the value must be a valid Julia
object
* argument value will be copied if it is an `inlinealloc` type (which
includes `isbits` otherwise, the value must be a valid Julia object
* `T (*)(...)` (e.g. a pointer to a function)

* `Ptr{Cvoid}` (you may need to use [`@cfunction`](@ref) explicitly to create this pointer)
* `Ptr{Cvoid}` (you may need to use [`@cfunction`](@ref) explicitly to
create this pointer)
* `...` (e.g. a vararg)

* `T...`, where `T` is the Julia type
* [for `ccall`]: `T...`, where `T` is the single Julia type of all
remaining arguments
* [for `@ccall`]: `; va_arg1::T, va_arg2::S, etc`, where `T` and `S` are
the Julia type (i.e. separate the regular arguments from varargs with
a `;`)
* currently unsupported by `@cfunction`
* `va_arg`

Expand Down Expand Up @@ -700,7 +708,6 @@ For translating a C return type to Julia:
* `jl_value_t**`

* `Ptr{Any}` (`Ref{Any}` is invalid as a return type)
* argument value must be a valid Julia object (or `C_NULL`)
* `T*`

* If the memory is already owned by Julia, or is an `isbits` type, and is known to be non-null:
Expand Down
23 changes: 23 additions & 0 deletions test/ccall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,29 @@ for i in 1:3
ccall((:test_echo_p, libccalltest), Ptr{Cvoid}, (Any,), f17413())
end

let r = Ref{Any}(10)
@GC.preserve r begin
pa = Base.unsafe_convert(Ptr{Any}, r) # pointer to value
pv = Base.unsafe_convert(Ptr{Cvoid}, r) # pointer to data
@test Ptr{Cvoid}(pa) != pv
@test unsafe_load(pa) === 10
@test unsafe_load(Ptr{Ptr{Cvoid}}(pa)) === pv
@test unsafe_load(Ptr{Int}(pv)) === 10
end
end

let r = Ref{Any}("123456789")
@GC.preserve r begin
pa = Base.unsafe_convert(Ptr{Any}, r) # pointer to value
pv = Base.unsafe_convert(Ptr{Cvoid}, r) # pointer to data
@test Ptr{Cvoid}(pa) != pv
@test unsafe_load(pa) === r[]
@test unsafe_load(Ptr{Ptr{Cvoid}}(pa)) === pv
@test unsafe_load(Ptr{Int}(pv)) === length(r[])
end
end


struct SpillPint
a::Ptr{Cint}
b::Ptr{Cint}
Expand Down

0 comments on commit b55e250

Please sign in to comment.