Skip to content

Commit 1ac5160

Browse files
committed
Fix method error generation.
1 parent 18cc0d4 commit 1ac5160

File tree

2 files changed

+37
-31
lines changed

2 files changed

+37
-31
lines changed

src/cache.jl

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,31 @@ using Base: _methods_by_ftype
99

1010

1111
"""
12-
get_world(f, tt)
12+
get_world(ft, tt)
1313
1414
A special function that returns the world age in which the current definition of function
15-
`f`, invoked with argument types `tt`, is defined. This can be used to cache compilation
16-
results:
15+
type `ft`, invoked with argument types `tt`, is defined. This can be used to cache
16+
compilation results:
1717
1818
compilation_cache = Dict()
19-
function cache_compilation(f, tt)
20-
world = get_world(f, tt)
21-
get!(compilation_cache, (f, tt, world)) do
19+
function cache_compilation(ft, tt)
20+
world = get_world(ft, tt)
21+
get!(compilation_cache, (ft, tt, world)) do
2222
# compile
2323
end
2424
end
2525
2626
What makes this function special is that it is a generated function, returning a constant,
27-
whose result is automatically invalidated when the function `f` (or any called function)
28-
is redefined. This makes this query ideally suited for hot code, where you want to avoid
29-
a costly look-up of the current world age on every invocation.
27+
whose result is automatically invalidated when the function `ft` (or any called function) is
28+
redefined. This makes this query ideally suited for hot code, where you want to avoid a
29+
costly look-up of the current world age on every invocation.
30+
31+
Normally, you shouldn't have to use this function, as it's used by `FunctionSpec`.
3032
3133
!!! warning
3234
3335
Due to a bug in Julia, JuliaLang/julia#34962, this function's results are only
34-
guaranteed to be correctly invalidated when the target function `f` is executed or
36+
guaranteed to be correctly invalidated when the target function `ft` is executed or
3537
processed by codegen (e.g., by calling `code_llvm`).
3638
"""
3739
get_world
@@ -41,15 +43,15 @@ if VERSION >= v"1.10.0-DEV.649"
4143
# on 1.10 (JuliaLang/julia#48611) the generated function knows which world it was invoked in
4244

4345
function _generated_ex(world, source, ex)
44-
stub = Core.GeneratedFunctionStub(identity, Core.svec(:get_world, :job), Core.svec())
46+
stub = Core.GeneratedFunctionStub(identity, Core.svec(:get_world, :ft, :tt), Core.svec())
4547
stub(world, source, ex)
4648
end
4749

48-
function get_world_generator(world::UInt, source, self, ::Type{Type{f}}, ::Type{Type{tt}}) where {f, tt}
50+
function get_world_generator(world::UInt, source, self, ::Type{Type{ft}}, ::Type{Type{tt}}) where {ft, tt}
4951
@nospecialize
5052

