typeprof 0.21.11 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -31
  3. data/bin/typeprof +5 -0
  4. data/doc/doc.ja.md +134 -0
  5. data/doc/doc.md +136 -0
  6. data/lib/typeprof/cli/cli.rb +180 -0
  7. data/lib/typeprof/cli.rb +2 -133
  8. data/lib/typeprof/code_range.rb +112 -0
  9. data/lib/typeprof/core/ast/base.rb +263 -0
  10. data/lib/typeprof/core/ast/call.rb +251 -0
  11. data/lib/typeprof/core/ast/const.rb +126 -0
  12. data/lib/typeprof/core/ast/control.rb +432 -0
  13. data/lib/typeprof/core/ast/meta.rb +150 -0
  14. data/lib/typeprof/core/ast/method.rb +335 -0
  15. data/lib/typeprof/core/ast/misc.rb +263 -0
  16. data/lib/typeprof/core/ast/module.rb +123 -0
  17. data/lib/typeprof/core/ast/pattern.rb +140 -0
  18. data/lib/typeprof/core/ast/sig_decl.rb +471 -0
  19. data/lib/typeprof/core/ast/sig_type.rb +663 -0
  20. data/lib/typeprof/core/ast/value.rb +319 -0
  21. data/lib/typeprof/core/ast/variable.rb +315 -0
  22. data/lib/typeprof/core/ast.rb +472 -0
  23. data/lib/typeprof/core/builtin.rb +146 -0
  24. data/lib/typeprof/core/env/method.rb +137 -0
  25. data/lib/typeprof/core/env/method_entity.rb +55 -0
  26. data/lib/typeprof/core/env/module_entity.rb +408 -0
  27. data/lib/typeprof/core/env/static_read.rb +155 -0
  28. data/lib/typeprof/core/env/type_alias_entity.rb +27 -0
  29. data/lib/typeprof/core/env/value_entity.rb +32 -0
  30. data/lib/typeprof/core/env.rb +360 -0
  31. data/lib/typeprof/core/graph/box.rb +991 -0
  32. data/lib/typeprof/core/graph/change_set.rb +224 -0
  33. data/lib/typeprof/core/graph/filter.rb +155 -0
  34. data/lib/typeprof/core/graph/vertex.rb +222 -0
  35. data/lib/typeprof/core/graph.rb +3 -0
  36. data/lib/typeprof/core/service.rb +522 -0
  37. data/lib/typeprof/core/type.rb +348 -0
  38. data/lib/typeprof/core/util.rb +81 -0
  39. data/lib/typeprof/core.rb +32 -0
  40. data/lib/typeprof/diagnostic.rb +35 -0
  41. data/lib/typeprof/lsp/messages.rb +430 -0
  42. data/lib/typeprof/lsp/server.rb +177 -0
  43. data/lib/typeprof/lsp/text.rb +69 -0
  44. data/lib/typeprof/lsp/util.rb +61 -0
  45. data/lib/typeprof/lsp.rb +4 -907
  46. data/lib/typeprof/version.rb +1 -1
  47. data/lib/typeprof.rb +4 -18
  48. data/typeprof.gemspec +5 -7
  49. metadata +48 -35
  50. data/.github/dependabot.yml +0 -6
  51. data/.github/workflows/main.yml +0 -39
  52. data/.gitignore +0 -9
  53. data/Gemfile +0 -17
  54. data/Gemfile.lock +0 -41
  55. data/Rakefile +0 -10
  56. data/exe/typeprof +0 -10
  57. data/lib/typeprof/analyzer.rb +0 -2598
  58. data/lib/typeprof/arguments.rb +0 -414
  59. data/lib/typeprof/block.rb +0 -176
  60. data/lib/typeprof/builtin.rb +0 -893
  61. data/lib/typeprof/code-range.rb +0 -177
  62. data/lib/typeprof/config.rb +0 -158
  63. data/lib/typeprof/container-type.rb +0 -912
  64. data/lib/typeprof/export.rb +0 -589
  65. data/lib/typeprof/import.rb +0 -852
  66. data/lib/typeprof/insns-def.rb +0 -65
  67. data/lib/typeprof/iseq.rb +0 -864
  68. data/lib/typeprof/method.rb +0 -355
  69. data/lib/typeprof/type.rb +0 -1140
  70. data/lib/typeprof/utils.rb +0 -212
  71. data/tools/coverage.rb +0 -14
  72. data/tools/setup-insns-def.rb +0 -30
  73. data/typeprof-lsp +0 -3
