typeprof 0.21.11 → 0.30.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.
- checksums.yaml +4 -4
- data/README.md +15 -31
- data/bin/typeprof +5 -0
- data/doc/doc.ja.md +134 -0
- data/doc/doc.md +136 -0
- data/lib/typeprof/cli/cli.rb +180 -0
- data/lib/typeprof/cli.rb +2 -133
- data/lib/typeprof/code_range.rb +112 -0
- data/lib/typeprof/core/ast/base.rb +263 -0
- data/lib/typeprof/core/ast/call.rb +251 -0
- data/lib/typeprof/core/ast/const.rb +126 -0
- data/lib/typeprof/core/ast/control.rb +432 -0
- data/lib/typeprof/core/ast/meta.rb +150 -0
- data/lib/typeprof/core/ast/method.rb +335 -0
- data/lib/typeprof/core/ast/misc.rb +263 -0
- data/lib/typeprof/core/ast/module.rb +123 -0
- data/lib/typeprof/core/ast/pattern.rb +140 -0
- data/lib/typeprof/core/ast/sig_decl.rb +471 -0
- data/lib/typeprof/core/ast/sig_type.rb +663 -0
- data/lib/typeprof/core/ast/value.rb +319 -0
- data/lib/typeprof/core/ast/variable.rb +315 -0
- data/lib/typeprof/core/ast.rb +472 -0
- data/lib/typeprof/core/builtin.rb +146 -0
- data/lib/typeprof/core/env/method.rb +137 -0
- data/lib/typeprof/core/env/method_entity.rb +55 -0
- data/lib/typeprof/core/env/module_entity.rb +408 -0
- data/lib/typeprof/core/env/static_read.rb +155 -0
- data/lib/typeprof/core/env/type_alias_entity.rb +27 -0
- data/lib/typeprof/core/env/value_entity.rb +32 -0
- data/lib/typeprof/core/env.rb +360 -0
- data/lib/typeprof/core/graph/box.rb +991 -0
- data/lib/typeprof/core/graph/change_set.rb +224 -0
- data/lib/typeprof/core/graph/filter.rb +155 -0
- data/lib/typeprof/core/graph/vertex.rb +222 -0
- data/lib/typeprof/core/graph.rb +3 -0
- data/lib/typeprof/core/service.rb +522 -0
- data/lib/typeprof/core/type.rb +348 -0
- data/lib/typeprof/core/util.rb +81 -0
- data/lib/typeprof/core.rb +32 -0
- data/lib/typeprof/diagnostic.rb +35 -0
- data/lib/typeprof/lsp/messages.rb +430 -0
- data/lib/typeprof/lsp/server.rb +177 -0
- data/lib/typeprof/lsp/text.rb +69 -0
- data/lib/typeprof/lsp/util.rb +61 -0
- data/lib/typeprof/lsp.rb +4 -907
- data/lib/typeprof/version.rb +1 -1
- data/lib/typeprof.rb +4 -18
- data/typeprof.gemspec +5 -7
- metadata +48 -35
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/main.yml +0 -39
- data/.gitignore +0 -9
- data/Gemfile +0 -17
- data/Gemfile.lock +0 -41
- data/Rakefile +0 -10
- data/exe/typeprof +0 -10
- data/lib/typeprof/analyzer.rb +0 -2598
- data/lib/typeprof/arguments.rb +0 -414
- data/lib/typeprof/block.rb +0 -176
- data/lib/typeprof/builtin.rb +0 -893
- data/lib/typeprof/code-range.rb +0 -177
- data/lib/typeprof/config.rb +0 -158
- data/lib/typeprof/container-type.rb +0 -912
- data/lib/typeprof/export.rb +0 -589
- data/lib/typeprof/import.rb +0 -852
- data/lib/typeprof/insns-def.rb +0 -65
- data/lib/typeprof/iseq.rb +0 -864
- data/lib/typeprof/method.rb +0 -355
- data/lib/typeprof/type.rb +0 -1140
- data/lib/typeprof/utils.rb +0 -212
- data/tools/coverage.rb +0 -14
- data/tools/setup-insns-def.rb +0 -30
- 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
|