typeprof 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +26 -0
  3. data/.gitignore +7 -0
  4. data/.gitmodules +6 -0
  5. data/Gemfile +12 -0
  6. data/Gemfile.lock +41 -0
  7. data/README.md +53 -0
  8. data/Rakefile +10 -0
  9. data/doc/doc.ja.md +415 -0
  10. data/doc/doc.md +429 -0
  11. data/doc/ppl2019.pdf +0 -0
  12. data/exe/typeprof +5 -0
  13. data/lib/typeprof.rb +13 -0
  14. data/lib/typeprof/analyzer.rb +1911 -0
  15. data/lib/typeprof/builtin.rb +554 -0
  16. data/lib/typeprof/cli.rb +110 -0
  17. data/lib/typeprof/container-type.rb +626 -0
  18. data/lib/typeprof/export.rb +203 -0
  19. data/lib/typeprof/import.rb +546 -0
  20. data/lib/typeprof/insns-def.rb +61 -0
  21. data/lib/typeprof/iseq.rb +387 -0
  22. data/lib/typeprof/method.rb +267 -0
  23. data/lib/typeprof/type.rb +1092 -0
  24. data/lib/typeprof/utils.rb +209 -0
  25. data/run.sh +3 -0
  26. data/smoke/alias.rb +30 -0
  27. data/smoke/alias2.rb +19 -0
  28. data/smoke/any-cbase.rb +5 -0
  29. data/smoke/any1.rb +15 -0
  30. data/smoke/any2.rb +17 -0
  31. data/smoke/arguments.rb +16 -0
  32. data/smoke/array-each.rb +14 -0
  33. data/smoke/array-each2.rb +15 -0
  34. data/smoke/array-each3.rb +15 -0
  35. data/smoke/array-ltlt.rb +13 -0
  36. data/smoke/array-ltlt2.rb +16 -0
  37. data/smoke/array-map.rb +11 -0
  38. data/smoke/array-map2.rb +10 -0
  39. data/smoke/array-map3.rb +22 -0
  40. data/smoke/array-mul.rb +17 -0
  41. data/smoke/array-plus1.rb +10 -0
  42. data/smoke/array-plus2.rb +15 -0
  43. data/smoke/array-pop.rb +11 -0
  44. data/smoke/array-replace.rb +12 -0
  45. data/smoke/array-s-aref.rb +11 -0
  46. data/smoke/array1.rb +26 -0
  47. data/smoke/array10.rb +14 -0
  48. data/smoke/array11.rb +13 -0
  49. data/smoke/array12.rb +24 -0
  50. data/smoke/array13.rb +30 -0
  51. data/smoke/array14.rb +13 -0
  52. data/smoke/array2.rb +27 -0
  53. data/smoke/array3.rb +25 -0
  54. data/smoke/array4.rb +14 -0
  55. data/smoke/array5.rb +13 -0
  56. data/smoke/array6.rb +14 -0
  57. data/smoke/array7.rb +13 -0
  58. data/smoke/array8.rb +13 -0
  59. data/smoke/array9.rb +12 -0
  60. data/smoke/attr.rb +28 -0
  61. data/smoke/backtrace.rb +32 -0
  62. data/smoke/block1.rb +22 -0
  63. data/smoke/block10.rb +14 -0
  64. data/smoke/block11.rb +39 -0
  65. data/smoke/block12.rb +22 -0
  66. data/smoke/block2.rb +14 -0
  67. data/smoke/block3.rb +38 -0
  68. data/smoke/block4.rb +18 -0
  69. data/smoke/block5.rb +18 -0
  70. data/smoke/block6.rb +20 -0
  71. data/smoke/block7.rb +20 -0
  72. data/smoke/block8.rb +27 -0
  73. data/smoke/block9.rb +12 -0
  74. data/smoke/blown.rb +12 -0
  75. data/smoke/break1.rb +18 -0
  76. data/smoke/break2.rb +15 -0
  77. data/smoke/case.rb +16 -0
  78. data/smoke/case2.rb +17 -0
  79. data/smoke/class.rb +5 -0
  80. data/smoke/class_instance_var.rb +9 -0
  81. data/smoke/class_method.rb +25 -0
  82. data/smoke/class_method2.rb +21 -0
  83. data/smoke/class_method3.rb +27 -0
  84. data/smoke/constant1.rb +38 -0
  85. data/smoke/constant2.rb +33 -0
  86. data/smoke/constant3.rb +9 -0
  87. data/smoke/constant4.rb +11 -0
  88. data/smoke/context-sensitive1.rb +12 -0
  89. data/smoke/cvar.rb +28 -0
  90. data/smoke/cvar2.rb +17 -0
  91. data/smoke/demo.rb +80 -0
  92. data/smoke/demo1.rb +16 -0
  93. data/smoke/demo10.rb +20 -0
  94. data/smoke/demo11.rb +11 -0
  95. data/smoke/demo2.rb +14 -0
  96. data/smoke/demo3.rb +16 -0
  97. data/smoke/demo4.rb +27 -0
  98. data/smoke/demo5.rb +13 -0
  99. data/smoke/demo6.rb +21 -0
  100. data/smoke/demo7.rb +14 -0
  101. data/smoke/demo8.rb +18 -0
  102. data/smoke/demo9.rb +18 -0
  103. data/smoke/dummy-execution1.rb +14 -0
  104. data/smoke/dummy-execution2.rb +16 -0
  105. data/smoke/ensure1.rb +20 -0
  106. data/smoke/enumerator.rb +15 -0
  107. data/smoke/expandarray1.rb +22 -0
  108. data/smoke/expandarray2.rb +23 -0
  109. data/smoke/fib.rb +28 -0
  110. data/smoke/flow1.rb +16 -0
  111. data/smoke/flow2.rb +14 -0
  112. data/smoke/flow3.rb +14 -0
  113. data/smoke/flow4.rb +5 -0
  114. data/smoke/flow5.rb +19 -0
  115. data/smoke/flow6.rb +19 -0
  116. data/smoke/flow7.rb +26 -0
  117. data/smoke/for.rb +9 -0
  118. data/smoke/freeze.rb +11 -0
  119. data/smoke/function.rb +16 -0
  120. data/smoke/gvar.rb +13 -0
  121. data/smoke/hash-fetch.rb +27 -0
  122. data/smoke/hash1.rb +18 -0
  123. data/smoke/hash2.rb +12 -0
  124. data/smoke/hash3.rb +13 -0
  125. data/smoke/hash4.rb +10 -0
  126. data/smoke/hash5.rb +14 -0
  127. data/smoke/inheritance.rb +34 -0
  128. data/smoke/inheritance2.rb +29 -0
  129. data/smoke/initialize.rb +26 -0
  130. data/smoke/instance_eval.rb +18 -0
  131. data/smoke/int_times.rb +14 -0
  132. data/smoke/integer.rb +10 -0
  133. data/smoke/ivar.rb +29 -0
  134. data/smoke/ivar2.rb +30 -0
  135. data/smoke/kernel-class.rb +12 -0
  136. data/smoke/keyword1.rb +11 -0
  137. data/smoke/keyword2.rb +11 -0
  138. data/smoke/keyword3.rb +12 -0
  139. data/smoke/keyword4.rb +11 -0
  140. data/smoke/keyword5.rb +15 -0
  141. data/smoke/kwsplat1.rb +42 -0
  142. data/smoke/kwsplat2.rb +12 -0
  143. data/smoke/manual-rbs.rb +27 -0
  144. data/smoke/manual-rbs.rbs +3 -0
  145. data/smoke/manual-rbs2.rb +20 -0
  146. data/smoke/manual-rbs2.rbs +8 -0
  147. data/smoke/masgn1.rb +13 -0
  148. data/smoke/masgn2.rb +17 -0
  149. data/smoke/masgn3.rb +12 -0
  150. data/smoke/method_in_branch.rb +22 -0
  151. data/smoke/module1.rb +29 -0
  152. data/smoke/module2.rb +28 -0
  153. data/smoke/module3.rb +33 -0
  154. data/smoke/module4.rb +29 -0
  155. data/smoke/module_function1.rb +28 -0
  156. data/smoke/module_function2.rb +28 -0
  157. data/smoke/multiple-include.rb +14 -0
  158. data/smoke/multiple-superclass.rb +16 -0
  159. data/smoke/next1.rb +20 -0
  160. data/smoke/next2.rb +16 -0
  161. data/smoke/object-send1.rb +22 -0
  162. data/smoke/once.rb +12 -0
  163. data/smoke/optional1.rb +13 -0
  164. data/smoke/optional2.rb +15 -0
  165. data/smoke/parameterizedd-self.rb +18 -0
  166. data/smoke/pathname1.rb +13 -0
  167. data/smoke/pathname2.rb +13 -0
  168. data/smoke/printf.rb +20 -0
  169. data/smoke/proc.rb +19 -0
  170. data/smoke/proc2.rb +16 -0
  171. data/smoke/proc3.rb +14 -0
  172. data/smoke/proc4.rb +11 -0
  173. data/smoke/range.rb +13 -0
  174. data/smoke/redo1.rb +21 -0
  175. data/smoke/redo2.rb +22 -0
  176. data/smoke/req-keyword.rb +12 -0
  177. data/smoke/rescue1.rb +20 -0
  178. data/smoke/rescue2.rb +22 -0
  179. data/smoke/respond_to.rb +22 -0
  180. data/smoke/rest-farg.rb +10 -0
  181. data/smoke/rest1.rb +25 -0
  182. data/smoke/rest2.rb +30 -0
  183. data/smoke/rest3.rb +36 -0
  184. data/smoke/rest4.rb +18 -0
  185. data/smoke/rest5.rb +10 -0
  186. data/smoke/rest6.rb +11 -0
  187. data/smoke/retry1.rb +20 -0
  188. data/smoke/return.rb +13 -0
  189. data/smoke/reveal.rb +13 -0
  190. data/smoke/singleton_class.rb +8 -0
  191. data/smoke/singleton_method.rb +9 -0
  192. data/smoke/step.rb +17 -0
  193. data/smoke/string-split.rb +11 -0
  194. data/smoke/struct.rb +9 -0
  195. data/smoke/struct2.rb +24 -0
  196. data/smoke/super1.rb +50 -0
  197. data/smoke/super2.rb +16 -0
  198. data/smoke/super3.rb +19 -0
  199. data/smoke/svar1.rb +12 -0
  200. data/smoke/tap1.rb +17 -0
  201. data/smoke/toplevel.rb +12 -0
  202. data/smoke/two-map.rb +17 -0
  203. data/smoke/type_var.rb +10 -0
  204. data/smoke/typed_method.rb +15 -0
  205. data/smoke/union-recv.rb +29 -0
  206. data/smoke/variadic1.rb.notyet +5 -0
  207. data/smoke/wrong-extend.rb +25 -0
  208. data/smoke/wrong-include.rb +26 -0
  209. data/smoke/wrong-rbs.rb +15 -0
  210. data/smoke/wrong-rbs.rbs +7 -0
  211. data/testbed/ao.rb +297 -0
  212. data/testbed/diff-lcs-entrypoint.rb +4 -0
  213. data/testbed/goodcheck-Gemfile.lock +51 -0
  214. data/tools/coverage.rb +14 -0
  215. data/tools/setup-insns-def.rb +30 -0
  216. data/tools/stackprof-wrapper.rb +10 -0
  217. data/typeprof.gemspec +24 -0
  218. 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