1
1
using MethodAnalysis
2
2
3
-
4
3
"""
5
- atrisktyp (tt)
4
+ is_atrisk_type (tt)
6
5
7
6
Given a Tuple-type signature (e.g., `Tuple{typeof(sum),Vector{Int}}`), determine whether this signature
8
7
is "at risk" for invalidation. Essentially it returns `true` if one or more arguments are of abstract type,
9
8
although there are prominent exceptions:
10
9
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)
15
13
- `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.
16
19
"""
17
- function atrisktype (@nospecialize (typ))
20
+ function is_atrisk_type (@nospecialize (typ))
18
21
# signatures like `convert(Vector, a)`, `foo(::Vararg{Synbol,N}) where N` do not seem to pose a problem
19
22
isa (typ, TypeVar) && return false
20
23
# isbits parameters are not a problem
@@ -24,7 +27,7 @@ function atrisktype(@nospecialize(typ))
24
27
end
25
28
# Exclude signatures with Union{}
26
29
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)
28
31
# Type{T}: signatures like `convert(::Type{AbstractString}, ::String)` are not problematic
29
32
typ <: Type && return false
30
33
if typ <: Tuple && length (typ. parameters) >= 1
@@ -38,6 +41,9 @@ function atrisktype(@nospecialize(typ))
38
41
p2 = Base. unwrap_unionall (p2)
39
42
if isa (p2, DataType) && length (p2. parameters) === 1
40
43
T = p2. parameters[1 ]
44
+ if isa (T, TypeVar)
45
+ T = T. ub
46
+ end
41
47
isa (p3, Type) && isa (T, Type) && p3 <: T && return false
42
48
end
43
49
end
@@ -53,9 +59,9 @@ function atrisktype(@nospecialize(typ))
53
59
end
54
60
# show(io::IO, x) is OK as long as typeof(x) is safe
55
61
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
57
63
for i = 3 : length (typ. parameters)
58
- atrisktype (typ. parameters[i]) && return true
64
+ is_atrisk_type (typ. parameters[i]) && return true
59
65
end
60
66
return false
61
67
# setindex!(a, x, idx) and push!(a, x) are safe if typeof(x) <: eltype(a)
@@ -75,30 +81,46 @@ function atrisktype(@nospecialize(typ))
75
81
isconcretetype (typ) && return false
76
82
# ::Function args are excluded
77
83
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 )
79
85
return true
80
86
end
81
87
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
100
122
101
- isexported (mi:: Core.MethodInstance ) = isdefined (Main, mi. def. name)
123
+ isexported (mi:: Core.MethodInstance ) = isdefined (Main, codename ( mi. def. name) )
102
124
getfunc (mi:: Core.MethodInstance ) = getfunc (mi. def)
103
125
getfunc (m:: Method ) = getfield (m. module, m. name)
104
126
nmethods (mi:: Core.MethodInstance ) = length (methods (getfunc (mi)))
124
146
const becounter = Dict {Core.MethodInstance,Int} ()
125
147
visit () do item
126
148
if item isa Core. MethodInstance && ! fromcc (item. def. module)
127
- if atrisktype (item. specTypes)
149
+ if is_atrisk_type (item. specTypes)
128
150
becounter[item] = length (all_backedges (item))
129
151
end
130
152
return false
@@ -141,15 +163,12 @@ open("/tmp/methdata_$VERSION.log", "w") do io
141
163
end
142
164
143
165
# Split into exported & private functions
144
- mtup = (nmethods = 0 , nbackedges = 0 )
145
- miexp = Pair{Core. MethodInstance,typeof (mtup)}[]
166
+ miexp = Pair{Core. MethodInstance,Int}[]
146
167
mipriv = similar (miexp)
147
168
for (mi, c) in prs
148
- n = nmethods (mi)
149
- pr = mi=> (nmethods= n, nbackedges= c)
150
169
if isexported (mi)
151
- push! (miexp, pr )
170
+ push! (miexp, mi => c )
152
171
else
153
- push! (mipriv, pr )
172
+ push! (mipriv, mi => c )
154
173
end
155
174
end
0 commit comments