@@ -1,912 +0,0 @@
1
- module TypeProf
2
- class AllocationSite
3
- include Utils::StructuralEquality
4
-
5
- def initialize(val, parent = nil)
6
- raise if !val.is_a?(Utils::StructuralEquality) && !val.is_a?(Integer) && !val.is_a?(Symbol)
7
- @val = val
8
- @parent = parent
9
- end
10
-
11
- attr_reader :val, :parent
12
-
13
- def add_id(val)
14
- AllocationSite.new(val, self)
15
- end
16
- end
17
-
18
- class Type # or AbstractValue
19
- # Cell, Array, and Hash are types for global interface, e.g., TypedISeq.
20
- # Do not push such types to local environment, stack, etc.
21
-
22
- class ContainerType < Type
23
- def match?(other)
24
- return nil if self.class != other.class
25
- return nil unless @base_type.consistent?(other.base_type)
26
- @elems.match?(other.elems)
27
- end
28
-
29
- def each_free_type_variable(&blk)
30
- @elems.each_free_type_variable(&blk)
31
- end
32
-
33
- def consistent?(other)
34
- raise "must not be used"
35
- end
36
-
37
- def self.create_empty_instance(klass)
38
- base_type = Type::Instance.new(klass)
39
- case klass
40
- when Type::Builtin[:ary] # XXX: check inheritance...
41
- Type::Array.new(Type::Array::Elements.new([], Type.bot), base_type)
42
- when Type::Builtin[:hash]
43
- Type.gen_hash(base_type) {|h| }
44
- else
45
- Type::Cell.new(Type::Cell::Elements.new([Type.bot] * klass.type_params.size), base_type)
46
- end
47
- end
48
-
49
- def include_untyped?(scratch)
50
- return true if @base_type.include_untyped?(scratch)
51
- return true if @elems.include_untyped?(scratch)
52
- false
53
- end
54
- end
55
-
56
- # The most basic container type for default type parameter class
57
- class Cell < ContainerType
58
- def initialize(elems, base_type)
59
- raise if !elems.is_a?(Cell::Elements)
60
- @elems = elems # Cell::Elements
61
- raise unless base_type
62
- @base_type = base_type
63
- if base_type.klass.type_params.size != elems.elems.size
64
- raise
65
- end
66
- end
67
-
68
- attr_reader :elems, :base_type
69
-
70
- def inspect
71
- "Type::Cell[#{ @elems.inspect }, base_type: #{ @base_type.inspect }]"
72
- end
73
-
74
- def screen_name(scratch)
75
- str = @elems.screen_name(scratch)
76
- if str.start_with?("*")
77
- str = @base_type.screen_name(scratch) + str[1..]
78
- end
79
- str
80
- end
81
-
82
- def localize(env, alloc_site, depth)
83
- return env, Type.any if depth <= 0
84
- alloc_site = alloc_site.add_id(:cell).add_id(@base_type)
85
- env, elems = @elems.localize(env, alloc_site, depth)
86
- ty = Local.new(Cell, alloc_site, @base_type)
87
- env = env.deploy_type(alloc_site, elems)
88
- return env, ty
89
- end
90
-
91
- def limit_size(limit)
92
- return Type.any if limit <= 0
93
- Cell.new(@elems.limit_size(limit - 1), @base_type)
94
- end
95
-
96
- def method_dispatch_info
97
- raise
98
- end
99
-
100
- def substitute(subst, depth)
101
- return Type.any if depth <= 0
102
- elems = @elems.substitute(subst, depth)
103
- Cell.new(elems, @base_type)
104
- end
105
-
106
- def generate_substitution
107
- subst = {}
108
- tyvars = @base_type.klass.type_params.map {|name,| Type::Var.new(name) }
109
- tyvars.zip(@elems.elems) do |tyvar, elem|
110
- if subst[tyvar]
111
- subst[tyvar] = subst[tyvar].union(elem)
112
- else
113
- subst[tyvar] = elem
114
- end
115
- end
116
- subst
117
- end
118
-
119
- class Elements # Cell
120
- include Utils::StructuralEquality
121
-
122
- def initialize(elems)
123
- @elems = elems
124
- end
125
-
126
- def self.dummy_elements
127
- Elements.new([]) # XXX
128
- end
129
-
130
- attr_reader :elems
131
-
132
- def to_local_type(id, base_ty)
133
- Type::Local.new(Cell, id, base_ty)
134
- end
135
-
136
- def globalize(env, visited, depth)
137
- Elements.new(@elems.map {|ty| ty.globalize(env, visited, depth) })
138
- end
139
-
140
- def localize(env, alloc_site, depth)
141
- elems = @elems.map.with_index do |ty, i|
142
- alloc_site2 = alloc_site.add_id(i)
143
- env, ty = ty.localize(env, alloc_site2, depth)
144
- ty
145
- end
146
- return env, Elements.new(elems)
147
- end
148
-
149
- def limit_size(limit)
150
- Elements.new(@elems.map {|ty| ty.limit_size(limit) })
151
- end
152
-
153
- def screen_name(scratch)
154
- "*[#{ @elems.map {|ty| ty.screen_name(scratch) }.join(", ") }]"
155
- end
156
-
157
- def pretty_print(q)
158
- q.group(9, "Elements[", "]") do
159
- q.seplist(@elems) do |elem|
160
- q.pp elem
161
- end
162
- end
163
- end
164
-
165
- def match?(other)
166
- return nil if @elems.size != other.elems.size
167
- subst = nil
168
- @elems.zip(other.elems) do |ty0, ty1|
169
- subst2 = Type.match?(ty0, ty1)
170
- return nil unless subst2
171
- subst = Type.merge_substitution(subst, subst2)
172
- end
173
- subst
174
- end
175
-
176
- def each_free_type_variable(&blk)
177
- @elems.each do |ty|
178
- ty.each_free_type_variable(&blk)
179
- end
180
- end
181
-
182
- def substitute(subst, depth)
183
- Elements.new(@elems.map {|ty| ty.substitute(subst, depth) })
184
- end
185
-
186
- def [](idx)
187
- @elems[idx]
188
- end
189
-
190
- def update(idx, ty)
191
- Elements.new(Utils.array_update(@elems, idx, @elems[idx].union(ty)))
192
- end
193
-
194
- def union(other)
195
- return self if self == other
196
- if @elems.size != other.elems.size
197
- raise "#{ @elems.size } != #{ other.elems.size }"
198
- end
199
- elems = []
200
- @elems.zip(other.elems) do |ty0, ty1|
201
- elems << ty0.union(ty1)
202
- end
203
- Elements.new(elems)
204
- end
205
-
206
- def include_untyped?(scratch)
207
- return @elems.any? {|ty| ty.include_untyped?(scratch) }
208
- end
209
- end
210
- end
211
-
212
- # Do not insert Array type to local environment, stack, etc.
213
- class Array < ContainerType
214
- def initialize(elems, base_type)
215
- raise unless elems.is_a?(Array::Elements)
216
- @elems = elems # Array::Elements
217
- raise unless base_type
218
- @base_type = base_type
219
- end
220
-
221
- attr_reader :elems, :base_type
222
-
223
- def inspect
224
- "Type::Array[#{ @elems.inspect }, base_type: #{ @base_type.inspect }]"
225
- #@base_type.inspect
226
- end
227
-
228
- def screen_name(scratch)
229
- str = @elems.screen_name(scratch)
230
- if str =~ /\A\*\[(.*)\]\z/
231
- str = @base_type.klass.type_params.map {|var_name,| var_name == :Elem ? $1 : "untyped" }.join(", ")
232
- str = "#{ @base_type.screen_name(scratch) }[#{ str }]"
233
- end
234
- str
235
- end
236
-
237
- def localize(env, alloc_site, depth)
238
- return env, Type.any if depth <= 0
239
- alloc_site = alloc_site.add_id(:ary).add_id(@base_type)
240
- env, elems = @elems.localize(env, alloc_site, depth - 1)
241
- ty = Local.new(Array, alloc_site, @base_type)
242
- env = env.deploy_type(alloc_site, elems)
243
- return env, ty
244
- end
245
-
246
- def limit_size(limit)
247
- return Type.any if limit <= 0
248
- Array.new(@elems.limit_size(limit - 1), @base_type)
249
- end
250
-
251
- def method_dispatch_info
252
- raise
253
- end
254
-
255
- def substitute(subst, depth)
256
- return Type.any if depth <= 0
257
- elems = @elems.substitute(subst, depth - 1)
258
- Array.new(elems, @base_type)
259
- end
260
-
261
- def generate_substitution
262
- { Type::Var.new(:Elem) => @elems.squash }
263
- end
264
-
265
- class Elements # Array
266
- include Utils::StructuralEquality
267
-
268
- def initialize(lead_tys, rest_ty = Type.bot)
269
- raise unless lead_tys.all? {|ty| ty.is_a?(Type) }
270
- raise unless rest_ty.is_a?(Type)
271
- @lead_tys, @rest_ty = lead_tys, rest_ty
272
- end
273
-
274
- def self.dummy_elements
275
- Elements.new([], Type.any)
276
- end
277
-
278
- attr_reader :lead_tys, :rest_ty
279
-
280
- def to_local_type(id, base_ty)
281
- Type::Local.new(Array, id, base_ty)
282
- end
283
-
284
- def globalize(env, visited, depth)
285
- lead_tys = []
286
- @lead_tys.each do |ty|
287
- lead_tys << ty.globalize(env, visited, depth)
288
- end
289
- rest_ty = @rest_ty&.globalize(env, visited, depth)
290
- Elements.new(lead_tys, rest_ty)
291
- end
292
-
293
- def localize(env, alloc_site, depth)
294
- lead_tys = @lead_tys.map.with_index do |ty, i|
295
- alloc_site2 = alloc_site.add_id(i)
296
- env, ty = ty.localize(env, alloc_site2, depth)
297
- ty
298
- end
299
- alloc_site_rest = alloc_site.add_id(:rest)
300
- env, rest_ty = @rest_ty.localize(env, alloc_site_rest, depth)
301
- return env, Elements.new(lead_tys, rest_ty)
302
- end
303
-
304
- def limit_size(limit)
305
- Elements.new(@lead_tys.map {|ty| ty.limit_size(limit) }, @rest_ty.limit_size(limit))
306
- end
307
-
308
- def screen_name(scratch)
309
- if @rest_ty == Type.bot
310
- if @lead_tys.empty?
311
- # This is heuristic: in general, an empty array is a wrong guess.
312
- # Note that an empty array is representable as "[ ]" in RBS, but
313
- # users often have to modify it to "Array[something]".
314
- # In this term, "Array[untyped]" is considered more useful than "[ ]".
315
- return "Array[untyped]"
316
- end
317
- s = @lead_tys.map do |ty|
318
- ty.screen_name(scratch)
319
- end
320
- s << "*" + @rest_ty.screen_name(scratch) if @rest_ty != Type.bot
321
- return "[#{ s.join(", ") }]"
322
- end
323
-
324
- "*[#{ squash.screen_name(scratch) }]"
325
- rescue SystemStackError
326
- p squash
327
- exit!
328
- end
329
-
330
- def pretty_print(q)
331
- q.group(9, "Elements[", "]") do
332
- q.seplist(@lead_tys + [@rest_ty]) do |elem|
333
- q.pp elem
334
- end
335
- end
336
- end
337
-
338
- def match?(other)
339
- n = [@lead_tys.size, other.lead_tys.size].min
340
- rest_ty1 = @lead_tys[n..].inject(@rest_ty) {|ty1, ty2| ty1.union(ty2) }
341
- rest_ty2 = other.lead_tys[n..].inject(other.rest_ty) {|ty1, ty2| ty1.union(ty2) }
342
- subst = nil
343
- (@lead_tys[0, n] + [rest_ty1]).zip(other.lead_tys[0, n] + [rest_ty2]) do |ty0, ty1|
344
- subst2 = Type.match?(ty0, ty1)
345
- return nil unless subst2
346
- subst = Type.merge_substitution(subst, subst2)
347
- end
348
- subst
349
- end
350
-
351
- def each_free_type_variable(&blk)
352
- @lead_tys.each do |ty|
353
- ty.each_free_type_variable(&blk)
354
- end
355
- @rest_ty&.each_free_type_variable(&blk)
356
- end
357
-
358
- def substitute(subst, depth)
359
- lead_tys = @lead_tys.map {|ty| ty.substitute(subst, depth) }
360
- rest_ty = @rest_ty.substitute(subst, depth)
361
- Elements.new(lead_tys, rest_ty)
362
- end
363
-
364
- def squash
365
- @lead_tys.inject(@rest_ty) {|ty1, ty2| ty1.union(ty2) } #.union(Type.nil) # is this needed?
366
- end
367
-
368
- def squash_or_any
369
- ty = squash
370
- ty == Type.bot ? Type.any : ty
371
- end
372
-
373
- def [](idx)
374
- if idx.is_a?(Range)
375
- if @rest_ty == Type.bot
376
- lead_tys = @lead_tys[idx]
377
- if lead_tys
378
- rest_ty = Type.bot
379
- else
380
- return Type.nil
381
- end
382
- else
383
- b, e = idx.begin, idx.end
384
- b = 0 if !b
385
- if !e
386
- lead_tys = @lead_tys[idx] || []
387
- rest_ty = @rest_ty
388
- elsif b >= 0
389
- if e >= 0
390
- if b <= e
391
- if e < @lead_tys.size
392
- lead_tys = @lead_tys[idx]
393
- rest_ty = Type.bot
394
- else
395
- lead_tys = @lead_tys[idx] || []
396
- rest_ty = @rest_ty
397
- end
398
- else
399
- return Type.nil
400
- end
401
- else
402
- lead_tys = @lead_tys[idx] || []
403
- e = idx.exclude_end? ? e : e == -1 ? @lead_tys.size : e + 1
404
- rest_ty = (@lead_tys[e + 1..] || []).inject(@rest_ty) {|ty0, ty1| ty0.union(ty1) }
405
- end
406
- else
407
- lead_tys = []
408
- if e >= 0
409
- rest_ty = e < @lead_tys.size ? Type.bot : @rest_ty
410
- range = [0, @lead_tys.size + b].max .. (idx.exclude_end? ? e - 1 : e)
411
- rest_ty = @lead_tys[range].inject(rest_ty) {|ty0, ty1| ty0.union(ty1) }
412
- else
413
- if b <= e
414
- range = [0, @lead_tys.size + b].max .. (idx.exclude_end? ? e - 1 : e)
415
- rest_ty = @lead_tys[range].inject(@rest_ty) {|ty0, ty1| ty0.union(ty1) }
416
- else
417
- return Type.nil
418
- end
419
- end
420
- end
421
- end
422
- base_ty = Type::Instance.new(Type::Builtin[:ary])
423
- Array.new(Elements.new(lead_tys, rest_ty), base_ty)
424
- elsif idx >= 0
425
- if idx < @lead_tys.size
426
- @lead_tys[idx]
427
- elsif @rest_ty == Type.bot
428
- Type.nil
429
- else
430
- @rest_ty
431
- end
432
- else
433
- i = @lead_tys.size + idx
434
- i = [i, 0].max
435
- ty = @rest_ty
436
- @lead_tys[i..].each do |ty2|
437
- ty = ty.union(ty2)
438
- end
439
- ty
440
- end
441
- end
442
-
443
- def update(idx, ty)
444
- if idx
445
- if idx >= 0
446
- if idx < @lead_tys.size
447
- lead_tys = Utils.array_update(@lead_tys, idx, ty)
448
- Elements.new(lead_tys, @rest_ty)
449
- else
450
- rest_ty = @rest_ty.union(ty)
451
- Elements.new(@lead_tys, rest_ty)
452
- end
453
- else
454
- i = @lead_tys.size + idx
455
- if @rest_ty == Type.bot
456
- if i >= 0
457
- lead_tys = Utils.array_update(@lead_tys, i, ty)
458
- Elements.new(lead_tys, Type.bot)
459
- else
460
- # TODO: out of bound? should we emit an error?
461
- Elements.new(@lead_tys, Type.bot)
462
- end
463
- else
464
- i = [i, 0].max
465
- lead_tys = @lead_tys[0, i] + @lead_tys[i..].map {|ty2| ty2.union(ty) }
466
- rest_ty = @rest_ty.union(ty)
467
- Elements.new(@lead_tys, rest_ty)
468
- end
469
- end
470
- else
471
- lead_tys = @lead_tys.map {|ty1| ty1.union(ty) }
472
- rest_ty = @rest_ty.union(ty)
473
- Elements.new(lead_tys, rest_ty)
474
- end
475
- end
476
-
477
- def append(ty)
478
- if @rest_ty == Type.bot
479
- if @lead_tys.size < 5 # XXX: should be configurable, or ...?
480
- lead_tys = @lead_tys + [ty]
481
- Elements.new(lead_tys, @rest_ty)
482
- else
483
- Elements.new(@lead_tys, ty)
484
- end
485
- else
486
- Elements.new(@lead_tys, @rest_ty.union(ty))
487
- end
488
- end
489
-
490
- def union(other)
491
- return self if self == other
492
- raise "Hash::Elements merge Array::Elements" if other.is_a?(Hash::Elements)
493
-
494
- lead_count = [@lead_tys.size, other.lead_tys.size].min
495
- lead_tys = (0...lead_count).map do |i|
496
- @lead_tys[i].union(other.lead_tys[i])
497
- end
498
-
499
- rest_ty = @rest_ty.union(other.rest_ty)
500
- (@lead_tys[lead_count..-1] + other.lead_tys[lead_count..-1]).each do |ty|
501
- rest_ty = rest_ty.union(ty)
502
- end
503
-
504
- Elements.new(lead_tys, rest_ty)
505
- end
506
-
507
- def take_first(num)
508
- base_ty = Type::Instance.new(Type::Builtin[:ary])
509
- if @lead_tys.size >= num
510
- lead_tys = @lead_tys[0, num]
511
- rest_ary_ty = Array.new(Elements.new(@lead_tys[num..-1], @rest_ty), base_ty)
512
- return lead_tys, rest_ary_ty
513
- else
514
- lead_tys = @lead_tys.dup
515
- until lead_tys.size == num
516
- # .union(Type.nil) is needed for `a, b, c = [42]` to assign nil to b and c
517
- lead_tys << @rest_ty.union(Type.nil)
518
- end
519
- rest_ary_ty = Array.new(Elements.new([], @rest_ty), base_ty)
520
- return lead_tys, rest_ary_ty
521
- end
522
- end
523
-
524
- def take_last(num)
525
- base_ty = Type::Instance.new(Type::Builtin[:ary])
526
- if @rest_ty == Type.bot
527
- if @lead_tys.size >= num
528
- following_tys = @lead_tys[-num, num]
529
- rest_ary_ty = Array.new(Elements.new(@lead_tys[0...-num], Type.bot), base_ty)
530
- return rest_ary_ty, following_tys
531
- else
532
- following_tys = @lead_tys[-num, num] || []
533
- until following_tys.size == num
534
- following_tys.unshift(Type.nil)
535
- end
536
- rest_ary_ty = Array.new(Elements.new([], Type.bot), base_ty)
537
- return rest_ary_ty, following_tys
538
- end
539
- else
540
- lead_tys = @lead_tys.dup
541
- last_ty = rest_ty
542
- following_tys = []
543
- until following_tys.size == num
544
- last_ty = last_ty.union(lead_tys.pop) unless lead_tys.empty?
545
- following_tys.unshift(last_ty)
546
- end
547
- rest_ty = lead_tys.inject(last_ty) {|ty1, ty2| ty1.union(ty2) }
548
- rest_ary_ty = Array.new(Elements.new([], rest_ty), base_ty)
549
- return rest_ary_ty, following_tys
550
- end
551
- end
552
-
553
- def include_untyped?(scratch)
554
- return true if @lead_tys.any? {|ty| ty.include_untyped?(scratch) }
555
- return true if @rest_ty.include_untyped?(scratch)
556
- false
557
- end
558
- end
559
- end
560
-
561
- class Hash < ContainerType
562
- def initialize(elems, base_type)
563
- @elems = elems
564
- raise unless elems
565
- @base_type = base_type
566
- end
567
-
568
- attr_reader :elems, :base_type
569
-
570
- def inspect
571
- "Type::Hash#{ @elems.inspect }"
572
- end
573
-
574
- def screen_name(scratch)
575
- @elems.screen_name(scratch)
576
- end
577
-
578
- def localize(env, alloc_site, depth)
579
- return env, Type.any if depth <= 0
580
- alloc_site = alloc_site.add_id(:hash).add_id(@base_type)
581
- env, elems = @elems.localize(env, alloc_site, depth - 1)
582
- ty = Local.new(Hash, alloc_site, @base_type)
583
- env = env.deploy_type(alloc_site, elems)
584
- return env, ty
585
- end
586
-
587
- def limit_size(limit)
588
- return Type.any if limit <= 0
589
- Hash.new(@elems.limit_size(limit - 1), @base_type)
590
- end
591
-
592
- def method_dispatch_info
593
- raise
594
- end
595
-
596
- def substitute(subst, depth)
597
- return Type.any if depth <= 0
598
- elems = @elems.substitute(subst, depth - 1)
599
- Hash.new(elems, @base_type)
600
- end
601
-
602
- def generate_substitution
603
- tyvar_k = Type::Var.new(:K)
604
- tyvar_v = Type::Var.new(:V)
605
- k_ty0, v_ty0 = @elems.squash
606
- # XXX: need to heuristically replace ret type Hash[K, V] with self, instead of conversative type?
607
- { tyvar_k => k_ty0, tyvar_v => v_ty0 }
608
- end
609
-
610
- class Elements # Hash
611
- include Utils::StructuralEquality
612
-
613
- def initialize(map_tys)
614
- map_tys.each do |k_ty, v_ty|
615
- raise unless k_ty.is_a?(Type)
616
- raise unless v_ty.is_a?(Type)
617
- raise if k_ty.is_a?(Type::Union)
618
- raise if k_ty.is_a?(Type::Local)
619
- raise if k_ty.is_a?(Type::Array)
620
- raise if k_ty.is_a?(Type::Hash)
621
- end
622
- @map_tys = map_tys
623
- end
624
-
625
- def self.dummy_elements
626
- Elements.new({Type.any => Type.any})
627
- end
628
-
629
- attr_reader :map_tys
630
-
631
- def to_local_type(id, base_ty)
632
- Type::Local.new(Hash, id, base_ty)
633
- end
634
-
635
- def globalize(env, visited, depth)
636
- map_tys = {}
637
- @map_tys.each do |k_ty, v_ty|
638
- v_ty = v_ty.globalize(env, visited, depth)
639
- if map_tys[k_ty]
640
- map_tys[k_ty] = map_tys[k_ty].union(v_ty)
641
- else
642
- map_tys[k_ty] = v_ty
643
- end
644
- end
645
- Elements.new(map_tys)
646
- end
647
-
648
- def localize(env, alloc_site, depth)
649
- map_tys = @map_tys.to_h do |k_ty, v_ty|
650
- alloc_site2 = alloc_site.add_id(k_ty)
651
- env, v_ty = v_ty.localize(env, alloc_site2, depth)
652
- [k_ty, v_ty]
653
- end
654
- return env, Elements.new(map_tys)
655
- end
656
-
657
- def limit_size(limit)
658
- map_tys = {}
659
- @map_tys.each do |k_ty, v_ty|
660
- k_ty = k_ty.limit_size(limit)
661
- v_ty = v_ty.limit_size(limit)
662
- if map_tys[k_ty]
663
- map_tys[k_ty] = map_tys[k_ty].union(v_ty)
664
- else
665
- map_tys[k_ty] = v_ty
666
- end
667
- end
668
- Elements.new(map_tys)
669
- end
670
-
671
- def screen_name(scratch)
672
- if !@map_tys.empty? && @map_tys.all? {|k_ty,| k_ty.is_a?(Type::Symbol) }
673
- s = @map_tys.map do |k_ty, v_ty|
674
- v = v_ty.screen_name(scratch)
675
- "#{ k_ty.sym }: #{ v }"
676
- end.join(", ")
677
- "{#{ s }}"
678
- else
679
- k_ty = v_ty = Type.bot
680
- @map_tys.each do |k, v|
681
- k_ty = k_ty.union(k)
682
- v_ty = v_ty.union(v)
683
- end
684
- k_ty = Type.any if k_ty == Type.bot
685
- v_ty = Type.any if v_ty == Type.bot
686
- k_ty = k_ty.screen_name(scratch)
687
- v_ty = v_ty.screen_name(scratch)
688
- "Hash[#{ k_ty }, #{ v_ty }]"
689
- end
690
- end
691
-
692
- def pretty_print(q)
693
- q.group(9, "Elements[", "]") do
694
- q.seplist(@map_tys) do |k_ty, v_ty|
695
- q.group do
696
- q.pp k_ty
697
- q.text '=>'
698
- q.group(1) do
699
- q.breakable ''
700
- q.pp v_ty
701
- end
702
- end
703
- end
704
- end
705
- end
706
-
707
- def match?(other)
708
- subst = nil
709
- other.map_tys.each do |k1, v1|
710
- subst2 = nil
711
- @map_tys.each do |k0, v0|
712
- subst3 = Type.match?(k0, k1)
713
- if subst3
714
- subst4 = Type.match?(v0, v1)
715
- if subst4
716
- subst2 = Type.merge_substitution(subst2, subst3)
717
- subst2 = Type.merge_substitution(subst2, subst4)
718
- end
719
- end
720
- end
721
- return nil unless subst2
722
- subst = Type.merge_substitution(subst, subst2)
723
- end
724
- subst
725
- end
726
-
727
- def each_free_type_variable(&blk)
728
- @map_tys.each do |k, v|
729
- k.each_free_type_variable(&blk)
730
- v.each_free_type_variable(&blk)
731
- end
732
- end
733
-
734
- def substitute(subst, depth)
735
- map_tys = {}
736
- @map_tys.each do |k_ty_orig, v_ty_orig|
737
- k_ty = k_ty_orig.substitute(subst, depth)
738
- v_ty = v_ty_orig.substitute(subst, depth)
739
- k_ty.each_child_global do |k_ty|
740
- # This is a temporal hack to mitigate type explosion
741
- k_ty = Type.any if k_ty.is_a?(Type::Array)
742
- k_ty = Type.any if k_ty.is_a?(Type::Hash)
743
- if map_tys[k_ty]
744
- map_tys[k_ty] = map_tys[k_ty].union(v_ty)
745
- else
746
- map_tys[k_ty] = v_ty
747
- end
748
- end
749
- end
750
- Elements.new(map_tys)
751
- end
752
-
753
- def squash
754
- all_k_ty, all_v_ty = Type.bot, Type.bot
755
- @map_tys.each do |k_ty, v_ty|
756
- all_k_ty = all_k_ty.union(k_ty)
757
- all_v_ty = all_v_ty.union(v_ty)
758
- end
759
- return all_k_ty, all_v_ty
760
- end
761
-
762
- def [](key_ty)
763
- val_ty = Type.bot
764
- @map_tys.each do |k_ty, v_ty|
765
- if Type.match?(k_ty, key_ty)
766
- val_ty = val_ty.union(v_ty)
767
- end
768
- end
769
- val_ty
770
- end
771
-
772
- def update(idx, ty)
773
- map_tys = @map_tys.dup
774
- idx.each_child_global do |idx|
775
- # This is a temporal hack to mitigate type explosion
776
- idx = Type.any if idx.is_a?(Type::Array)
777
- idx = Type.any if idx.is_a?(Type::Hash)
778
-
779
- if map_tys[idx]
780
- map_tys[idx] = map_tys[idx].union(ty)
781
- else
782
- map_tys[idx] = ty
783
- end
784
- end
785
- Elements.new(map_tys)
786
- end
787
-
788
- def union(other)
789
- return self if self == other
790
- raise "Array::Elements merge Hash::Elements" if other.is_a?(Array::Elements)
791
-
792
- map_tys = @map_tys.dup
793
- other.map_tys.each do |k_ty, v_ty|
794
- if map_tys[k_ty]
795
- map_tys[k_ty] = map_tys[k_ty].union(v_ty)
796
- else
797
- map_tys[k_ty] = v_ty
798
- end
799
- end
800
-
801
- Elements.new(map_tys)
802
- end
803
-
804
- def to_keywords
805
- kw_tys = {}
806
- @map_tys.each do |key_ty, val_ty|
807
- if key_ty.is_a?(Type::Symbol)
808
- kw_tys[key_ty.sym] = val_ty
809
- else
810
- all_val_ty = Type.bot
811
- @map_tys.each do |_key_ty, val_ty|
812
- all_val_ty = all_val_ty.union(val_ty)
813
- end
814
- return { nil => all_val_ty }
815
- end
816
- end
817
- kw_tys
818
- end
819
-
820
- def include_untyped?(scratch)
821
- @map_tys.each do |key, val|
822
- return true if key.include_untyped?(scratch)
823
- return true if val.include_untyped?(scratch)
824
- end
825
- false
826
- end
827
- end
828
- end
829
-
830
- class Local < ContainerType
831
- def initialize(kind, id, base_type)
832
- @kind = kind
833
- raise if @kind != Cell && @kind != Array && @kind != Hash
834
- @id = id
835
- raise unless base_type
836
- @base_type = base_type
837
- end
838
-
839
- attr_reader :kind, :id, :base_type
840
-
841
- def inspect
842
- "Type::Local[#{ @kind }, #{ @id }, base_type: #{ @base_type.inspect }]"
843
- end
844
-
845
- def screen_name(scratch)
846
- #raise "Local type must not be included in signature"
847
- "Local[#{ @kind }]"
848
- end
849
-
850
- def globalize(env, visited, depth)
851
- if visited[self] || depth <= 0
852
- Type.any
853
- else
854
- visited[self] = true
855
- elems = env.get_container_elem_types(@id)
856
- if elems
857
- elems = elems.globalize(env, visited, depth - 1)
858
- else
859
- # TODO: currently out-of-scope array cannot be accessed
860
- elems = @kind::Elements.dummy_elements
861
- end
862
- visited.delete(self)
863
- @kind.new(elems, @base_type)
864
- end
865
- end
866
-
867
- def method_dispatch_info
868
- @base_type.method_dispatch_info
869
- end
870
-
871
- def update_container_elem_type(subst, env, caller_ep, scratch)
872
- case
873
- when @kind == Cell
874
- tyvars = @base_type.klass.type_params.map {|name,| Type::Var.new(name) }
875
- # XXX: This should be skipped when the called methods belongs to superclass
876
- tyvars.each_with_index do |tyvar, idx|
877
- ty = subst[tyvar]
878
- if ty
879
- env, ty = scratch.localize_type(ty, env, caller_ep)
880
- env = scratch.update_container_elem_types(env, caller_ep, @id, @base_type) do |elems|
881
- elems.update(idx, ty)
882
- end
883
- end
884
- end
885
- when @kind == Array
886
- tyvar_elem = Type::Var.new(:Elem)
887
- if subst[tyvar_elem]
888
- ty = subst[tyvar_elem]
889
- env, ty = scratch.localize_type(ty, env, caller_ep)
890
- env = scratch.update_container_elem_types(env, caller_ep, @id, @base_type) do |elems|
891
- elems.update(nil, ty)
892
- end
893
- end
894
- when @kind == Hash
895
- tyvar_k = Type::Var.new(:K)
896
- tyvar_v = Type::Var.new(:V)
897
- if subst[tyvar_k] && subst[tyvar_v]
898
- k_ty = subst[tyvar_k]
899
- v_ty = subst[tyvar_v]
900
- alloc_site = AllocationSite.new(caller_ep)
901
- env, k_ty = scratch.localize_type(k_ty, env, caller_ep, alloc_site.add_id(:k))
902
- env, v_ty = scratch.localize_type(v_ty, env, caller_ep, alloc_site.add_id(:v))
903
- env = scratch.update_container_elem_types(env, caller_ep, @id, @base_type) do |elems|
904
- elems.update(k_ty, v_ty)
905
- end
906
- end
907
- end
908
- env
909
- end
910
- end
911
- end
912
- end