11using MethodAnalysis
22
3-
43"""
5- atrisktyp (tt)
4+ is_atrisk_type (tt)
65
76Given a Tuple-type signature (e.g., `Tuple{typeof(sum),Vector{Int}}`), determine whether this signature
87is "at risk" for invalidation. Essentially it returns `true` if one or more arguments are of abstract type,
98although there are prominent exceptions:
109
11- - `Function` is allowed
12- - any constructor call is allowed
13- - `convert(X, x)` where `isa(x, X)` is true
14- - `setindex!` and `push!` methods where the valtype is a subtype of the eltype (likewise keytype for AbstractDicts)
10+ - Constructor calls with arbitrary argument types
11+ - `convert(X, x)` where `isa(x, X)`
12+ - `setindex!` and `push!` methods where the valtype is a subtype of the eltype (for AbstractDicts, likewise for the keytype)
1513- `getindex`, `length`, `isempty`, and `iterate` on any tuple
14+
15+ All of these are "allowed," meaning that they return `false`.
16+ Moreover, some specific non-concrete argument types---like `Union`s of concrete types and `Function`---
17+ do not trigger a return of `true`, although other at-risk argument types can lead to an overall `true` return
18+ for the signature.
1619"""
17- function atrisktype (@nospecialize (typ))
20+ function is_atrisk_type (@nospecialize (typ))
1821 # signatures like `convert(Vector, a)`, `foo(::Vararg{Synbol,N}) where N` do not seem to pose a problem
1922 isa (typ, TypeVar) && return false
2023 # isbits parameters are not a problem
@@ -24,7 +27,7 @@ function atrisktype(@nospecialize(typ))
2427 end
2528 # Exclude signatures with Union{}
2629 typ === Union{} && return false
27- isa (typ, Union) && return atrisktype (typ. a) | atrisktype (typ. b)
30+ isa (typ, Union) && return is_atrisk_type (typ. a) | is_atrisk_type (typ. b)
2831 # Type{T}: signatures like `convert(::Type{AbstractString}, ::String)` are not problematic
2932 typ <: Type && return false
3033 if typ <: Tuple && length (typ. parameters) >= 1
@@ -38,6 +41,9 @@ function atrisktype(@nospecialize(typ))
3841 p2 = Base. unwrap_unionall (p2)
3942 if isa (p2, DataType) && length (p2. parameters) === 1
4043 T = p2. parameters[1 ]
44+ if isa (T, TypeVar)
45+ T = T. ub
46+ end
4147 isa (p3, Type) && isa (T, Type) && p3 <: T && return false
4248 end
4349 end
@@ -53,9 +59,9 @@ function atrisktype(@nospecialize(typ))
5359 end
5460 # show(io::IO, x) is OK as long as typeof(x) is safe
5561 elseif p1 === typeof (Base. show) || p1 === typeof (Base. print) || p1 === typeof (Base. println)
56- # atrisktype (typ.parameters[2]) && return true
62+ # is_atrisk_type (typ.parameters[2]) && return true
5763 for i = 3 : length (typ. parameters)
58- atrisktype (typ. parameters[i]) && return true
64+ is_atrisk_type (typ. parameters[i]) && return true
5965 end
6066 return false
6167 # setindex!(a, x, idx) and push!(a, x) are safe if typeof(x) <: eltype(a)
@@ -75,30 +81,46 @@ function atrisktype(@nospecialize(typ))
7581 isconcretetype (typ) && return false
7682 # ::Function args are excluded
7783 typ === Function && return false
78- ! isempty (typ. parameters) && (any (atrisktype , typ. parameters) || return false )
84+ ! isempty (typ. parameters) && (any (is_atrisk_type , typ. parameters) || return false )
7985 return true
8086end
8187
82- @assert atrisktype (Tuple{typeof (== ),Any,Any})
83- @assert atrisktype (Tuple{typeof (== ),Symbol,Any})
84- @assert atrisktype (Tuple{typeof (== ),Any,Symbol})
85- @assert ! atrisktype (Tuple{typeof (== ),Symbol,Symbol})
86- @assert ! atrisktype (Tuple{typeof (convert),Type{Any},Any})
87- @assert ! atrisktype (Tuple{typeof (convert),Type{AbstractString},AbstractString})
88- @assert ! atrisktype (Tuple{typeof (convert),Type{AbstractString},String})
89- @assert atrisktype (Tuple{typeof (convert),Type{String},AbstractString})
90- @assert ! atrisktype (Tuple{typeof (map),Function,Vector{Any}})
91- @assert ! atrisktype (Tuple{typeof (getindex),Dict{Union{String,Int},Any},Union{String,Int}})
92- @assert atrisktype (Tuple{typeof (getindex),Dict{Union{String,Int},Any},Any})
93- @assert ! atrisktype (Tuple{Type{BoundsError},Any,Any})
94- @assert atrisktype (Tuple{typeof (sin),Any})
95- @assert ! atrisktype (Tuple{typeof (length),Tuple{Any,Any}})
96- @assert atrisktype (Tuple{typeof (setindex!),Vector{Int},Any,Int})
97- @assert ! atrisktype (Tuple{typeof (setindex!),Vector{Any},Any,Int})
98- @assert atrisktype (Tuple{typeof (push!),Vector{Int},Any})
99- @assert ! atrisktype (Tuple{typeof (push!),Vector{Any},Any})
88+ @assert is_atrisk_type (Tuple{typeof (== ),Any,Any})
89+ @assert is_atrisk_type (Tuple{typeof (== ),Symbol,Any})
90+ @assert is_atrisk_type (Tuple{typeof (== ),Any,Symbol})
91+ @assert ! is_atrisk_type (Tuple{typeof (== ),Symbol,Symbol})
92+ @assert ! is_atrisk_type (Tuple{typeof (convert),Type{Any},Any})
93+ @assert ! is_atrisk_type (Tuple{typeof (convert),Type{AbstractString},AbstractString})
94+ @assert ! is_atrisk_type (Tuple{typeof (convert),Type{AbstractString},String})
95+ @assert is_atrisk_type (Tuple{typeof (convert),Type{String},AbstractString})
96+ @assert ! is_atrisk_type (Tuple{typeof (convert),Type{Union{Int,Float32}},Int})
97+ @assert ! is_atrisk_type (Tuple{typeof (convert),Type{Union{Int,Float32}},Int32})
98+ @assert is_atrisk_type (Tuple{typeof (convert),Type{Union{Int,Float32}},Integer})
99+ @assert ! is_atrisk_type (Tuple{typeof (convert),Type{T} where T<: Union{Int,Float32} ,Int})
100+ @assert ! is_atrisk_type (Tuple{typeof (map),Function,Vector{Any}})
101+ @assert ! is_atrisk_type (Tuple{typeof (getindex),Dict{Union{String,Int},Any},Union{String,Int}})
102+ @assert is_atrisk_type (Tuple{typeof (getindex),Dict{Union{String,Int},Any},Any})
103+ @assert ! is_atrisk_type (Tuple{Type{BoundsError},Any,Any})
104+ @assert is_atrisk_type (Tuple{typeof (sin),Any})
105+ @assert ! is_atrisk_type (Tuple{typeof (length),Tuple{Any,Any}})
106+ @assert is_atrisk_type (Tuple{typeof (setindex!),Vector{Int},Any,Int})
107+ @assert ! is_atrisk_type (Tuple{typeof (setindex!),Vector{Any},Any,Int})
108+ @assert is_atrisk_type (Tuple{typeof (push!),Vector{Int},Any})
109+ @assert ! is_atrisk_type (Tuple{typeof (push!),Vector{Any},Any})
110+
111+ # Get the name of a method as written in the code. This strips keyword-method mangling.
112+ function codename (sym:: Symbol )
113+ symstr = String (sym)
114+ # Body methods
115+ m = match (r" ^#(.*?)#\d +$" , symstr)
116+ m != = nothing && return Symbol (only (m. captures))
117+ # kw methods
118+ m = match (r" ^(.*?)##kw$" , symstr)
119+ m != = nothing && return Symbol (only (m. captures))
120+ return sym
121+ end
100122
101- isexported (mi:: Core.MethodInstance ) = isdefined (Main, mi. def. name)
123+ isexported (mi:: Core.MethodInstance ) = isdefined (Main, codename ( mi. def. name) )
102124getfunc (mi:: Core.MethodInstance ) = getfunc (mi. def)
103125getfunc (m:: Method ) = getfield (m. module, m. name)
104126nmethods (mi:: Core.MethodInstance ) = length (methods (getfunc (mi)))
124146const becounter = Dict {Core.MethodInstance,Int} ()
125147visit () do item
126148 if item isa Core. MethodInstance && ! fromcc (item. def. module)
127- if atrisktype (item. specTypes)
149+ if is_atrisk_type (item. specTypes)
128150 becounter[item] = length (all_backedges (item))
129151 end
130152 return false
@@ -141,15 +163,12 @@ open("/tmp/methdata_$VERSION.log", "w") do io
141163end
142164
143165# Split into exported & private functions
144- mtup = (nmethods = 0 , nbackedges = 0 )
145- miexp = Pair{Core. MethodInstance,typeof (mtup)}[]
166+ miexp = Pair{Core. MethodInstance,Int}[]
146167mipriv = similar (miexp)
147168for (mi, c) in prs
148- n = nmethods (mi)
149- pr = mi=> (nmethods= n, nbackedges= c)
150169 if isexported (mi)
151- push! (miexp, pr )
170+ push! (miexp, mi => c )
152171 else
153- push! (mipriv, pr )
172+ push! (mipriv, mi => c )
154173 end
155174end
0 commit comments