Skip to content

Commit b55e250

Browse files
authored
avoid type-specialization in show-default (#37591)
Refs #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).
1 parent 978123f commit b55e250

File tree

5 files changed

+74
-37
lines changed

5 files changed

+74
-37
lines changed

base/refpointer.jl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ If `T` is a bitstype, `isassigned(Ref{T}())` will always be true.
2424
2525
When passed as a `ccall` argument (either as a `Ptr` or `Ref` type), a `Ref`
2626
object will be converted to a native pointer to the data it references.
27+
For most `T`, or when converted to a `Ptr{Cvoid}`, this is a pointer to the
28+
object data. When `T` is an `isbits` type, this value may be safely mutated,
29+
otherwise mutation is strictly undefined behavior.
30+
31+
As a special case, setting `T = Any` will instead cause the creation of a
32+
pointer to the reference itself when converted to a `Ptr{Any}`
33+
(a `jl_value_t const* const*` if T is immutable, else a `jl_value_t *const *`).
34+
When converted to a `Ptr{Cvoid}`, it will still return a pointer to the data
35+
region as for any other `T`.
2736
2837
A `C_NULL` instance of `Ptr` can be passed to a `ccall` `Ref` argument to initialize it.
2938
@@ -105,7 +114,7 @@ RefArray(x::AbstractArray{T}, i::Int, roots::Any) where {T} = RefArray{T,typeof(
105114
RefArray(x::AbstractArray{T}, i::Int=1, roots::Nothing=nothing) where {T} = RefArray{T,typeof(x),Nothing}(x, i, nothing)
106115
convert(::Type{Ref{T}}, x::AbstractArray{T}) where {T} = RefArray(x, 1)
107116

108-
function unsafe_convert(P::Type{Ptr{T}}, b::RefArray{T}) where T
117+
function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefArray{T})::P where T
109118
if allocatedinline(T)
110119
p = pointer(b.x, b.i)
111120
elseif isconcretetype(T) && T.mutable
@@ -114,12 +123,11 @@ function unsafe_convert(P::Type{Ptr{T}}, b::RefArray{T}) where T
114123
# see comment on equivalent branch for RefValue
115124
p = pointerref(Ptr{Ptr{Cvoid}}(pointer(b.x, b.i)), 1, Core.sizeof(Ptr{Cvoid}))
116125
end
117-
return convert(P, p)
126+
return p
118127
end
119-
function unsafe_convert(P::Type{Ptr{Any}}, b::RefArray{Any})
120-
return convert(P, pointer(b.x, b.i))
128+
function unsafe_convert(::Type{Ptr{Any}}, b::RefArray{Any})::Ptr{Any}
129+
return pointer(b.x, b.i)
121130
end
122-
unsafe_convert(::Type{Ptr{Cvoid}}, b::RefArray{T}) where {T} = convert(Ptr{Cvoid}, unsafe_convert(Ptr{T}, b))
123131

124132
###
125133
if is_primary_base_module

base/refvalue.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ true
3535
"""
3636
isassigned(x::RefValue) = isdefined(x, :x)
3737

38-
function unsafe_convert(P::Type{Ptr{T}}, b::RefValue{T}) where T
38+
function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefValue{T})::P where T
3939
if allocatedinline(T)
4040
p = pointer_from_objref(b)
4141
elseif isconcretetype(T) && T.mutable
@@ -47,12 +47,11 @@ function unsafe_convert(P::Type{Ptr{T}}, b::RefValue{T}) where T
4747
# which also ensures this returns same pointer as the one rooted in the `RefValue` object.
4848
p = pointerref(Ptr{Ptr{Cvoid}}(pointer_from_objref(b)), 1, Core.sizeof(Ptr{Cvoid}))
4949
end
50-
return convert(P, p)::Ptr{T}
50+
return p
5151
end
52-
function unsafe_convert(P::Type{Ptr{Any}}, b::RefValue{Any})
53-
return convert(P, pointer_from_objref(b))::Ptr{Any}
52+
function unsafe_convert(::Type{Ptr{Any}}, b::RefValue{Any})::Ptr{Any}
53+
return pointer_from_objref(b)
5454
end
55-
unsafe_convert(::Type{Ptr{Cvoid}}, b::RefValue{T}) where {T} = convert(Ptr{Cvoid}, unsafe_convert(Ptr{T}, b))::Ptr{Cvoid}
5655

5756
getindex(b::RefValue) = b.x
5857
setindex!(b::RefValue, x) = (b.x = x; b)

base/show.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ function _show_default(io::IO, @nospecialize(x))
416416
end
417417
else
418418
print(io, "0x")
419-
r = Ref(x)
419+
r = Ref{Any}(x)
420420
GC.@preserve r begin
421421
p = unsafe_convert(Ptr{Cvoid}, r)
422422
for i in (nb - 1):-1:0

doc/src/manual/calling-c-and-fortran-code.md

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -365,30 +365,31 @@ an `Int` in Julia).
365365
| `unsigned char` | `CHARACTER` | `Cuchar` | `UInt8` |
366366
| `bool` (_Bool in C99+) | | `Cuchar` | `UInt8` |
367367
| `short` | `INTEGER*2`, `LOGICAL*2` | `Cshort` | `Int16` |
368-
| `unsigned short` |   | `Cushort` | `UInt16` |
368+
| `unsigned short` | | `Cushort` | `UInt16` |
369369
| `int`, `BOOL` (C, typical) | `INTEGER*4`, `LOGICAL*4` | `Cint` | `Int32` |
370-
| `unsigned int` |   | `Cuint` | `UInt32` |
370+
| `unsigned int` | | `Cuint` | `UInt32` |
371371
| `long long` | `INTEGER*8`, `LOGICAL*8` | `Clonglong` | `Int64` |
372-
| `unsigned long long` |   | `Culonglong` | `UInt64` |
373-
| `intmax_t` |   | `Cintmax_t` | `Int64` |
374-
| `uintmax_t` |   | `Cuintmax_t` | `UInt64` |
372+
| `unsigned long long` | | `Culonglong` | `UInt64` |
373+
| `intmax_t` | | `Cintmax_t` | `Int64` |
374+
| `uintmax_t` | | `Cuintmax_t` | `UInt64` |
375375
| `float` | `REAL*4i` | `Cfloat` | `Float32` |
376376
| `double` | `REAL*8` | `Cdouble` | `Float64` |
377-
| `complex float` | `COMPLEX*8` | `ComplexF32` | `Complex{Float32}` |
377+
| `complex float` | `COMPLEX*8` | `ComplexF32` | `Complex{Float32}` |
378378
| `complex double` | `COMPLEX*16` | `ComplexF64` | `Complex{Float64}` |
379-
| `ptrdiff_t` |   | `Cptrdiff_t` | `Int` |
380-
| `ssize_t` |   | `Cssize_t` | `Int` |
381-
| `size_t` |   | `Csize_t` | `UInt` |
382-
| `void` |   |   | `Cvoid` |
383-
| `void` and `[[noreturn]]` or `_Noreturn` |   |   | `Union{}` |
384-
| `void*` |   |   | `Ptr{Cvoid}` |
385-
| `T*` (where T represents an appropriately defined type) |   |   | `Ref{T}` |
386-
| `char*` (or `char[]`, e.g. a string) | `CHARACTER*N` |   | `Cstring` if NUL-terminated, or `Ptr{UInt8}` if not |
387-
| `char**` (or `*char[]`) |   |   | `Ptr{Ptr{UInt8}}` |
388-
| `jl_value_t*` (any Julia Type) |   |   | `Any` |
389-
| `jl_value_t**` (a reference to a Julia Type) |   |   | `Ref{Any}` |
390-
| `va_arg` |   |   | Not supported |
391-
| `...` (variadic function specification) |   |   | `T...` (where `T` is one of the above types, variadic functions of different argument types are not supported) |
379+
| `ptrdiff_t` | | `Cptrdiff_t` | `Int` |
380+
| `ssize_t` | | `Cssize_t` | `Int` |
381+
| `size_t` | | `Csize_t` | `UInt` |
382+
| `void` | | | `Cvoid` |
383+
| `void` and `[[noreturn]]` or `_Noreturn` | | | `Union{}` |
384+
| `void*` | | | `Ptr{Cvoid}` (or similarly `Ref{Cvoid}`) |
385+
| `T*` (where T represents an appropriately defined type) | | | `Ref{T}` (T may be safely mutated only if T is an isbits type) |
386+
| `char*` (or `char[]`, e.g. a string) | `CHARACTER*N` | | `Cstring` if NUL-terminated, or `Ptr{UInt8}` if not |
387+
| `char**` (or `*char[]`) | | | `Ptr{Ptr{UInt8}}` |
388+
| `jl_value_t*` (any Julia Type) | | | `Any` |
389+
| `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) |
390+
| `va_arg` | | | Not supported |
391+
| `...` (variadic function specification) | | | `T...` (where `T` is one of the above types, when using the `ccall` function) |
392+
| `...` (variadic function specification) | | | `; va_arg1::T, va_arg2::S, etc.` (only supported with `@ccall` macro) |
392393

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

652653
* `Any`
653654
* argument value must be a valid Julia object
654-
* `jl_value_t**`
655+
* `jl_value_t* const*`
655656

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

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

665-
* `Ptr{Cvoid}` (you may need to use [`@cfunction`](@ref) explicitly to create this pointer)
668+
* `Ptr{Cvoid}` (you may need to use [`@cfunction`](@ref) explicitly to
669+
create this pointer)
666670
* `...` (e.g. a vararg)
667671

668-
* `T...`, where `T` is the Julia type
672+
* [for `ccall`]: `T...`, where `T` is the single Julia type of all
673+
remaining arguments
674+
* [for `@ccall`]: `; va_arg1::T, va_arg2::S, etc`, where `T` and `S` are
675+
the Julia type (i.e. separate the regular arguments from varargs with
676+
a `;`)
669677
* currently unsupported by `@cfunction`
670678
* `va_arg`
671679

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

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

706713
* If the memory is already owned by Julia, or is an `isbits` type, and is known to be non-null:

test/ccall.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,29 @@ for i in 1:3
13251325
ccall((:test_echo_p, libccalltest), Ptr{Cvoid}, (Any,), f17413())
13261326
end
13271327

1328+
let r = Ref{Any}(10)
1329+
@GC.preserve r begin
1330+
pa = Base.unsafe_convert(Ptr{Any}, r) # pointer to value
1331+
pv = Base.unsafe_convert(Ptr{Cvoid}, r) # pointer to data
1332+
@test Ptr{Cvoid}(pa) != pv
1333+
@test unsafe_load(pa) === 10
1334+
@test unsafe_load(Ptr{Ptr{Cvoid}}(pa)) === pv
1335+
@test unsafe_load(Ptr{Int}(pv)) === 10
1336+
end
1337+
end
1338+
1339+
let r = Ref{Any}("123456789")
1340+
@GC.preserve r begin
1341+
pa = Base.unsafe_convert(Ptr{Any}, r) # pointer to value
1342+
pv = Base.unsafe_convert(Ptr{Cvoid}, r) # pointer to data
1343+
@test Ptr{Cvoid}(pa) != pv
1344+
@test unsafe_load(pa) === r[]
1345+
@test unsafe_load(Ptr{Ptr{Cvoid}}(pa)) === pv
1346+
@test unsafe_load(Ptr{Int}(pv)) === length(r[])
1347+
end
1348+
end
1349+
1350+
13281351
struct SpillPint
13291352
a::Ptr{Cint}
13301353
b::Ptr{Cint}

0 commit comments

Comments
 (0)