5153
# look up the method
52-
sig = Tuple{f, tt.parameters...}
54+
sig = Tuple{ft, tt.parameters...}
5355
min_world = Ref{UInt}(typemin(UInt))
5456
max_world = Ref{UInt}(typemax(UInt))
5557
has_ambig = Ptr{Int32}(C_NULL) # don't care about ambiguous results
@@ -65,7 +67,7 @@ function get_world_generator(world::UInt, source, self, ::Type{Type{f}}, ::Type{
6567
end
6668

6769
# check the validity of the method matches
68-
method_error = :(throw(MethodError(f, tt, $world)))
70+
method_error = :(throw(MethodError(ft, tt, $world)))
6971
mthds === nothing && return _generated_ex(world, source, method_error)
7072
Base.isdispatchtuple(tt) || return _generated_ex(world, source, :(error("$tt is not a dispatch tuple")))
7173
length(mthds) == 1 || return _generated_ex(world, source, method_error)
@@ -91,7 +93,7 @@ function get_world_generator(world::UInt, source, self, ::Type{Type{f}}, ::Type{
9193
# underlying C methods -- which GPUCompiler does, so everything Just Works.
9294

9395
# prepare the slots
94-
new_ci.slotnames = Symbol[Symbol("#self#"), :f, :tt]
96+
new_ci.slotnames = Symbol[Symbol("#self#"), :ft, :tt]
9597
new_ci.slotflags = UInt8[0x00 for i = 1:3]
9698

9799
# return the world
@@ -108,7 +110,7 @@ function get_world_generator(world::UInt, source, self, ::Type{Type{f}}, ::Type{
108110
return new_ci
109111
end
110112

111-
@eval function get_world(f, tt)
113+
@eval function get_world(ft, tt)
112114
$(Expr(:meta, :generated_only))
113115
$(Expr(:meta, :generated, get_world_generator))
114116
end
@@ -118,11 +120,11 @@ else
118120
# on older versions of Julia we fall back to looking up the current world. this may be wrong
119121
# when the generator is invoked in a different world (TODO: when does this happen?)
120122

121-
function get_world_generator(self, ::Type{Type{f}}, ::Type{Type{tt}}) where {f, tt}
123+
function get_world_generator(self, ::Type{Type{ft}}, ::Type{Type{tt}}) where {ft, tt}
122124
@nospecialize
123125

124126
# look up the method
125-
sig = Tuple{f, tt.parameters...}
127+
sig = Tuple{ft, tt.parameters...}
126128
min_world = Ref{UInt}(typemin(UInt))
127129
max_world = Ref{UInt}(typemax(UInt))
128130
has_ambig = Ptr{Int32}(C_NULL) # don't care about ambiguous results
@@ -139,7 +141,7 @@ function get_world_generator(self, ::Type{Type{f}}, ::Type{Type{tt}}) where {f,
139141
# XXX: using world=-1 is wrong, but the current world isn't exposed to this generator
140142

141143
# check the validity of the method matches
142-
method_error = :(throw(MethodError(f, tt)))
144+
method_error = :(throw(MethodError(ft, tt)))
143145
mthds === nothing && return method_error
144146
Base.isdispatchtuple(tt) || return(:(error("$tt is not a dispatch tuple")))
145147
length(mthds) == 1 || return method_error
@@ -170,7 +172,7 @@ function get_world_generator(self, ::Type{Type{f}}, ::Type{Type{tt}}) where {f,
170172
# underlying C methods -- which GPUCompiler does, so everything Just Works.
171173

172174
# prepare the slots
173-
new_ci.slotnames = Symbol[Symbol("#self#"), :f, :tt]
175+
new_ci.slotnames = Symbol[Symbol("#self#"), :ft, :tt]
174176
new_ci.slotflags = UInt8[0x00 for i = 1:3]
175177

176178
# return the world
@@ -187,14 +189,14 @@ function get_world_generator(self, ::Type{Type{f}}, ::Type{Type{tt}}) where {f,
187189
return new_ci
188190
end
189191

190-
@eval function get_world(f, tt)
192+
@eval function get_world(ft, tt)
191193
$(Expr(:meta, :generated_only))
192194
$(Expr(:meta,
193195
:generated,
194196
Expr(:new,
195197
Core.GeneratedFunctionStub,
196198
:get_world_generator,
197-
Any[:get_world, :f, :tt],
199+
Any[:get_world, :ft, :tt],
198200
Any[],
199201
@__LINE__,
200202
QuoteNode(Symbol(@__FILE__)),

src/validation.jl

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ function return_type(m::Core.MethodMatch; interp::AbstractInterpreter)
1818
return something(ty, Any)
1919
end
2020

21+
# create a MethodError from a function type
22+
# TODO: fix upstream
23+
function MethodError(ft::Type, tt::Type, world::Integer=typemax(UInt))
24+
f = if isdefined(ft, :instance)
25+
ft.instance
26+
else
27+
# HACK: dealing with a closure or something... let's do somthing really invalid,
28+
# which works because MethodError doesn't actually use the function
29+
Ref{ft}()[]
30+
end
31+
Base.MethodError(f, tt, world)
32+
end
2133

2234
function check_method(@nospecialize(job::CompilerJob))
2335
isa(job.source.ft, Core.Builtin) &&
@@ -26,15 +38,7 @@ function check_method(@nospecialize(job::CompilerJob))
2638
# get the method
2739
ms = method_matches(typed_signature(job); job.source.world)
2840
if length(ms) != 1
29-
# we only have a function type, but MethodError needs an instance...
30-
f = if isdefined(job.source.ft, :instance)
31-
job.source.ft.instance
32-
else
33-
# HACK: dealing with a closure or something... let's do somthing really invalid,
34-
# which works because MethodError doesn't actually use the function
35-
Ref{job.source.ft}()[]
36-
end
37-
throw(MethodError(f, job.source.tt, job.source.world))
41+
throw(MethodError(job.source.ft, job.source.tt, job.source.world))
3842
end
3943

4044
# kernels can't return values

0 commit comments

Comments
 (0)