typeprof 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +26 -0
- data/.gitignore +7 -0
- data/.gitmodules +6 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +41 -0
- data/README.md +53 -0
- data/Rakefile +10 -0
- data/doc/doc.ja.md +415 -0
- data/doc/doc.md +429 -0
- data/doc/ppl2019.pdf +0 -0
- data/exe/typeprof +5 -0
- data/lib/typeprof.rb +13 -0
- data/lib/typeprof/analyzer.rb +1911 -0
- data/lib/typeprof/builtin.rb +554 -0
- data/lib/typeprof/cli.rb +110 -0
- data/lib/typeprof/container-type.rb +626 -0
- data/lib/typeprof/export.rb +203 -0
- data/lib/typeprof/import.rb +546 -0
- data/lib/typeprof/insns-def.rb +61 -0
- data/lib/typeprof/iseq.rb +387 -0
- data/lib/typeprof/method.rb +267 -0
- data/lib/typeprof/type.rb +1092 -0
- data/lib/typeprof/utils.rb +209 -0
- data/run.sh +3 -0
- data/smoke/alias.rb +30 -0
- data/smoke/alias2.rb +19 -0
- data/smoke/any-cbase.rb +5 -0
- data/smoke/any1.rb +15 -0
- data/smoke/any2.rb +17 -0
- data/smoke/arguments.rb +16 -0
- data/smoke/array-each.rb +14 -0
- data/smoke/array-each2.rb +15 -0
- data/smoke/array-each3.rb +15 -0
- data/smoke/array-ltlt.rb +13 -0
- data/smoke/array-ltlt2.rb +16 -0
- data/smoke/array-map.rb +11 -0
- data/smoke/array-map2.rb +10 -0
- data/smoke/array-map3.rb +22 -0
- data/smoke/array-mul.rb +17 -0
- data/smoke/array-plus1.rb +10 -0
- data/smoke/array-plus2.rb +15 -0
- data/smoke/array-pop.rb +11 -0
- data/smoke/array-replace.rb +12 -0
- data/smoke/array-s-aref.rb +11 -0
- data/smoke/array1.rb +26 -0
- data/smoke/array10.rb +14 -0
- data/smoke/array11.rb +13 -0
- data/smoke/array12.rb +24 -0
- data/smoke/array13.rb +30 -0
- data/smoke/array14.rb +13 -0
- data/smoke/array2.rb +27 -0
- data/smoke/array3.rb +25 -0
- data/smoke/array4.rb +14 -0
- data/smoke/array5.rb +13 -0
- data/smoke/array6.rb +14 -0
- data/smoke/array7.rb +13 -0
- data/smoke/array8.rb +13 -0
- data/smoke/array9.rb +12 -0
- data/smoke/attr.rb +28 -0
- data/smoke/backtrace.rb +32 -0
- data/smoke/block1.rb +22 -0
- data/smoke/block10.rb +14 -0
- data/smoke/block11.rb +39 -0
- data/smoke/block12.rb +22 -0
- data/smoke/block2.rb +14 -0
- data/smoke/block3.rb +38 -0
- data/smoke/block4.rb +18 -0
- data/smoke/block5.rb +18 -0
- data/smoke/block6.rb +20 -0
- data/smoke/block7.rb +20 -0
- data/smoke/block8.rb +27 -0
- data/smoke/block9.rb +12 -0
- data/smoke/blown.rb +12 -0
- data/smoke/break1.rb +18 -0
- data/smoke/break2.rb +15 -0
- data/smoke/case.rb +16 -0
- data/smoke/case2.rb +17 -0
- data/smoke/class.rb +5 -0
- data/smoke/class_instance_var.rb +9 -0
- data/smoke/class_method.rb +25 -0
- data/smoke/class_method2.rb +21 -0
- data/smoke/class_method3.rb +27 -0
- data/smoke/constant1.rb +38 -0
- data/smoke/constant2.rb +33 -0
- data/smoke/constant3.rb +9 -0
- data/smoke/constant4.rb +11 -0
- data/smoke/context-sensitive1.rb +12 -0
- data/smoke/cvar.rb +28 -0
- data/smoke/cvar2.rb +17 -0
- data/smoke/demo.rb +80 -0
- data/smoke/demo1.rb +16 -0
- data/smoke/demo10.rb +20 -0
- data/smoke/demo11.rb +11 -0
- data/smoke/demo2.rb +14 -0
- data/smoke/demo3.rb +16 -0
- data/smoke/demo4.rb +27 -0
- data/smoke/demo5.rb +13 -0
- data/smoke/demo6.rb +21 -0
- data/smoke/demo7.rb +14 -0
- data/smoke/demo8.rb +18 -0
- data/smoke/demo9.rb +18 -0
- data/smoke/dummy-execution1.rb +14 -0
- data/smoke/dummy-execution2.rb +16 -0
- data/smoke/ensure1.rb +20 -0
- data/smoke/enumerator.rb +15 -0
- data/smoke/expandarray1.rb +22 -0
- data/smoke/expandarray2.rb +23 -0
- data/smoke/fib.rb +28 -0
- data/smoke/flow1.rb +16 -0
- data/smoke/flow2.rb +14 -0
- data/smoke/flow3.rb +14 -0
- data/smoke/flow4.rb +5 -0
- data/smoke/flow5.rb +19 -0
- data/smoke/flow6.rb +19 -0
- data/smoke/flow7.rb +26 -0
- data/smoke/for.rb +9 -0
- data/smoke/freeze.rb +11 -0
- data/smoke/function.rb +16 -0
- data/smoke/gvar.rb +13 -0
- data/smoke/hash-fetch.rb +27 -0
- data/smoke/hash1.rb +18 -0
- data/smoke/hash2.rb +12 -0
- data/smoke/hash3.rb +13 -0
- data/smoke/hash4.rb +10 -0
- data/smoke/hash5.rb +14 -0
- data/smoke/inheritance.rb +34 -0
- data/smoke/inheritance2.rb +29 -0
- data/smoke/initialize.rb +26 -0
- data/smoke/instance_eval.rb +18 -0
- data/smoke/int_times.rb +14 -0
- data/smoke/integer.rb +10 -0
- data/smoke/ivar.rb +29 -0
- data/smoke/ivar2.rb +30 -0
- data/smoke/kernel-class.rb +12 -0
- data/smoke/keyword1.rb +11 -0
- data/smoke/keyword2.rb +11 -0
- data/smoke/keyword3.rb +12 -0
- data/smoke/keyword4.rb +11 -0
- data/smoke/keyword5.rb +15 -0
- data/smoke/kwsplat1.rb +42 -0
- data/smoke/kwsplat2.rb +12 -0
- data/smoke/manual-rbs.rb +27 -0
- data/smoke/manual-rbs.rbs +3 -0
- data/smoke/manual-rbs2.rb +20 -0
- data/smoke/manual-rbs2.rbs +8 -0
- data/smoke/masgn1.rb +13 -0
- data/smoke/masgn2.rb +17 -0
- data/smoke/masgn3.rb +12 -0
- data/smoke/method_in_branch.rb +22 -0
- data/smoke/module1.rb +29 -0
- data/smoke/module2.rb +28 -0
- data/smoke/module3.rb +33 -0
- data/smoke/module4.rb +29 -0
- data/smoke/module_function1.rb +28 -0
- data/smoke/module_function2.rb +28 -0
- data/smoke/multiple-include.rb +14 -0
- data/smoke/multiple-superclass.rb +16 -0
- data/smoke/next1.rb +20 -0
- data/smoke/next2.rb +16 -0
- data/smoke/object-send1.rb +22 -0
- data/smoke/once.rb +12 -0
- data/smoke/optional1.rb +13 -0
- data/smoke/optional2.rb +15 -0
- data/smoke/parameterizedd-self.rb +18 -0
- data/smoke/pathname1.rb +13 -0
- data/smoke/pathname2.rb +13 -0
- data/smoke/printf.rb +20 -0
- data/smoke/proc.rb +19 -0
- data/smoke/proc2.rb +16 -0
- data/smoke/proc3.rb +14 -0
- data/smoke/proc4.rb +11 -0
- data/smoke/range.rb +13 -0
- data/smoke/redo1.rb +21 -0
- data/smoke/redo2.rb +22 -0
- data/smoke/req-keyword.rb +12 -0
- data/smoke/rescue1.rb +20 -0
- data/smoke/rescue2.rb +22 -0
- data/smoke/respond_to.rb +22 -0
- data/smoke/rest-farg.rb +10 -0
- data/smoke/rest1.rb +25 -0
- data/smoke/rest2.rb +30 -0
- data/smoke/rest3.rb +36 -0
- data/smoke/rest4.rb +18 -0
- data/smoke/rest5.rb +10 -0
- data/smoke/rest6.rb +11 -0
- data/smoke/retry1.rb +20 -0
- data/smoke/return.rb +13 -0
- data/smoke/reveal.rb +13 -0
- data/smoke/singleton_class.rb +8 -0
- data/smoke/singleton_method.rb +9 -0
- data/smoke/step.rb +17 -0
- data/smoke/string-split.rb +11 -0
- data/smoke/struct.rb +9 -0
- data/smoke/struct2.rb +24 -0
- data/smoke/super1.rb +50 -0
- data/smoke/super2.rb +16 -0
- data/smoke/super3.rb +19 -0
- data/smoke/svar1.rb +12 -0
- data/smoke/tap1.rb +17 -0
- data/smoke/toplevel.rb +12 -0
- data/smoke/two-map.rb +17 -0
- data/smoke/type_var.rb +10 -0
- data/smoke/typed_method.rb +15 -0
- data/smoke/union-recv.rb +29 -0
- data/smoke/variadic1.rb.notyet +5 -0
- data/smoke/wrong-extend.rb +25 -0
- data/smoke/wrong-include.rb +26 -0
- data/smoke/wrong-rbs.rb +15 -0
- data/smoke/wrong-rbs.rbs +7 -0
- data/testbed/ao.rb +297 -0
- data/testbed/diff-lcs-entrypoint.rb +4 -0
- data/testbed/goodcheck-Gemfile.lock +51 -0
- data/tools/coverage.rb +14 -0
- data/tools/setup-insns-def.rb +30 -0
- data/tools/stackprof-wrapper.rb +10 -0
- data/typeprof.gemspec +24 -0
- metadata +262 -0
@@ -0,0 +1,1092 @@
|
|
1
|
+
module TypeProf
|
2
|
+
class Type # or AbstractValue
|
3
|
+
include Utils::StructuralEquality
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
raise "cannot instanciate abstract type"
|
7
|
+
end
|
8
|
+
|
9
|
+
Builtin = {}
|
10
|
+
|
11
|
+
def globalize(_env, _visited, _depth)
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def localize(env, _alloc_site, _depth)
|
16
|
+
return env, self
|
17
|
+
end
|
18
|
+
|
19
|
+
def limit_size(limit)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def consistent?(other, subst)
|
24
|
+
case other
|
25
|
+
when Type::Any then true
|
26
|
+
when Type::Var then other.add_subst!(self, subst)
|
27
|
+
when Type::Union
|
28
|
+
other.types.each do |ty2|
|
29
|
+
return true if consistent?(ty2, subst)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
self == other
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def each_child
|
37
|
+
yield self
|
38
|
+
end
|
39
|
+
|
40
|
+
def each_child_global
|
41
|
+
yield self
|
42
|
+
end
|
43
|
+
|
44
|
+
def union(other)
|
45
|
+
return self if self == other # fastpath
|
46
|
+
|
47
|
+
ty1, ty2 = self, other
|
48
|
+
|
49
|
+
ty1 = container_to_union(ty1)
|
50
|
+
ty2 = container_to_union(ty2)
|
51
|
+
|
52
|
+
if ty1.is_a?(Union) && ty2.is_a?(Union)
|
53
|
+
ty = ty1.types.sum(ty2.types)
|
54
|
+
all_elems = ty1.elems.dup || {}
|
55
|
+
ty2.elems&.each do |key, elems|
|
56
|
+
all_elems[key] = union_elems(all_elems[key], elems)
|
57
|
+
end
|
58
|
+
all_elems = nil if all_elems.empty?
|
59
|
+
|
60
|
+
Type::Union.new(ty, all_elems).normalize
|
61
|
+
else
|
62
|
+
ty1, ty2 = ty2, ty1 if ty2.is_a?(Union)
|
63
|
+
if ty1.is_a?(Union)
|
64
|
+
Type::Union.new(ty1.types.add(ty2), ty1.elems).normalize
|
65
|
+
else
|
66
|
+
Type::Union.new(Utils::Set[ty1, ty2], nil).normalize
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private def container_to_union(ty)
|
72
|
+
case ty
|
73
|
+
when Type::Array, Type::Hash
|
74
|
+
Type::Union.new(Utils::Set[], { [ty.class, ty.base_type] => ty.elems })
|
75
|
+
else
|
76
|
+
ty
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private def union_elems(e1, e2)
|
81
|
+
if e1
|
82
|
+
if e2
|
83
|
+
e1.union(e2)
|
84
|
+
else
|
85
|
+
e1
|
86
|
+
end
|
87
|
+
else
|
88
|
+
e2
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def substitute(_subst, _depth)
|
93
|
+
raise "cannot substitute abstract type: #{ self.class }"
|
94
|
+
end
|
95
|
+
|
96
|
+
DummySubstitution = Object.new
|
97
|
+
def DummySubstitution.[](_)
|
98
|
+
Type.any
|
99
|
+
end
|
100
|
+
|
101
|
+
def remove_type_vars
|
102
|
+
substitute(DummySubstitution, Config.options[:type_depth_limit])
|
103
|
+
end
|
104
|
+
|
105
|
+
class Any < Type
|
106
|
+
def initialize
|
107
|
+
end
|
108
|
+
|
109
|
+
def inspect
|
110
|
+
"Type::Any"
|
111
|
+
end
|
112
|
+
|
113
|
+
def screen_name(scratch)
|
114
|
+
"untyped"
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_method(mid, scratch)
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def consistent?(other, subst)
|
122
|
+
# need to create a type assignment if other is Var
|
123
|
+
other.add_subst!(self, subst) if other.is_a?(Type::Var)
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
def substitute(_subst, _depth)
|
128
|
+
self
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Union < Type
|
133
|
+
def initialize(tys, elems)
|
134
|
+
raise unless tys.is_a?(Utils::Set)
|
135
|
+
@types = tys # Set
|
136
|
+
|
137
|
+
# invariant check
|
138
|
+
local = nil
|
139
|
+
tys.each do |ty|
|
140
|
+
raise unless ty.is_a?(Type)
|
141
|
+
local = true if ty.is_a?(LocalArray) || ty.is_a?(LocalHash)
|
142
|
+
end
|
143
|
+
raise if local && elems
|
144
|
+
|
145
|
+
@elems = elems
|
146
|
+
end
|
147
|
+
|
148
|
+
def limit_size(limit)
|
149
|
+
return Type.any if limit <= 0
|
150
|
+
tys = Utils::Set[]
|
151
|
+
@types.each do |ty|
|
152
|
+
tys = tys.add(ty.limit_size(limit - 1))
|
153
|
+
end
|
154
|
+
elems = @elems&.to_h do |key, elems|
|
155
|
+
[key, elems.limit_size(limit - 1)]
|
156
|
+
end
|
157
|
+
Union.new(tys, elems)
|
158
|
+
end
|
159
|
+
|
160
|
+
attr_reader :types, :elems
|
161
|
+
|
162
|
+
def normalize
|
163
|
+
if @types.size == 1 && !@elems
|
164
|
+
@types.each {|ty| return ty }
|
165
|
+
elsif @types.size == 0
|
166
|
+
if @elems && @elems.size == 1
|
167
|
+
(container_kind, base_type), elems = @elems.first
|
168
|
+
# container_kind = Type::Array or Type::Hash
|
169
|
+
container_kind.new(elems, base_type)
|
170
|
+
else
|
171
|
+
self
|
172
|
+
end
|
173
|
+
else
|
174
|
+
self
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def each_child(&blk) # local
|
179
|
+
@types.each(&blk)
|
180
|
+
raise if @elems
|
181
|
+
end
|
182
|
+
|
183
|
+
def each_child_global(&blk)
|
184
|
+
@types.each(&blk)
|
185
|
+
@elems&.each do |(container_kind, base_type), elems|
|
186
|
+
yield container_kind.new(elems, base_type)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def inspect
|
191
|
+
a = []
|
192
|
+
a << "Type::Union{#{ @types.to_a.map {|ty| ty.inspect }.join(", ") }"
|
193
|
+
@elems&.each do |(container_kind, base_type), elems|
|
194
|
+
a << ", #{ container_kind.new(elems, base_type).inspect }"
|
195
|
+
end
|
196
|
+
a << "}"
|
197
|
+
a.join
|
198
|
+
end
|
199
|
+
|
200
|
+
def screen_name(scratch)
|
201
|
+
types = @types.to_a
|
202
|
+
@elems&.each do |(container_kind, base_type), elems|
|
203
|
+
types << container_kind.new(elems, base_type)
|
204
|
+
end
|
205
|
+
if types.size == 0
|
206
|
+
"bot"
|
207
|
+
else
|
208
|
+
types = types.to_a
|
209
|
+
optional = !!types.delete(Type::Instance.new(Type::Builtin[:nil]))
|
210
|
+
bool = false
|
211
|
+
if types.include?(Type::Instance.new(Type::Builtin[:false])) &&
|
212
|
+
types.include?(Type::Instance.new(Type::Builtin[:true]))
|
213
|
+
types.delete(Type::Instance.new(Type::Builtin[:false]))
|
214
|
+
types.delete(Type::Instance.new(Type::Builtin[:true]))
|
215
|
+
bool = true
|
216
|
+
end
|
217
|
+
types.delete(Type.any) unless Config.options[:pedantic_output]
|
218
|
+
types = types.map {|ty| ty.screen_name(scratch) }
|
219
|
+
types << "bool" if bool
|
220
|
+
types = types.sort
|
221
|
+
if optional
|
222
|
+
if types.size == 1
|
223
|
+
types.first + "?"
|
224
|
+
else
|
225
|
+
"(#{ types.join (" | ") })?"
|
226
|
+
end
|
227
|
+
else
|
228
|
+
types.join (" | ")
|
229
|
+
end
|
230
|
+
end
|
231
|
+
rescue SystemStackError
|
232
|
+
p self
|
233
|
+
raise
|
234
|
+
end
|
235
|
+
|
236
|
+
def globalize(env, visited, depth)
|
237
|
+
return Type.any if depth <= 0
|
238
|
+
tys = Utils::Set[]
|
239
|
+
raise if @elems
|
240
|
+
|
241
|
+
elems = {}
|
242
|
+
@types.each do |ty|
|
243
|
+
ty = ty.globalize(env, visited, depth - 1)
|
244
|
+
case ty
|
245
|
+
when Type::Array, Type::Hash
|
246
|
+
key = [ty.class, ty.base_type]
|
247
|
+
elems[key] = union_elems(elems[key], ty.elems)
|
248
|
+
else
|
249
|
+
tys = tys.add(ty)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
elems = nil if elems.empty?
|
253
|
+
|
254
|
+
Type::Union.new(tys, elems).normalize
|
255
|
+
end
|
256
|
+
|
257
|
+
def localize(env, alloc_site, depth)
|
258
|
+
return env, Type.any if depth <= 0
|
259
|
+
tys = @types.map do |ty|
|
260
|
+
alloc_site2 = alloc_site.add_id(ty)
|
261
|
+
env, ty2 = ty.localize(env, alloc_site2, depth - 1)
|
262
|
+
ty2
|
263
|
+
end
|
264
|
+
@elems&.each do |(container_kind, base_type), elems|
|
265
|
+
ty = container_kind.new(elems, base_type)
|
266
|
+
alloc_site2 = alloc_site.add_id(container_kind.name.to_sym).add_id(base_type)
|
267
|
+
env, ty = ty.localize(env, alloc_site2, depth - 1)
|
268
|
+
tys = tys.add(ty)
|
269
|
+
end
|
270
|
+
ty = Union.new(tys, nil).normalize
|
271
|
+
return env, ty
|
272
|
+
end
|
273
|
+
|
274
|
+
def consistent?(other, subst)
|
275
|
+
case other
|
276
|
+
when Type::Any then true
|
277
|
+
when Type::Var then other.add_subst!(self, subst)
|
278
|
+
when Type::Union
|
279
|
+
# this is very conservative to create subst:
|
280
|
+
# consistent?( int | str, int | X) creates { X => int | str } but should be { X => str }???
|
281
|
+
@types.each do |ty1|
|
282
|
+
other.types.each do |ty2|
|
283
|
+
subst2 = subst.dup
|
284
|
+
if ty1.consistent?(ty2, subst2)
|
285
|
+
subst.replace(subst2)
|
286
|
+
# XXX: need to check other pairs to create conservative substitution??
|
287
|
+
# consistent?( X | :foo, str | int ) may return { X => str } or { X => int } but should be { X => str | int }?
|
288
|
+
return true
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
return true if @types.size == 0 && other.types.size == 0 # XXX: is this okay?
|
293
|
+
# TODO: array argument?
|
294
|
+
return false
|
295
|
+
else
|
296
|
+
@types.each do |ty1|
|
297
|
+
return true if ty1.consistent?(other, subst)
|
298
|
+
end
|
299
|
+
# TODO: array argument?
|
300
|
+
return false
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def substitute(subst, depth)
|
305
|
+
return Type.any if depth <= 0
|
306
|
+
unions = []
|
307
|
+
tys = Utils::Set[]
|
308
|
+
@types.each do |ty|
|
309
|
+
ty = ty.substitute(subst, depth - 1)
|
310
|
+
case ty
|
311
|
+
when Union
|
312
|
+
unions << ty
|
313
|
+
else
|
314
|
+
tys = tys.add(ty)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
elems = @elems&.to_h do |(container_kind, base_type), elems|
|
318
|
+
[[container_kind, base_type], elems.substitute(subst, depth - 1)]
|
319
|
+
end
|
320
|
+
ty = Union.new(tys, elems)
|
321
|
+
unions.each do |ty0|
|
322
|
+
ty = ty.union(ty0)
|
323
|
+
end
|
324
|
+
ty
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def self.any
|
329
|
+
@any ||= Any.new
|
330
|
+
end
|
331
|
+
|
332
|
+
def self.bot
|
333
|
+
@bot ||= Union.new(Utils::Set[], nil)
|
334
|
+
end
|
335
|
+
|
336
|
+
def self.bool
|
337
|
+
@bool ||= Union.new(Utils::Set[
|
338
|
+
Instance.new(Type::Builtin[:true]),
|
339
|
+
Instance.new(Type::Builtin[:false])
|
340
|
+
], nil)
|
341
|
+
end
|
342
|
+
|
343
|
+
def self.nil
|
344
|
+
@nil ||= Instance.new(Type::Builtin[:nil])
|
345
|
+
end
|
346
|
+
|
347
|
+
def self.optional(ty)
|
348
|
+
ty.union(Type.nil)
|
349
|
+
end
|
350
|
+
|
351
|
+
class Var < Type
|
352
|
+
def initialize(name)
|
353
|
+
@name = name
|
354
|
+
end
|
355
|
+
|
356
|
+
def screen_name(scratch)
|
357
|
+
"Var[#{ @name }]"
|
358
|
+
end
|
359
|
+
|
360
|
+
def substitute(subst, depth)
|
361
|
+
if subst[self]
|
362
|
+
subst[self].limit_size(depth)
|
363
|
+
else
|
364
|
+
self
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
def consistent?(other, subst)
|
369
|
+
raise "should not be called: #{ self }"
|
370
|
+
end
|
371
|
+
|
372
|
+
def add_subst!(ty, subst)
|
373
|
+
if subst[self]
|
374
|
+
subst[self] = subst[self].union(ty)
|
375
|
+
else
|
376
|
+
subst[self] = ty
|
377
|
+
end
|
378
|
+
true
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
class Class < Type # or Module
|
383
|
+
def initialize(kind, idx, type_params, superclass, name)
|
384
|
+
@kind = kind # :class | :module
|
385
|
+
@idx = idx
|
386
|
+
@type_params = type_params
|
387
|
+
@superclass = superclass
|
388
|
+
@_name = name
|
389
|
+
end
|
390
|
+
|
391
|
+
attr_reader :kind, :idx, :type_params, :superclass
|
392
|
+
|
393
|
+
def inspect
|
394
|
+
if @_name
|
395
|
+
"#{ @_name }@#{ @idx }"
|
396
|
+
else
|
397
|
+
"Class[#{ @idx }]"
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def screen_name(scratch)
|
402
|
+
"#{ scratch.get_class_name(self) }.class"
|
403
|
+
end
|
404
|
+
|
405
|
+
def get_method(mid, scratch)
|
406
|
+
scratch.get_method(self, true, mid)
|
407
|
+
end
|
408
|
+
|
409
|
+
def consistent?(other, subst)
|
410
|
+
case other
|
411
|
+
when Type::Any then true
|
412
|
+
when Type::Var then other.add_subst!(self, subst)
|
413
|
+
when Type::Union
|
414
|
+
other.types.each do |ty|
|
415
|
+
return true if consistent?(ty, subst)
|
416
|
+
end
|
417
|
+
return false
|
418
|
+
when Type::Class
|
419
|
+
ty = self
|
420
|
+
loop do
|
421
|
+
# ad-hoc
|
422
|
+
return false if !ty || !other # module
|
423
|
+
|
424
|
+
return true if ty.idx == other.idx
|
425
|
+
return false if ty.idx == 0 # Object
|
426
|
+
ty = ty.superclass
|
427
|
+
end
|
428
|
+
when Type::Instance
|
429
|
+
return true if other.klass == Type::Builtin[:obj] || other.klass == Type::Builtin[:class] || other.klass == Type::Builtin[:module]
|
430
|
+
return false
|
431
|
+
else
|
432
|
+
false
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
def substitute(_subst, _depth)
|
437
|
+
self
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
class Instance < Type
|
442
|
+
def initialize(klass)
|
443
|
+
raise unless klass
|
444
|
+
raise if klass == Type.any
|
445
|
+
@klass = klass
|
446
|
+
end
|
447
|
+
|
448
|
+
attr_reader :klass
|
449
|
+
|
450
|
+
def inspect
|
451
|
+
"I[#{ @klass.inspect }]"
|
452
|
+
end
|
453
|
+
|
454
|
+
def screen_name(scratch)
|
455
|
+
case @klass
|
456
|
+
when Type::Builtin[:nil] then "nil"
|
457
|
+
when Type::Builtin[:true] then "true"
|
458
|
+
when Type::Builtin[:false] then "false"
|
459
|
+
else
|
460
|
+
scratch.get_class_name(@klass)
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def get_method(mid, scratch)
|
465
|
+
scratch.get_method(@klass, false, mid)
|
466
|
+
end
|
467
|
+
|
468
|
+
def consistent?(other, subst)
|
469
|
+
case other
|
470
|
+
when Type::Any then true
|
471
|
+
when Type::Var then other.add_subst!(self, subst)
|
472
|
+
when Type::Union
|
473
|
+
other.types.each do |ty|
|
474
|
+
return true if consistent?(ty, subst)
|
475
|
+
end
|
476
|
+
return false
|
477
|
+
when Type::Instance
|
478
|
+
@klass.consistent?(other.klass, subst)
|
479
|
+
when Type::Class
|
480
|
+
return true if @klass == Type::Builtin[:obj] || @klass == Type::Builtin[:class] || @klass == Type::Builtin[:module]
|
481
|
+
return false
|
482
|
+
else
|
483
|
+
false
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def substitute(subst, depth)
|
488
|
+
Instance.new(@klass.substitute(subst, depth))
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
class ISeq < Type
|
493
|
+
def initialize(iseq)
|
494
|
+
@iseq = iseq
|
495
|
+
end
|
496
|
+
|
497
|
+
attr_reader :iseq
|
498
|
+
|
499
|
+
def inspect
|
500
|
+
"Type::ISeq[#{ @iseq }]"
|
501
|
+
end
|
502
|
+
|
503
|
+
def screen_name(_scratch)
|
504
|
+
raise NotImplementedError
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
class ISeqProc < Type
|
509
|
+
def initialize(iseq, ep, type)
|
510
|
+
@iseq = iseq
|
511
|
+
@ep = ep
|
512
|
+
@type = type
|
513
|
+
end
|
514
|
+
|
515
|
+
attr_reader :iseq, :ep, :type
|
516
|
+
|
517
|
+
def inspect
|
518
|
+
"#<ISeqProc>"
|
519
|
+
end
|
520
|
+
|
521
|
+
def screen_name(scratch)
|
522
|
+
"Proc[#{ scratch.proc_screen_name(self) }]" # TODO: use RBS syntax
|
523
|
+
end
|
524
|
+
|
525
|
+
def get_method(mid, scratch)
|
526
|
+
@type.get_method(mid, scratch)
|
527
|
+
end
|
528
|
+
|
529
|
+
def substitute(_subst, _depth)
|
530
|
+
self # XXX
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
class TypedProc < Type
|
535
|
+
def initialize(fargs, ret_ty, type)
|
536
|
+
# XXX: need to receive blk_ty?
|
537
|
+
# XXX: may refactor "arguments = arg_tys * blk_ty" out
|
538
|
+
@fargs = fargs
|
539
|
+
@ret_ty = ret_ty
|
540
|
+
@type = type
|
541
|
+
end
|
542
|
+
|
543
|
+
attr_reader :fargs, :ret_ty
|
544
|
+
|
545
|
+
def screen_name(scratch)
|
546
|
+
"TypedProc[...]" # TODO: use RBS syntax
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
class Symbol < Type
|
551
|
+
def initialize(sym, type)
|
552
|
+
@sym = sym
|
553
|
+
@type = type
|
554
|
+
end
|
555
|
+
|
556
|
+
attr_reader :sym, :type
|
557
|
+
|
558
|
+
def inspect
|
559
|
+
"Type::Symbol[#{ @sym ? @sym.inspect : "(dynamic symbol)" }, #{ @type.inspect }]"
|
560
|
+
end
|
561
|
+
|
562
|
+
def consistent?(other, subst)
|
563
|
+
case other
|
564
|
+
when Var
|
565
|
+
other.add_subst!(self, subst)
|
566
|
+
when Symbol
|
567
|
+
@sym == other.sym
|
568
|
+
else
|
569
|
+
@type.consistent?(other, subst)
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
def screen_name(scratch)
|
574
|
+
if @sym
|
575
|
+
@sym.inspect
|
576
|
+
else
|
577
|
+
@type.screen_name(scratch)
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
def get_method(mid, scratch)
|
582
|
+
@type.get_method(mid, scratch)
|
583
|
+
end
|
584
|
+
|
585
|
+
def substitute(_subst, _depth)
|
586
|
+
self # dummy
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
# local info
|
591
|
+
class Literal < Type
|
592
|
+
def initialize(lit, type)
|
593
|
+
@lit = lit
|
594
|
+
@type = type
|
595
|
+
end
|
596
|
+
|
597
|
+
attr_reader :lit, :type
|
598
|
+
|
599
|
+
def inspect
|
600
|
+
"Type::Literal[#{ @lit.inspect }, #{ @type.inspect }]"
|
601
|
+
end
|
602
|
+
|
603
|
+
def screen_name(scratch)
|
604
|
+
@type.screen_name(scratch) + "<#{ @lit.inspect }>"
|
605
|
+
end
|
606
|
+
|
607
|
+
def globalize(_env, _visited, _depth)
|
608
|
+
@type
|
609
|
+
end
|
610
|
+
|
611
|
+
def get_method(mid, scratch)
|
612
|
+
@type.get_method(mid, scratch)
|
613
|
+
end
|
614
|
+
|
615
|
+
def consistent?(other, subst)
|
616
|
+
@type.consistent?(other, subst)
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
class HashGenerator
|
621
|
+
def initialize
|
622
|
+
@map_tys = {}
|
623
|
+
end
|
624
|
+
|
625
|
+
attr_reader :map_tys
|
626
|
+
|
627
|
+
def []=(k_ty, v_ty)
|
628
|
+
k_ty.each_child_global do |k_ty|
|
629
|
+
# This is a temporal hack to mitigate type explosion
|
630
|
+
k_ty = Type.any if k_ty.is_a?(Type::Array)
|
631
|
+
k_ty = Type.any if k_ty.is_a?(Type::Hash)
|
632
|
+
|
633
|
+
if @map_tys[k_ty]
|
634
|
+
@map_tys[k_ty] = @map_tys[k_ty].union(v_ty)
|
635
|
+
else
|
636
|
+
@map_tys[k_ty] = v_ty
|
637
|
+
end
|
638
|
+
end
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
def self.gen_hash
|
643
|
+
hg = HashGenerator.new
|
644
|
+
yield hg
|
645
|
+
base_ty = Type::Instance.new(Type::Builtin[:hash])
|
646
|
+
Type::Hash.new(Type::Hash::Elements.new(hg.map_tys), base_ty)
|
647
|
+
end
|
648
|
+
|
649
|
+
def self.guess_literal_type(obj)
|
650
|
+
case obj
|
651
|
+
when ::Symbol
|
652
|
+
Type::Symbol.new(obj, Type::Instance.new(Type::Builtin[:sym]))
|
653
|
+
when ::Integer
|
654
|
+
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:int]))
|
655
|
+
when ::Rational
|
656
|
+
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:rational]))
|
657
|
+
when ::Float
|
658
|
+
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:float]))
|
659
|
+
when ::Class
|
660
|
+
return Type.any if obj < Exception
|
661
|
+
case obj
|
662
|
+
when ::Object
|
663
|
+
Type::Builtin[:obj]
|
664
|
+
when ::Array
|
665
|
+
Type::Builtin[:ary]
|
666
|
+
else
|
667
|
+
raise "unknown class: #{ obj.inspect }"
|
668
|
+
end
|
669
|
+
when ::TrueClass
|
670
|
+
Type::Instance.new(Type::Builtin[:true])
|
671
|
+
when ::FalseClass
|
672
|
+
Type::Instance.new(Type::Builtin[:false])
|
673
|
+
when ::Array
|
674
|
+
base_ty = Type::Instance.new(Type::Builtin[:ary])
|
675
|
+
lead_tys = obj.map {|arg| guess_literal_type(arg) }
|
676
|
+
Type::Array.new(Type::Array::Elements.new(lead_tys), base_ty)
|
677
|
+
when ::Hash
|
678
|
+
Type.gen_hash do |h|
|
679
|
+
obj.each do |k, v|
|
680
|
+
k_ty = guess_literal_type(k).globalize(nil, {}, Config.options[:type_depth_limit])
|
681
|
+
v_ty = guess_literal_type(v)
|
682
|
+
h[k_ty] = v_ty
|
683
|
+
end
|
684
|
+
end
|
685
|
+
when ::String
|
686
|
+
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:str]))
|
687
|
+
when ::Regexp
|
688
|
+
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:regexp]))
|
689
|
+
when ::NilClass
|
690
|
+
Type.nil
|
691
|
+
when ::Range
|
692
|
+
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:range]))
|
693
|
+
else
|
694
|
+
raise "unknown object: #{ obj.inspect }"
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
def self.builtin_global_variable_type(var)
|
699
|
+
case var
|
700
|
+
when :$_, :$/, :$\, :$,, :$;
|
701
|
+
Type.optional(Type::Instance.new(Type::Builtin[:str]))
|
702
|
+
when :$0, :$PROGRAM_NAME
|
703
|
+
Type::Instance.new(Type::Builtin[:str])
|
704
|
+
when :$~
|
705
|
+
Type.optional(Type::Instance.new(Type::Builtin[:matchdata]))
|
706
|
+
when :$., :$$
|
707
|
+
Type::Instance.new(Type::Builtin[:int])
|
708
|
+
when :$?
|
709
|
+
Type.optional(Type::Instance.new(Type::Builtin[:int]))
|
710
|
+
when :$!
|
711
|
+
Type.optional(Type::Instance.new(Type::Builtin[:exc]))
|
712
|
+
when :$@
|
713
|
+
str = Type::Instance.new(Type::Builtin[:str])
|
714
|
+
base_ty = Type::Instance.new(Type::Builtin[:ary])
|
715
|
+
Type.optional(Type::Array.new(Type::Array::Elements.new([], str), base_ty))
|
716
|
+
when :$*, :$:, :$LOAD_PATH, :$", :$LOADED_FEATURES
|
717
|
+
str = Type::Instance.new(Type::Builtin[:str])
|
718
|
+
base_ty = Type::Instance.new(Type::Builtin[:ary])
|
719
|
+
Type::Array.new(Type::Array::Elements.new([], str), base_ty)
|
720
|
+
when :$<
|
721
|
+
:ARGF
|
722
|
+
when :$>
|
723
|
+
:STDOUT
|
724
|
+
when :$DEBUG
|
725
|
+
Type.bool
|
726
|
+
when :$FILENAME
|
727
|
+
Type::Instance.new(Type::Builtin[:str])
|
728
|
+
when :$stdin
|
729
|
+
:STDIN
|
730
|
+
when :$stdout
|
731
|
+
:STDOUT
|
732
|
+
when :$stderr
|
733
|
+
:STDERR
|
734
|
+
when :$VERBOSE
|
735
|
+
Type.bool.union(Type.nil)
|
736
|
+
else
|
737
|
+
nil
|
738
|
+
end
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
# Arguments for callee side
|
743
|
+
class FormalArguments
|
744
|
+
include Utils::StructuralEquality
|
745
|
+
|
746
|
+
def initialize(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
747
|
+
@lead_tys = lead_tys
|
748
|
+
@opt_tys = opt_tys
|
749
|
+
@rest_ty = rest_ty
|
750
|
+
@post_tys = post_tys
|
751
|
+
@kw_tys = kw_tys
|
752
|
+
kw_tys.each {|a| raise if a.size != 3 } if kw_tys
|
753
|
+
@kw_rest_ty = kw_rest_ty
|
754
|
+
@blk_ty = blk_ty
|
755
|
+
end
|
756
|
+
|
757
|
+
attr_reader :lead_tys, :opt_tys, :rest_ty, :post_tys, :kw_tys, :kw_rest_ty, :blk_ty
|
758
|
+
|
759
|
+
def consistent?(fargs, subst)
|
760
|
+
warn "used?"
|
761
|
+
return false if @lead_tys.size != fargs.lead_tys.size
|
762
|
+
return false unless @lead_tys.zip(fargs.lead_tys).all? {|ty1, ty2| ty1.consistent?(ty2, subst) }
|
763
|
+
return false if (@opt_tys || []) != (fargs.opt_tys || []) # ??
|
764
|
+
if @rest_ty
|
765
|
+
return false unless @rest_ty.consistent?(fargs.rest_ty, subst)
|
766
|
+
end
|
767
|
+
if @post_tys
|
768
|
+
return false if @post_tys.size != fargs.post_tys.size
|
769
|
+
return false unless @post_tys.zip(fargs.post_tys).all? {|ty1, ty2| ty1.consistent?(ty2, subst) }
|
770
|
+
end
|
771
|
+
return false if @kw_tys.size != fargs.kw_tys.size
|
772
|
+
return false unless @kw_tys.zip(fargs.kw_tys).all? {|(_, ty1), (_, ty2)| ty1.consistent?(ty2, subst) }
|
773
|
+
if @kw_rest_ty
|
774
|
+
return false unless @kw_rest_ty.consistent?(fargs.kw_rest_ty, subst)
|
775
|
+
end
|
776
|
+
# intentionally skip blk_ty
|
777
|
+
true
|
778
|
+
end
|
779
|
+
|
780
|
+
def screen_name(scratch)
|
781
|
+
fargs = @lead_tys.map {|ty| ty.screen_name(scratch) }
|
782
|
+
if @opt_tys
|
783
|
+
fargs += @opt_tys.map {|ty| "?" + ty.screen_name(scratch) }
|
784
|
+
end
|
785
|
+
if @rest_ty
|
786
|
+
fargs << ("*" + @rest_ty.screen_name(scratch))
|
787
|
+
end
|
788
|
+
if @post_tys
|
789
|
+
fargs += @post_tys.map {|ty| ty.screen_name(scratch) }
|
790
|
+
end
|
791
|
+
if @kw_tys
|
792
|
+
@kw_tys.each do |req, sym, ty|
|
793
|
+
opt = req ? "" : "?"
|
794
|
+
fargs << "#{ opt }#{ sym }: #{ ty.screen_name(scratch) }"
|
795
|
+
end
|
796
|
+
end
|
797
|
+
if @kw_rest_ty
|
798
|
+
fargs << ("**" + @kw_rest_ty.screen_name(scratch))
|
799
|
+
end
|
800
|
+
# intentionally skip blk_ty
|
801
|
+
fargs
|
802
|
+
end
|
803
|
+
|
804
|
+
def merge(other)
|
805
|
+
raise if @lead_tys.size != other.lead_tys.size
|
806
|
+
raise if @post_tys.size != other.post_tys.size
|
807
|
+
if @kw_tys && other.kw_tys
|
808
|
+
kws1 = {}
|
809
|
+
@kw_tys.each {|req, kw, _| kws1[kw] = req }
|
810
|
+
kws2 = {}
|
811
|
+
other.kw_tys.each {|req, kw, _| kws2[kw] = req }
|
812
|
+
(kws1.keys & kws2.keys).each do |kw|
|
813
|
+
raise if !!kws1[kw] != !!kws2[kw]
|
814
|
+
end
|
815
|
+
elsif @kw_tys || other.kw_tys
|
816
|
+
puts
|
817
|
+
p self, other
|
818
|
+
(@kw_tys || other.kw_tys).each do |req,|
|
819
|
+
raise if req
|
820
|
+
end
|
821
|
+
end
|
822
|
+
lead_tys = @lead_tys.zip(other.lead_tys).map {|ty1, ty2| ty1.union(ty2) }
|
823
|
+
if @opt_tys || other.opt_tys
|
824
|
+
opt_tys = []
|
825
|
+
[@opt_tys.size, other.opt_tys.size].max.times do |i|
|
826
|
+
ty1 = @opt_tys[i]
|
827
|
+
ty2 = other.opt_tys[i]
|
828
|
+
ty = ty1 ? ty2 ? ty1.union(ty2) : ty1 : ty2
|
829
|
+
opt_tys << ty
|
830
|
+
end
|
831
|
+
end
|
832
|
+
if @rest_ty || other.rest_ty
|
833
|
+
if @rest_ty && other.rest_ty
|
834
|
+
rest_ty = @rest_ty.union(other.rest_ty)
|
835
|
+
else
|
836
|
+
rest_ty = @rest_ty || other.rest_ty
|
837
|
+
end
|
838
|
+
end
|
839
|
+
post_tys = @post_tys.zip(other.post_tys).map {|ty1, ty2| ty1.union(ty2) }
|
840
|
+
if @kw_tys && other.kw_tys
|
841
|
+
kws1 = {}
|
842
|
+
@kw_tys.each {|req, kw, ty| kws1[kw] = [req, ty] }
|
843
|
+
kws2 = {}
|
844
|
+
other.kw_tys.each {|req, kw, ty| kws2[kw] = [req, ty] }
|
845
|
+
kw_tys = (kws1.keys | kws2.keys).map do |kw|
|
846
|
+
req1, ty1 = kws1[kw]
|
847
|
+
_req2, ty2 = kws2[kw]
|
848
|
+
ty1 ||= Type.bot
|
849
|
+
ty2 ||= Type.bot
|
850
|
+
[!!req1, kw, ty1.union(ty2)]
|
851
|
+
end
|
852
|
+
elsif @kw_tys || other.kw_tys
|
853
|
+
kw_tys = @kw_tys || other.kw_tys
|
854
|
+
else
|
855
|
+
kw_tys = nil
|
856
|
+
end
|
857
|
+
if @kw_rest_ty || other.kw_rest_ty
|
858
|
+
if @kw_rest_ty && other.kw_rest_ty
|
859
|
+
kw_rest_ty = @kw_rest_ty.union(other.kw_rest_ty)
|
860
|
+
else
|
861
|
+
kw_rest_ty = @kw_rest_ty || other.kw_rest_ty
|
862
|
+
end
|
863
|
+
end
|
864
|
+
blk_ty = @blk_ty.union(other.blk_ty) if @blk_ty
|
865
|
+
FormalArguments.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
# Arguments from caller side
|
870
|
+
class ActualArguments
|
871
|
+
def initialize(lead_tys, rest_ty, kw_ty, blk_ty)
|
872
|
+
@lead_tys = lead_tys
|
873
|
+
@rest_ty = rest_ty
|
874
|
+
@kw_ty = kw_ty
|
875
|
+
@blk_ty = blk_ty
|
876
|
+
raise unless blk_ty
|
877
|
+
end
|
878
|
+
|
879
|
+
attr_reader :lead_tys, :rest_ty, :kw_ty, :blk_ty
|
880
|
+
|
881
|
+
def merge(aargs)
|
882
|
+
len = [@lead_tys.size, aargs.lead_tys.size].min
|
883
|
+
lead_tys = @lead_tys[0, len].zip(aargs.lead_tys[0, len]).map do |ty1, ty2|
|
884
|
+
ty1.union(ty2)
|
885
|
+
end
|
886
|
+
rest_ty = @rest_ty || Type.bot
|
887
|
+
rest_ty = rest_ty.union(aargs.rest_ty) if aargs.rest_ty
|
888
|
+
(@lead_tys[len..] + aargs.lead_tys[len..]).each do |ty|
|
889
|
+
rest_ty = rest_ty.union(ty)
|
890
|
+
end
|
891
|
+
rest_ty = nil if rest_ty == Type.bot
|
892
|
+
#kw_ty = @kw_ty.union(aargs.kw_ty) # TODO
|
893
|
+
blk_ty = @blk_ty.union(aargs.blk_ty)
|
894
|
+
ActualArguments.new(lead_tys, rest_ty, kw_ty, blk_ty)
|
895
|
+
end
|
896
|
+
|
897
|
+
def globalize(caller_env, visited, depth)
|
898
|
+
lead_tys = @lead_tys.map {|ty| ty.globalize(caller_env, visited, depth) }
|
899
|
+
rest_ty = @rest_ty.globalize(caller_env, visited, depth) if @rest_ty
|
900
|
+
kw_ty = @kw_ty.globalize(caller_env, visited, depth) if @kw_ty
|
901
|
+
ActualArguments.new(lead_tys, rest_ty, kw_ty, @blk_ty)
|
902
|
+
end
|
903
|
+
|
904
|
+
def limit_size(limit)
|
905
|
+
self
|
906
|
+
end
|
907
|
+
|
908
|
+
def each_formal_arguments(fargs_format)
|
909
|
+
lead_num = fargs_format[:lead_num] || 0
|
910
|
+
post_num = fargs_format[:post_num] || 0
|
911
|
+
rest_acceptable = !!fargs_format[:rest_start]
|
912
|
+
keyword = fargs_format[:keyword]
|
913
|
+
kw_rest_acceptable = !!fargs_format[:kwrest]
|
914
|
+
opt = fargs_format[:opt]
|
915
|
+
#p fargs_format
|
916
|
+
|
917
|
+
# TODO: expand tuples to normal arguments
|
918
|
+
|
919
|
+
# check number of arguments
|
920
|
+
if !@rest_ty && lead_num + post_num > @lead_tys.size
|
921
|
+
# too less
|
922
|
+
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
|
923
|
+
return
|
924
|
+
end
|
925
|
+
if !rest_acceptable
|
926
|
+
# too many
|
927
|
+
if opt
|
928
|
+
if lead_num + post_num + opt.size - 1 < @lead_tys.size
|
929
|
+
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num }..#{ lead_num + post_num + opt.size - 1})"
|
930
|
+
return
|
931
|
+
end
|
932
|
+
else
|
933
|
+
if lead_num + post_num < @lead_tys.size
|
934
|
+
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
|
935
|
+
return
|
936
|
+
end
|
937
|
+
end
|
938
|
+
end
|
939
|
+
|
940
|
+
if @rest_ty
|
941
|
+
lower_bound = [lead_num + post_num - @lead_tys.size, 0].max
|
942
|
+
upper_bound = lead_num + post_num - @lead_tys.size + (opt ? opt.size - 1 : 0) + (rest_acceptable ? 1 : 0)
|
943
|
+
rest_elem = @rest_ty.is_a?(Type::Array) ? @rest_ty.elems.squash : Type.any
|
944
|
+
else
|
945
|
+
lower_bound = upper_bound = 0
|
946
|
+
end
|
947
|
+
|
948
|
+
if keyword
|
949
|
+
kw_tys = []
|
950
|
+
keyword.each do |kw|
|
951
|
+
case
|
952
|
+
when kw.is_a?(Symbol) # required keyword
|
953
|
+
key = kw
|
954
|
+
req = true
|
955
|
+
when kw.size == 2 # optional keyword (default value is a literal)
|
956
|
+
key, default_ty = *kw
|
957
|
+
default_ty = Type.guess_literal_type(default_ty)
|
958
|
+
default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
|
959
|
+
req = false
|
960
|
+
else # optional keyword (default value is an expression)
|
961
|
+
key, = kw
|
962
|
+
req = false
|
963
|
+
end
|
964
|
+
|
965
|
+
sym = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
|
966
|
+
ty = Type.bot
|
967
|
+
if @kw_ty.is_a?(Type::Hash)
|
968
|
+
# XXX: consider Union
|
969
|
+
ty = @kw_ty.elems[sym]
|
970
|
+
# XXX: remove the key
|
971
|
+
end
|
972
|
+
if ty == Type.bot
|
973
|
+
yield "no argument for required keywords"
|
974
|
+
return
|
975
|
+
end
|
976
|
+
ty = ty.union(default_ty) if default_ty
|
977
|
+
kw_tys << [req, key, ty]
|
978
|
+
end
|
979
|
+
end
|
980
|
+
if kw_rest_acceptable
|
981
|
+
kw_rest_ty = @kw_ty
|
982
|
+
if kw_rest_ty == Type.any
|
983
|
+
kw_rest_ty = Type.gen_hash {|h| h[Type.any] = Type.any }
|
984
|
+
end
|
985
|
+
end
|
986
|
+
#if @kw_ty
|
987
|
+
# yield "passed a keyword to non-keyword method"
|
988
|
+
#end
|
989
|
+
|
990
|
+
(lower_bound .. upper_bound).each do |rest_len|
|
991
|
+
aargs = @lead_tys + [rest_elem] * rest_len
|
992
|
+
lead_tys = aargs.shift(lead_num)
|
993
|
+
lead_tys << rest_elem until lead_tys.size == lead_num
|
994
|
+
post_tys = aargs.pop(post_num)
|
995
|
+
post_tys.unshift(rest_elem) until post_tys.size == post_num
|
996
|
+
start_pc = 0
|
997
|
+
if opt
|
998
|
+
tmp_opt = opt[1..]
|
999
|
+
opt_tys = []
|
1000
|
+
until aargs.empty? || tmp_opt.empty?
|
1001
|
+
opt_tys << aargs.shift
|
1002
|
+
start_pc = tmp_opt.shift
|
1003
|
+
end
|
1004
|
+
end
|
1005
|
+
if rest_acceptable
|
1006
|
+
acc = aargs.inject {|acc, ty| acc.union(ty) }
|
1007
|
+
acc = acc ? acc.union(rest_elem) : rest_elem if rest_elem
|
1008
|
+
acc ||= Type.bot
|
1009
|
+
rest_ty = acc
|
1010
|
+
aargs.clear
|
1011
|
+
end
|
1012
|
+
if !aargs.empty?
|
1013
|
+
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
|
1014
|
+
return
|
1015
|
+
end
|
1016
|
+
yield FormalArguments.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, @blk_ty), start_pc
|
1017
|
+
end
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def consistent_with_formal_arguments?(fargs, subst)
|
1021
|
+
aargs = @lead_tys.dup
|
1022
|
+
|
1023
|
+
# aargs: lead_tys, rest_ty
|
1024
|
+
# fargs: lead_tys, opt_tys, rest_ty, post_tys
|
1025
|
+
if @rest_ty
|
1026
|
+
lower_bound = [0, fargs.lead_tys.size + fargs.post_tys.size - aargs.size].max
|
1027
|
+
upper_bound = [0, lower_bound + fargs.opt_tys.size].max
|
1028
|
+
(lower_bound..upper_bound).each do |n|
|
1029
|
+
tmp_aargs = ActualArguments.new(@lead_tys + [@rest_ty] * n, nil, @kw_ty, @blk_ty)
|
1030
|
+
if tmp_aargs.consistent_with_formal_arguments?(fargs, subst)
|
1031
|
+
return true
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
return false
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
if fargs.rest_ty
|
1038
|
+
return false if aargs.size < fargs.lead_tys.size + fargs.post_tys.size
|
1039
|
+
aargs.shift(fargs.lead_tys.size).zip(fargs.lead_tys) do |aarg, farg|
|
1040
|
+
return false unless aarg.consistent?(farg, subst)
|
1041
|
+
end
|
1042
|
+
aargs.pop(fargs.post_tys.size).zip(fargs.post_tys) do |aarg, farg|
|
1043
|
+
return false unless aarg.consistent?(farg, subst)
|
1044
|
+
end
|
1045
|
+
fargs.opt_tys.each do |farg|
|
1046
|
+
aarg = aargs.shift
|
1047
|
+
return false unless aarg.consistent?(farg, subst)
|
1048
|
+
end
|
1049
|
+
aargs.each do |aarg|
|
1050
|
+
return false unless aarg.consistent?(fargs.rest_ty, subst)
|
1051
|
+
end
|
1052
|
+
else
|
1053
|
+
return false if aargs.size < fargs.lead_tys.size + fargs.post_tys.size
|
1054
|
+
return false if aargs.size > fargs.lead_tys.size + fargs.post_tys.size + fargs.opt_tys.size
|
1055
|
+
aargs.shift(fargs.lead_tys.size).zip(fargs.lead_tys) do |aarg, farg|
|
1056
|
+
return false unless aarg.consistent?(farg, subst)
|
1057
|
+
end
|
1058
|
+
aargs.pop(fargs.post_tys.size).zip(fargs.post_tys) do |aarg, farg|
|
1059
|
+
return false unless aarg.consistent?(farg, subst)
|
1060
|
+
end
|
1061
|
+
aargs.zip(fargs.opt_tys) do |aarg, farg|
|
1062
|
+
return false unless aarg.consistent?(farg, subst)
|
1063
|
+
end
|
1064
|
+
end
|
1065
|
+
# XXX: fargs.keyword_tys
|
1066
|
+
|
1067
|
+
case fargs.blk_ty
|
1068
|
+
when Type::TypedProc
|
1069
|
+
return false if @blk_ty == Type.nil
|
1070
|
+
when Type.nil
|
1071
|
+
return false if @blk_ty != Type.nil
|
1072
|
+
when Type::Any
|
1073
|
+
else
|
1074
|
+
raise "unknown typo of formal block signature"
|
1075
|
+
end
|
1076
|
+
true
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
def screen_name(scratch)
|
1080
|
+
aargs = @lead_tys.map {|ty| ty.screen_name(scratch) }
|
1081
|
+
if @rest_ty
|
1082
|
+
aargs << ("*" + @rest_ty.screen_name(scratch))
|
1083
|
+
end
|
1084
|
+
if @kw_ty
|
1085
|
+
aargs << ("**" + @kw_ty.screen_name(scratch)) # TODO: Hash notation -> keyword notation
|
1086
|
+
end
|
1087
|
+
s = "(#{ aargs.join(", ") })"
|
1088
|
+
s << " { #{ scratch.proc_screen_name(@blk_ty) } }" if @blk_ty != Type.nil
|
1089
|
+
s
|
1090
|
+
end
|
1091
|
+
end
|
1092
|
+
end
|