In Julia 1.12 it is now possible to edit structs (finally!!), with the objects instantiated using the “old” structs “assigned” to a world age table rather than the last “version” of the type.
The issue is when you try to use them according to the latest version of the type they belong:
mutable struct Point
x::Float64
y::Float64
z::Int64 # Added later
end
a = Point(1.0, 2.0) # Point(1.0, 2.0)
# added field z to Point..
b = Point(1.0, 2.0, 3) # Point(1.0, 2.0, 3)
a == b # false
bt = typeof(b) # Point
at = typeof(a) # @world(Point,38522:38525)
at == bt # false
b.z # 3
a.z # ERROR: FieldError: type Point has no field `z`, available fields: `x`, `y`
I can anticipate this will be a common issue for many
Wouldn’t then be better to look explicitly if the type of the object is in an “outdated” world?
option 1 (I guess less computationally expensive): just replace Point with @world(Point,38522:38525) in the error message
option 2: check if the type is in a different world age than the latest one and add to the message that the field/method may be available in a later world age
option 3: check if it is the case (field is available only on a later age) and advise user
(1) agreed, it’s replaced in many other printouts so why not that one
(2, 3) Not helpful generally because you may not have a type assigned to const Point in the current world age, let alone a type with the property. Obsolete evaluations (instantiations, calls, annotations) are just a risk of interactivity, the same way we run into weird and sometimes silent behavior after caching results of invalidated methods.
Here’s the full error message since it wasn’t shown in the OP:
julia> mutable struct Point
x::Float64
y::Float64
end
julia> a = Point(1.0, 2.0);
julia> mutable struct Point
x::Float64
y::Float64
z::Int64
end
julia> a.z
ERROR: FieldError: type Point has no field `z`, available fields: `x`, `y`
Stacktrace:
[1] getproperty(x::@world(Point, 38777:38780), f::Symbol)
@ Base ./Base_compiler.jl:57
[2] top-level scope
@ REPL[4]:1
Notice that it does say in the stacktrace that this was caused by getproperty(x::@world(Point, 38777:38780), f::Symbol). Personally, I find this mostly clear, but I guess that’s because I’m used to reading stacktraces.
I’ve opened a PR that would implement Option 1, but I’m a bit conflicted on whether or not this actually makes things clearer or not. Open to thoughts / suggestions.
I prefer it to be upfront in the type’s printed name because the plain name Point seems to be consistently referring to the newest world age otherwise, and it doesn’t seem to be worth the risk of getting the wrong idea at reading the first few lines. Also, it’s possible for the call’s input types to be omitted by inlining or maybe some other reason. Note that the @inline in the following example was unnecessary, this problem shows up with automatic inlining too:
julia> mutable struct Point # v1.11.5
x::Float64
y::Float64
end
julia> e() = @inline Point(1,2).z
e (generic function with 1 method)
julia> e()
ERROR: type Point has no field z
Stacktrace:
[1] getproperty
@ .\Base.jl:49 [inlined]
[2] e()
@ Main .\REPL[50]:1
[3] top-level scope
@ REPL[53]:1
julia> f() = @noinline Point(1,2).z
f (generic function with 1 method)
julia> f()
ERROR: type Point has no field z
Stacktrace:
[1] getproperty(x::Point, f::Symbol)
@ Base .\Base.jl:49
[2] f()
@ Main .\REPL[54]:1
[3] top-level scope
@ REPL[55]:1