-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
/
typeutils.jl
161 lines (142 loc) · 5.26 KB
/
typeutils.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# This file is a part of Julia. License is MIT: https://julialang.org/license
#####################
# lattice utilities #
#####################
function rewrap(@nospecialize(t), @nospecialize(u))
isa(t, Const) && return t
isa(t, Conditional) && return t
return rewrap_unionall(t, u)
end
const _TYPE_NAME = Type.body.name
isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _TYPE_NAME
# true if Type{T} is inlineable as constant T
# requires that T is a singleton, s.t. T == S implies T === S
isconstType(@nospecialize t) = isType(t) && issingletontype(t.parameters[1])
# test whether T is a singleton type, s.t. T == S implies T === S
function issingletontype(@nospecialize t)
# typeof(Bottom) is special since even though it is a leaftype,
# at runtime, it might be Type{Union{}} instead, so don't attempt inference of it
t === typeof(Union{}) && return false
t === Union{} && return true
isa(t, TypeVar) && return false # TypeVars are identified by address, not equality
iskindtype(typeof(t)) || return true # non-types are always compared by egal in the type system
isconcretetype(t) && return true # these are also interned and pointer comparable
if isa(t, DataType) && t.name !== Tuple.name && !isvarargtype(t) # invariant DataTypes
return all(p -> issingletontype(p), t.parameters)
end
return false
end
iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom))
isconcretedispatch(@nospecialize t) = isconcretetype(t) && !iskindtype(t)
# equivalent to isdispatchtuple(Tuple{v}) || v == Union{}
# and is thus perhaps most similar to the old (pre-1.0) `isleaftype` query
function isdispatchelem(@nospecialize v)
return (v === Bottom) || (v === typeof(Bottom)) ||
isconcretedispatch(v) || (isType(v) && !has_free_typevars(v))
end
argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(widenconst, argtypes)...}
function isknownlength(t::DataType)
isvatuple(t) || return true
return length(t.parameters) > 0 && isa(unwrap_unionall(t.parameters[end]).parameters[2], Int)
end
# test if non-Type, non-TypeVar `x` can be used to parameterize a type
function valid_tparam(@nospecialize(x))
if isa(x, Tuple)
for t in x
isa(t, Symbol) || isbitstype(typeof(t)) || return false
end
return true
end
return isa(x, Symbol) || isbitstype(typeof(x))
end
has_free_typevars(@nospecialize(t)) = ccall(:jl_has_free_typevars, Cint, (Any,), t) != 0
# return an upper-bound on type `a` with type `b` removed
# such that `return <: a` && `Union{return, b} == Union{a, b}`
function typesubtract(@nospecialize(a), @nospecialize(b))
if a <: b
return Bottom
end
if isa(a, Union)
return Union{typesubtract(a.a, b),
typesubtract(a.b, b)}
end
return a # TODO: improve this bound?
end
function tvar_extent(@nospecialize t)
while t isa TypeVar
t = t.ub
end
return t
end
_typename(@nospecialize a) = Union{}
_typename(a::TypeVar) = Core.TypeName
function _typename(a::Union)
ta = _typename(a.a)
tb = _typename(a.b)
ta === tb && return ta # same type-name
(ta === Union{} || tb === Union{}) && return Union{} # threw an error
(ta isa Const && tb isa Const) && return Union{} # will throw an error (different type-names)
return Core.TypeName # uncertain result
end
_typename(union::UnionAll) = _typename(union.body)
_typename(a::DataType) = Const(a.name)
function tuple_tail_elem(@nospecialize(init), ct)
return Vararg{widenconst(foldl((a, b) -> tmerge(a, tvar_extent(unwrapva(b))), init, ct))}
end
# t[n:end]
function tupleparam_tail(t::SimpleVector, n)
lt = length(t)
if n > lt
va = t[lt]
if isvarargtype(va)
# assumes that we should never see Vararg{T, x}, where x is a constant (should be guaranteed by construction)
return Tuple{va}
end
return Tuple{}
end
return Tuple{t[n:lt]...}
end
# take a Tuple where one or more parameters are Unions
# and return an array such that those Unions are removed
# and `Union{return...} == ty`
function switchtupleunion(@nospecialize(ty))
tparams = (unwrap_unionall(ty)::DataType).parameters
return _switchtupleunion(Any[tparams...], length(tparams), [], ty)
end
function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospecialize(origt))
if i == 0
tpl = rewrap_unionall(Tuple{t...}, origt)
push!(tunion, tpl)
else
ti = t[i]
if isa(ti, Union)
for ty in uniontypes(ti::Union)
t[i] = ty
_switchtupleunion(t, i - 1, tunion, origt)
end
t[i] = ti
else
_switchtupleunion(t, i - 1, tunion, origt)
end
end
return tunion
end
tuplelen(@nospecialize tpl) = nothing
function tuplelen(tpl::DataType)
l = length(tpl.parameters)::Int
if l > 0
last = unwrap_unionall(tpl.parameters[l])
if isvarargtype(last)
N = last.parameters[2]
N isa Int || return nothing
l += N - 1
end
end
return l
end
tuplelen(tpl::UnionAll) = tuplelen(tpl.body)
function tuplelen(tpl::Union)
la, lb = tuplelen(tpl.a), tuplelen(tpl.b)
la == lb && return la
return nothing
end