typeprof 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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