• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #38004

08 Feb 2025 04:15AM UTC coverage: 19.401% (-3.6%) from 23.008%
#38004

push

local

web-flow
Inference: propagate struct initialization info on `setfield!` (#57222)

When a variable has a field set with `setfield!(var, field, value)`,
inference now assumes that this specific field is defined and may for
example constant-propagate `isdefined(var, field)` as `true`.
`PartialStruct`, the lattice element used to encode this information,
still has a few limitations in terms of what it may represent (it cannot
represent mutable structs with non-contiguously defined fields yet),
further work on extending it would increase the impact of this change.

Consider the following function:
```julia
julia> function f()
           a = A(1)
           setfield!(a, :y, 2)
           invokelatest(identity, a)
           isdefined(a, :y) && return 1.0
           a
       end
f (generic function with 1 method)
```

Here is before on `master`:
```julia
julia> @code_typed f()
CodeInfo(
1 ─ %1 = %new(Main.A, 1)::A
│          builtin Main.setfield!(%1, :y, 2)::Int64
│        dynamic builtin (Core._call_latest)(identity, %1)::Any
│   %4 =   builtin Main.isdefined(%1, :y)::Bool
└──      goto #3 if not %4
2 ─      return 1.0
3 ─      return %1
) => Union{Float64, A}
```

And after this PR:
```julia
julia> @code_typed f()
CodeInfo(
1 ─ %1 = %new(Main.A, 1)::A
│          builtin Main.setfield!(%1, :y, 2)::Int64
│        dynamic builtin (Core._call_latest)(identity, %1)::Any
└──      return 1.0
) => Float64
```

---------

Co-authored-by: Cédric Belmant <cedric.belmant@juliahub.com>

9440 of 48658 relevant lines covered (19.4%)

95900.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

8.47
/base/refpointer.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
import Core: Ref
4

5
"""
6
    Ref{T}
7

8
An object that safely references data of type `T`. This type is guaranteed to point to
9
valid, Julia-allocated memory of the correct type. The underlying data is protected from
10
freeing by the garbage collector as long as the `Ref` itself is referenced.
11

12
In Julia, `Ref` objects are dereferenced (loaded or stored) with `[]`.
13

14
Creation of a `Ref` to a value `x` of type `T` is usually written `Ref(x)`.
15
Additionally, for creating interior pointers to containers (such as Array or Ptr),
16
it can be written `Ref(a, i)` for creating a reference to the `i`-th element of `a`.
17

18
`Ref{T}()` creates a reference to a value of type `T` without initialization.
19
For a bitstype `T`, the value will be whatever currently resides in the memory
20
allocated. For a non-bitstype `T`, the reference will be undefined and attempting to
21
dereference it will result in an error, "UndefRefError: access to undefined reference".
22

23
To check if a `Ref` is an undefined reference, use [`isassigned(ref::RefValue)`](@ref).
24
For example, `isassigned(Ref{T}())` is `false` if `T` is not a bitstype.
25
If `T` is a bitstype, `isassigned(Ref{T}())` will always be true.
26

27
When passed as a `ccall` argument (either as a `Ptr` or `Ref` type), a `Ref`
28
object will be converted to a native pointer to the data it references.
29
For most `T`, or when converted to a `Ptr{Cvoid}`, this is a pointer to the
30
object data. When `T` is an `isbits` type, this value may be safely mutated,
31
otherwise mutation is strictly undefined behavior.
32

33
As a special case, setting `T = Any` will instead cause the creation of a
34
pointer to the reference itself when converted to a `Ptr{Any}`
35
(a `jl_value_t const* const*` if T is immutable, else a `jl_value_t *const *`).
36
When converted to a `Ptr{Cvoid}`, it will still return a pointer to the data
37
region as for any other `T`.
38

39
A `C_NULL` instance of `Ptr` can be passed to a `ccall` `Ref` argument to initialize it.
40

41
# Use in broadcasting
42
`Ref` is sometimes used in broadcasting in order to treat the referenced values as a scalar.
43

44
# Examples
45

46
```jldoctest
47
julia> r = Ref(5) # Create a Ref with an initial value
48
Base.RefValue{Int64}(5)
49

50
julia> r[] # Getting a value from a Ref
51
5
52

53
julia> r[] = 7 # Storing a new value in a Ref
54
7
55

56
julia> r # The Ref now contains 7
57
Base.RefValue{Int64}(7)
58

59
julia> isa.(Ref([1,2,3]), [Array, Dict, Int]) # Treat reference values as scalar during broadcasting
60
3-element BitVector:
61
 1
62
 0
63
 0
64

65
julia> Ref{Function}()  # Undefined reference to a non-bitstype, Function
66
Base.RefValue{Function}(#undef)
67

68
julia> try
69
           Ref{Function}()[] # Dereferencing an undefined reference will result in an error
70
       catch e
71
           println(e)
72
       end
73
UndefRefError()
74

75
julia> Ref{Int64}()[]; # A reference to a bitstype refers to an undetermined value if not given
76

77
julia> isassigned(Ref{Int64}()) # A reference to a bitstype is always assigned
78
true
79
```
80
"""
81
Ref
82

83
# C NUL-terminated string pointers; these can be used in ccall
84
# instead of Ptr{Cchar} and Ptr{Cwchar_t}, respectively, to enforce
85
# a check for embedded NUL chars in the string (to avoid silent truncation).
86
if Int === Int64
87
    primitive type Cstring  64 end
88
    primitive type Cwstring 64 end
89
else
90
    primitive type Cstring  32 end
91
    primitive type Cwstring 32 end
92
end
93

94

95
### General Methods for Ref{T} type
96

97
eltype(x::Type{<:Ref{T}}) where {T} = @isdefined(T) ? T : Any
×
98
convert(::Type{Ref{T}}, x::Ref{T}) where {T} = x
×
99
size(x::Ref) = ()
×
100
axes(x::Ref) = ()
×
101
length(x::Ref) = 1
×
102
isempty(x::Ref) = false
×
103
ndims(x::Ref) = 0
×
104
ndims(::Type{<:Ref}) = 0
×
105
iterate(r::Ref) = (r[], nothing)
×
106
iterate(r::Ref, s) = nothing
×
107
IteratorSize(::Type{<:Ref}) = HasShape{0}()
×
108

109
# create Ref objects for general object conversion
110
unsafe_convert(::Type{Ref{T}}, x::Ref{T}) where {T} = unsafe_convert(Ptr{T}, x)
1,657✔
111
unsafe_convert(::Type{Ref{T}}, x) where {T} = unsafe_convert(Ptr{T}, x)
×
112

113
convert(::Type{Ref{T}}, x) where {T} = RefValue{T}(x)::RefValue{T}
6✔
114

115
### Methods for a Ref object that is backed by an array at index i
116
struct RefArray{T,A<:AbstractArray{T},R} <: Ref{T}
117
    x::A
118
    i::Int
119
    roots::R # should be either ::Nothing or ::Any
120
    RefArray{T,A,R}(x,i,roots=nothing) where {T,A<:AbstractArray{T},R} = new(x,i,roots)
×
121
end
122
RefArray(x::AbstractArray{T}, i::Int, roots::Any) where {T} = RefArray{T,typeof(x),Any}(x, i, roots)
×
123
RefArray(x::AbstractArray{T}, i::Int=1, roots::Nothing=nothing) where {T} = RefArray{T,typeof(x),Nothing}(x, i, nothing)
×
124
RefArray(x::AbstractArray{T}, i::Integer, roots::Any) where {T} = RefArray{T,typeof(x),Any}(x, Int(i), roots)
×
125
RefArray(x::AbstractArray{T}, i::Integer, roots::Nothing=nothing) where {T} = RefArray{T,typeof(x),Nothing}(x, Int(i), nothing)
×
126
convert(::Type{Ref{T}}, x::AbstractArray{T}) where {T} = RefArray(x, 1)
×
127

128
function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefArray{T})::P where T
129
    if allocatedinline(T)
