Skip to content

Commit ba7c4f4

Browse files
authored
fix world age errors for AbstractBindingOverlay on v1.12 (#66)
Starting from v1.12.1, the implementation of `isconst/getglobal` became more accurate, and when called in a generator's world that was defined before the target method table, it now correctly determines that the method table is not yet defined. So, from v1.12 onwards, it seems necessary to use functions like `[getglobal|isconst]_at_world` as implemented here. This might be something that should be provided by Base itself though.
1 parent 10e5e03 commit ba7c4f4

File tree

1 file changed

+37
-7
lines changed

1 file changed

+37
-7
lines changed

src/CassetteOverlay.jl

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function nonoverlay end
1515

1616
@nospecialize
1717
cassette_overlay_error() = error("CassetteOverlay is available via `@overlaypass` macro")
18-
methodtable(::Type{<:OverlayPass}) = cassette_overlay_error()
18+
methodtable(::UInt, ::Type{<:OverlayPass}) = cassette_overlay_error()
1919
getpass(args...; kwargs...) = cassette_overlay_error()
2020
nonoverlay(args...; kwargs...) = cassette_overlay_error()
2121
@specialize
@@ -54,7 +54,14 @@ function generate_overlay_src(world::UInt, source::SourceType, passtype, fargtyp
5454
selfname::Symbol, fargsname::Symbol)
5555
@nospecialize passtype fargtypes
5656
tt = Base.to_tuple_type(fargtypes)
57-
match = Base._which(tt; method_table=methodtable(passtype), raise=false, world)
57+
mt_worlds = methodtable(world, passtype)
58+
if mt_worlds isa Pair
59+
method_table, worlds = mt_worlds
60+
else
61+
method_table = mt_worlds
62+
worlds = nothing
63+
end
64+
match = Base._which(tt; method_table, raise=false, world)
5865
match === nothing && return nothing # method match failed – the fallback implementation will raise a proper MethodError
5966
mi = Core.Compiler.specialize_method(match)
6067
src = Core.Compiler.retrieve_code_info(mi, world)
@@ -68,6 +75,9 @@ function generate_overlay_src(world::UInt, source::SourceType, passtype, fargtyp
6875
push!(invalid_code, (world, source, passtype, fargtypes, src, selfname, fargsname))
6976
# TODO `return nothing` when updating the minimum compat to 1.12
7077
end
78+
if worlds !== nothing
79+
src.min_world, src.max_world = max(src.min_world, first(worlds)), min(src.max_world, last(worlds))
80+
end
7181
return src
7282
end
7383

@@ -92,7 +102,7 @@ macro overlaypass(args...)
92102
nonoverlaytype = typeof(CassetteOverlay.nonoverlay)
93103

94104
if method_table !== :nothing
95-
mthd_tbl = :($CassetteOverlay.methodtable(::Type{$PassName}) = $(esc(method_table)))
105+
mthd_tbl = :($CassetteOverlay.methodtable(::UInt, ::Type{$PassName}) = $(esc(method_table)))
96106
else
97107
mthd_tbl = nothing
98108
end
@@ -153,14 +163,34 @@ macro overlaypass(args...)
153163
end
154164
end
155165

166+
function isconst_at_world(m::Module, var::Symbol, world::UInt)
167+
bpart = Base.lookup_binding_partition(world, GlobalRef(m, var))
168+
kind = Base.binding_kind(bpart)
169+
return Base.is_defined_const_binding(kind)
170+
end
171+
172+
function getglobal_at_world(m::Module, var::Symbol, world::UInt)
173+
b = @ccall jl_get_binding(m::Any, var::Any)::Any
174+
bp = Base.lookup_binding_partition(world, b)
175+
val_ptr = @ccall jl_get_binding_value_in_world(b::Any, world::Csize_t)::Ptr{Any}
176+
if val_ptr == C_NULL
177+
throw(ccall(:jl_new_struct, Any, (Any, Any...), UndefVarError, var, world, m))
178+
end
179+
return Pair{Any,UnitRange{UInt}}(unsafe_pointer_to_objref(val_ptr), bp.min_world:bp.max_world)
180+
end
181+
156182
abstract type AbstractBindingOverlay{M, S} <: OverlayPass; end
157-
function methodtable(::Type{<:AbstractBindingOverlay{M, S}}) where {M, S}
183+
function methodtable(world::UInt, ::Type{<:AbstractBindingOverlay{M, S}}) where {M, S}
158184
if M === nothing
159185
return nothing
160186
end
161-
@assert @invokelatest isconst(M, S)
162-
mt = @invokelatest getglobal(M, S)
163-
return mt::MethodTable
187+
@static if VERSION v"1.12-"
188+
@assert isconst_at_world(M, S, world)
189+
return getglobal_at_world(M, S, world)
190+
else
191+
@assert @invokelatest isconst(M, S)
192+
return getglobal(M, S)::MethodTable
193+
end
164194
end
165195
@overlaypass AbstractBindingOverlay nothing
166196

0 commit comments

Comments
 (0)