|
| 1 | +struct WalkCache{K, V, W <: AbstractWalk, C <: AbstractDict{K, V}} <: AbstractDict{K, V} |
| 2 | + walk::W |
| 3 | + cache::C |
| 4 | + WalkCache(walk, cache::AbstractDict{K, V} = IdDict()) where {K, V} = new{K, V, typeof(walk), typeof(cache)}(walk, cache) |
| 5 | +end |
| 6 | +Base.length(cache::WalkCache) = length(cache.cache) |
| 7 | +Base.empty!(cache::WalkCache) = empty!(cache.cache) |
| 8 | +Base.haskey(cache::WalkCache, x) = haskey(cache.cache, x) |
| 9 | +Base.get(cache::WalkCache, x, default) = haskey(cache.cache, x) ? cache[x] : default |
| 10 | +Base.iterate(cache::WalkCache, state...) = iterate(cache.cache, state...) |
| 11 | +Base.setindex!(cache::WalkCache, value, key) = setindex!(cache.cache, value, key) |
| 12 | +Base.getindex(cache::WalkCache, x) = cache.cache[x] |
| 13 | + |
| 14 | +@static if VERSION >= v"1.10.0-DEV.609" |
| 15 | + function __cacheget_generator__(world, source, self, cache, x, args #= for `return_type` only =#) |
| 16 | + # :(return cache.cache[x]::(return_type(cache.walk, typeof(args)))) |
| 17 | + walk = cache.parameters[3] |
| 18 | + RT = Core.Compiler.return_type(Tuple{walk, args...}, world) |
| 19 | + body = Expr(:call, GlobalRef(Base, :getindex), Expr(:., :cache, QuoteNode(:cache)), :x) |
| 20 | + if RT != Any |
| 21 | + body = Expr(:(::), body, RT) |
| 22 | + end |
| 23 | + expr = Expr(:lambda, [Symbol("#self#"), :cache, :x, :args], |
| 24 | + Expr(Symbol("scope-block"), Expr(:block, Expr(:meta, :inline), Expr(:return, body)))) |
| 25 | + ci = ccall(:jl_expand, Any, (Any, Any), expr, @__MODULE__) |
| 26 | + ci.inlineable = true |
| 27 | + return ci |
| 28 | + end |
| 29 | + @eval function cacheget(cache::WalkCache, x, args...) |
| 30 | + $(Expr(:meta, :generated, __cacheget_generator__)) |
| 31 | + $(Expr(:meta, :generated_only)) |
| 32 | + end |
| 33 | +else |
| 34 | + @generated function cacheget(cache::WalkCache, x, args...) |
| 35 | + walk = cache.parameters[3] |
| 36 | + world = typemax(UInt) |
| 37 | + @static if VERSION >= v"1.8" |
| 38 | + RT = Core.Compiler.return_type(Tuple{walk, args...}, world) |
| 39 | + else |
| 40 | + if isdefined(walk, :instance) |
| 41 | + RT = Core.Compiler.return_type(walk.instance, Tuple{args...}, world) |
| 42 | + else |
| 43 | + RT = Any |
| 44 | + end |
| 45 | + end |
| 46 | + body = Expr(:call, GlobalRef(Base, :getindex), Expr(:., :cache, QuoteNode(:cache)), :x) |
| 47 | + if RT != Any |
| 48 | + body = Expr(:(::), body, RT) |
| 49 | + end |
| 50 | + expr = Expr(:lambda, [Symbol("#self#"), :cache, :x, :args], |
| 51 | + Expr(Symbol("scope-block"), Expr(:block, Expr(:meta, :inline), Expr(:return, body)))) |
| 52 | + ci = ccall(:jl_expand, Any, (Any, Any), expr, @__MODULE__) |
| 53 | + ci.inlineable = true |
| 54 | + return ci |
| 55 | + end |
| 56 | +end |
| 57 | +# fallback behavior that only lookup for `x` |
| 58 | +@inline cacheget(cache::AbstractDict, x, args...) = cache[x] |
0 commit comments