×
130
        p = pointer(b.x, b.i)
×
131
    elseif isconcretetype(T) && ismutabletype(T)
×
132
        p = pointer_from_objref(b.x[b.i])
×
133
    else
134
        # see comment on equivalent branch for RefValue
135
        p = pointerref(Ptr{Ptr{Cvoid}}(pointer(b.x, b.i)), 1, Core.sizeof(Ptr{Cvoid}))
×
136
    end
137
    return p
×
138
end
139
function unsafe_convert(::Type{Ptr{Any}}, b::RefArray{Any})::Ptr{Any}
×
140
    return pointer(b.x, b.i)
×
141
end
142

143
###
144
if is_primary_base_module
145
    Ref(x::Any) = RefValue(x)
142✔
146
    Ref{T}() where {T} = RefValue{T}() # Ref{T}()
19,629✔
147
    Ref{T}(x) where {T} = RefValue{T}(x) # Ref{T}(x)
1,489✔
148

149
    Ref(x::Ref, i::Integer) = (i != 1 && error("Ref only has one element"); x)
×
150
    Ref(x::Ptr{T}, i::Integer) where {T} = x + (i - 1) * Core.sizeof(T)
×
151

152
    # convert Arrays to pointer arrays for ccall
153
    # For example `["a", "b"]` to Ptr{Cstring} for `char **argv`
