Skip to content

Commit ec21524

Browse files
committed
clarity and doc improvements
1 parent 00ca045 commit ec21524

File tree

1 file changed

+40
-13
lines changed

1 file changed

+40
-13
lines changed

src/Traitor.jl

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export @traitor, supertrait, betray!
6363
using Cassette
6464

6565
# A context for doing nothing
66-
Cassette.@context RoundTrip
66+
Cassette.@context DoNothingCtx
6767

6868

6969
"""
@@ -124,6 +124,9 @@ is returns their common supertype (or else throws an error).
124124
end
125125
(Tb isa Union) || break
126126
end
127+
if traitclass == Any
128+
error("$T does not have a supertrait.")
129+
end
127130
return traitclass
128131
end
129132
supertrait(t) = error("Unknown trait $t")
@@ -190,6 +193,11 @@ if necessary, the first time it is called) the table joining the traits to
190193
the submethods
191194
"""
192195
@generated function get_trait_table(f::Function, ::Type{Signature}) where {Signature <: Tuple}
196+
# This is a questionable usage of a generated function. The idea is that it's an 'elegant' way
197+
# to create an independant global dictionary for each method specification which can later be mutated.
198+
# The problem is that if this generated function gets recompiled for some reason (which the compiler
199+
# is allowed to do!) then the dictionary will be replaced with a new empty dict, deleting our trait
200+
# table.
193201
d = Dict{Any, Function}()
194202
return quote
195203
$(Expr(:meta, :inline))
@@ -267,25 +275,24 @@ macro traitor(ex)
267275

268276
dispatchname = gensym(Symbol(funcname, :_dispatched))
269277

270-
# Traitor.trait_dispatch,Tuple{Base.Dict{Any, Function}, Type{Tuple{Int64}}}
271-
# Traitor.supertrait, Tuple{Type{Main.Small}}
272-
273278
ex = quote
274279
$Traitor.@generated function $funcname($(args...))
275280
dict = Traitor.get_trait_table($funcname, $(Expr(:curly, :Tuple, argtypes...)))
276281
thunk = () -> Traitor.trait_dispatch(dict, $(Expr(:curly, :Tuple, argnames...)))
277282

278-
# This cassette overdub pass literally does nothing. I can't figure out why this
279-
# doesn't work without it
280-
$dispatchname = $Cassette.overdub($RoundTrip(), thunk)
283+
# This cassette overdub pass literally does nothing other than normal evaluation.
284+
# However, if I don't use it, the backedge attachment later on doesn't appear
285+
# to have the desired effect.
286+
$dispatchname = $Cassette.overdub($DoNothingCtx(), thunk)
281287
#$dispatchname = thunk()
282288
ex = (Expr(:call, $dispatchname, $(QuoteNode.(argnames)...)))
283-
289+
284290
# Create a codeinfo
285291
ci = $expr_to_codeinfo($(__module__), [Symbol("#self#"), $(quotednames...)], [], (), ex)
286292

287-
#Make it so that anything that'd cause the trait_dispatch function to recompile, also
288-
#causes this function to recompile
293+
# Attached edges from MethodInstrances of the `supertrait` function to to this CodeInfo.
294+
# This should make it so that adding members to a trait relevant to this function
295+
# triggers recompilation, fixing the #265 equivalent for trait methods.
289296
ci.edges = $Core.MethodInstance[]
290297
for TT in [$(traits...)]
291298
for T in TT.parameters
@@ -305,8 +312,29 @@ macro traitor(ex)
305312
esc(ex)
306313
end
307314

315+
"""
316+
expr_to_codeinfo(m::Module, argnames, spnames, sp, e::Expr)
317+
318+
Take an expr (usually a generated function generator) and convert it into a CodeInfo object
319+
(Julia's internal, linear representation of code).
320+
321+
`m` is the module that the CodeInfo should be generated from (used for name resolution)
308322
309-
function expr_to_codeinfo(m, argnames, spnames, sp, e)
323+
`argnames` must be an iterable container of symbols describing the CodeInfo's input arguments.
324+
NOTE: the first argument should be given as `Symbol("#self#")`. So if the function is `f(x) = x + 1`,
325+
then `argnames = [Symbol("#self#"), :x]`
326+
327+
`spnames` should be an iterable container of the names of the static parameters to the CodeInfo body
328+
(e.g.) in `f(x::T) where {T <: Int} = ...`, `T` is a static parameter, so `spnames` should be `[:T]`
329+
330+
`sp` should be an iterable container of the static parameters to the CodeInfo body themselves (as
331+
opposed to their names) (e.g.) in `f(x::T) where {T <: Int} = ...`, `T` is a static parameter,
332+
so `sp` should be `[T]`
333+
334+
`e` is the actual expression to lower to CodeInfo. This must be 'pure' in the same sense as generated
335+
function bodies.
336+
"""
337+
function expr_to_codeinfo(m::Module, argnames, spnames, sp, e::Expr)
310338
lam = Expr(:lambda, argnames,
311339
Expr(Symbol("scope-block"),
312340
Expr(:block,
@@ -323,12 +351,11 @@ function expr_to_codeinfo(m, argnames, spnames, sp, e)
323351
# Get the code-info for the generatorbody in order to use it for generating a dummy
324352
# code info object.
325353
ci = ccall(:jl_expand_and_resolve, Any, (Any, Any, Core.SimpleVector), ex, m, Core.svec(sp...))
326-
@assert ci isa Core.CodeInfo "Failed to compile @staged function. This might mean it contains a closure or comprehension?"
354+
@assert ci isa Core.CodeInfo "Failed to create a CodeInfo from the given expression. This might mean it contains a closure or comprehension?\n Offending expression: $e"
327355
ci
328356
end
329357

330358

331-
332359
"""
333360
This function deterimines the dispatch on the traits. Each instance of
334361
this generated function is specialized on the (standard) signature of the

0 commit comments

Comments
 (0)