154
    function Ref{P}(a::Array{T}) where P<:Union{Ptr,Cwstring,Cstring} where T
×
155
        if P == T
×
156
            return getfield(a, :ref)
×
157
        elseif (isbitstype(T) ? T <: Ptr || T <: Union{Cwstring,Cstring} : T <: eltype(P))
×
158
            # this Array already has the right memory layout for the requested Ref
159
            # but the wrong eltype for the constructor
160
            return RefArray{P,typeof(a),Nothing}(a, 1, nothing) # effectively a no-op
×
161
        else
162
            ptrs = Vector{P}(undef, length(a)+1)
×
163
            roots = Vector{Any}(undef, length(a))
×
164
            for i = 1:length(a)
×
165
                root = cconvert(P, a[i])
×
166
                ptrs[i] = unsafe_convert(P, root)::P
×
167
                roots[i] = root
×
168
            end
×
169
            ptrs[length(a)+1] = C_NULL
×
170
            return RefArray{P,typeof(ptrs),typeof(roots)}(ptrs, 1, roots)
×
171
        end
172
    end
173
    Ref(x::AbstractArray, i::Integer) = RefArray(x, i)
×
174
end
175

176
cconvert(::Type{Ptr{P}}, a::Array{<:Union{Ptr,Cwstring,Cstring}}) where {P<:Union{Ptr,Cwstring,Cstring}} = getfield(a, :ref)
×
177
cconvert(::Type{Ref{P}}, a::Array{<:Union{Ptr,Cwstring,Cstring}}) where {P<:Union{Ptr,Cwstring,Cstring}} = getfield(a, :ref)
×
178
cconvert(::Type{Ptr{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a)
×
179
cconvert(::Type{Ref{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a)
×
180

181
# pass NTuple{N,T} as Ptr{T}/Ref{T}
182
cconvert(::Type{Ref{T}}, t::NTuple{N,T}) where {N,T} = Ref{NTuple{N,T}}(t)
×
183
cconvert(::Type{Ref{T}}, r::Ref{NTuple{N,T}}) where {N,T} = r
×
184
unsafe_convert(::Type{Ref{T}}, r::Ref{NTuple{N,T}}) where {N,T} =
×
185
    convert(Ptr{T}, unsafe_convert(Ptr{NTuple{N,T}}, r))
186
unsafe_convert(::Type{Ptr{T}}, r::Ref{NTuple{N,T}}) where {N,T} =
×
187
    convert(Ptr{T}, unsafe_convert(Ptr{NTuple{N,T}}, r))
188
unsafe_convert(::Type{Ptr{T}}, r::Ptr{NTuple{N,T}}) where {N,T} =
×
189
    convert(Ptr{T}, r)
190

191
###
192

193
getindex(b::RefArray) = b.x[b.i]
×
194
setindex!(b::RefArray, x) = (b.x[b.i] = x; b)
×
195

196
###
197

198
"""
199
    LLVMPtr{T, AS}
200

201
A pointer type that more closely resembles LLVM semantics: It includes the pointer address
202
space, and will be passed as an actual pointer instead of an integer.
203

204
This type is mainly used to interface with code that has strict requirements about pointers,
205
e.g., intrinsics that are selected based on the address space, or back-ends that require
206
pointers to be identifiable by their types.
207
"""
208
Core.LLVMPtr
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc