typeprof 0.2.0 → 0.5.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/.github/workflows/main.yml +1 -2
- data/.gitignore +1 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +10 -21
- data/LICENSE +21 -0
- data/README.md +6 -0
- data/doc/demo.md +398 -0
- data/doc/doc.ja.md +6 -1
- data/doc/doc.md +7 -2
- data/exe/typeprof +2 -1
- data/lib/typeprof.rb +9 -0
- data/lib/typeprof/analyzer.rb +427 -325
- data/lib/typeprof/arguments.rb +405 -0
- data/lib/typeprof/block.rb +136 -0
- data/lib/typeprof/builtin.rb +36 -25
- data/lib/typeprof/cli.rb +51 -98
- data/lib/typeprof/config.rb +114 -0
- data/lib/typeprof/container-type.rb +280 -92
- data/lib/typeprof/export.rb +197 -108
- data/lib/typeprof/import.rb +134 -80
- data/lib/typeprof/iseq.rb +42 -1
- data/lib/typeprof/method.rb +178 -82
- data/lib/typeprof/type.rb +228 -369
- data/lib/typeprof/utils.rb +4 -18
- data/lib/typeprof/version.rb +3 -0
- data/smoke/arguments2.rb +55 -0
- data/smoke/array-each3.rb +1 -4
- data/smoke/array12.rb +1 -1
- data/smoke/array6.rb +1 -0
- data/smoke/block-ambiguous.rb +36 -0
- data/smoke/block-args1-rest.rb +62 -0
- data/smoke/block-args1.rb +59 -0
- data/smoke/block-args2-rest.rb +62 -0
- data/smoke/block-args2.rb +59 -0
- data/smoke/block-args3-rest.rb +73 -0
- data/smoke/block-args3.rb +70 -0
- data/smoke/block-blockarg.rb +27 -0
- data/smoke/block-kwarg.rb +52 -0
- data/smoke/block10.rb +1 -1
- data/smoke/block11.rb +1 -1
- data/smoke/block14.rb +17 -0
- data/smoke/block4.rb +2 -2
- data/smoke/block5.rb +1 -0
- data/smoke/block6.rb +1 -1
- data/smoke/block7.rb +0 -2
- data/smoke/block8.rb +2 -2
- data/smoke/block9.rb +1 -1
- data/smoke/blown.rb +1 -1
- data/smoke/class-hierarchy.rb +54 -0
- data/smoke/class-hierarchy2.rb +27 -0
- data/smoke/constant1.rb +11 -6
- data/smoke/constant2.rb +2 -0
- data/smoke/cvar.rb +1 -0
- data/smoke/demo10.rb +1 -1
- data/smoke/demo8.rb +2 -2
- data/smoke/demo9.rb +1 -3
- data/smoke/flow7.rb +1 -7
- data/smoke/flow8.rb +13 -0
- data/smoke/hash-fetch.rb +3 -3
- data/smoke/hash-merge-bang.rb +11 -0
- data/smoke/hash1.rb +1 -1
- data/smoke/hash3.rb +1 -1
- data/smoke/hash4.rb +1 -1
- data/smoke/instance_eval.rb +1 -1
- data/smoke/int_times.rb +1 -1
- data/smoke/ivar2.rb +1 -1
- data/smoke/keyword3.rb +1 -2
- data/smoke/keyword4.rb +1 -1
- data/smoke/kwsplat1.rb +1 -1
- data/smoke/kwsplat2.rb +1 -1
- data/smoke/module4.rb +2 -0
- data/smoke/multiple-superclass.rb +4 -0
- data/smoke/next2.rb +1 -1
- data/smoke/optional1.rb +1 -1
- data/smoke/optional2.rb +1 -1
- data/smoke/optional3.rb +10 -0
- data/smoke/pattern-match1.rb +23 -0
- data/smoke/pattern-match2.rb +15 -0
- data/smoke/proc4.rb +1 -1
- data/smoke/rbs-extend.rb +9 -0
- data/smoke/rbs-extend.rbs +7 -0
- data/smoke/rbs-interface.rb +24 -0
- data/smoke/rbs-interface.rbs +12 -0
- data/smoke/rbs-proc1.rb +9 -0
- data/smoke/rbs-proc1.rbs +3 -0
- data/smoke/rbs-proc2.rb +20 -0
- data/smoke/rbs-proc2.rbs +3 -0
- data/smoke/rbs-proc3.rb +13 -0
- data/smoke/rbs-proc3.rbs +4 -0
- data/smoke/rbs-record.rb +17 -0
- data/smoke/rbs-record.rbs +4 -0
- data/smoke/rbs-tyvar.rb +18 -0
- data/smoke/rbs-tyvar.rbs +5 -0
- data/smoke/rbs-tyvar2.rb +20 -0
- data/smoke/rbs-tyvar2.rbs +9 -0
- data/smoke/rbs-tyvar3.rb +17 -0
- data/smoke/rbs-tyvar3.rbs +5 -0
- data/smoke/rbs-tyvar4.rb +36 -0
- data/smoke/rbs-tyvar5.rb +12 -0
- data/smoke/rbs-tyvar5.rbs +8 -0
- data/smoke/rest1.rb +1 -1
- data/smoke/rest2.rb +1 -1
- data/smoke/rest3.rb +1 -1
- data/smoke/rest5.rb +1 -1
- data/smoke/rest6.rb +1 -1
- data/smoke/retry1.rb +1 -1
- data/smoke/return.rb +1 -1
- data/smoke/singleton_method.rb +3 -0
- data/smoke/step.rb +1 -1
- data/smoke/struct.rb +4 -3
- data/smoke/struct3.rb +14 -0
- data/smoke/symbol-proc.rb +24 -0
- data/smoke/uninitialize-var.rb +12 -0
- data/smoke/user-demo.rb +15 -0
- data/smoke/wrong-extend.rb +1 -0
- data/typeprof.gemspec +4 -2
- metadata +53 -6
- data/run.sh +0 -3
- data/smoke/variadic1.rb.notyet +0 -5
data/lib/typeprof/iseq.rb
CHANGED
|
@@ -32,6 +32,23 @@ module TypeProf
|
|
|
32
32
|
@name, @path, @absolute_path, @start_lineno, @type,
|
|
33
33
|
@locals, @fargs_format, catch_table, insns = *iseq
|
|
34
34
|
|
|
35
|
+
case @type
|
|
36
|
+
when :method, :block
|
|
37
|
+
if @fargs_format[:opt]
|
|
38
|
+
label = @fargs_format[:opt].last
|
|
39
|
+
i = insns.index(label) + 1
|
|
40
|
+
else
|
|
41
|
+
i = insns.find_index {|insn| insn.is_a?(Array) }
|
|
42
|
+
end
|
|
43
|
+
# skip keyword initialization
|
|
44
|
+
while insns[i][0] == :checkkeyword
|
|
45
|
+
raise if insns[i + 1][0] != :branchif
|
|
46
|
+
label = insns[i + 1][1]
|
|
47
|
+
i = insns.index(label) + 1
|
|
48
|
+
end
|
|
49
|
+
insns[i, 0] = [[:_iseq_body_start]]
|
|
50
|
+
end
|
|
51
|
+
|
|
35
52
|
@insns = []
|
|
36
53
|
@linenos = []
|
|
37
54
|
|
|
@@ -79,7 +96,7 @@ module TypeProf
|
|
|
79
96
|
nil
|
|
80
97
|
when Array
|
|
81
98
|
insn, *operands = e
|
|
82
|
-
operands = INSN_TABLE[insn].zip(operands).map do |type, operand|
|
|
99
|
+
operands = (INSN_TABLE[insn] || []).zip(operands).map do |type, operand|
|
|
83
100
|
case type
|
|
84
101
|
when "ISEQ"
|
|
85
102
|
operand && ISeq.new(operand)
|
|
@@ -200,6 +217,30 @@ module TypeProf
|
|
|
200
217
|
@insns[j + 1] = [:send_branch, [getlocal_operands, send_operands, branch_operands]]
|
|
201
218
|
end
|
|
202
219
|
|
|
220
|
+
# find a pattern: getlocal, dup, branch
|
|
221
|
+
(@insns.size - 2).times do |i|
|
|
222
|
+
next if branch_targets[i + 1] || branch_targets[i + 2]
|
|
223
|
+
insn0, getlocal_operands = @insns[i]
|
|
224
|
+
insn1, dup_operands = @insns[i + 1]
|
|
225
|
+
insn2, branch_operands = @insns[i + 2]
|
|
226
|
+
if insn0 == :getlocal && insn1 == :dup && insn2 == :branch && getlocal_operands[1] == 0
|
|
227
|
+
@insns[i ] = [:nop]
|
|
228
|
+
@insns[i + 1] = [:nop]
|
|
229
|
+
@insns[i + 2] = [:getlocal_dup_branch, [getlocal_operands, dup_operands, branch_operands]]
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# find a pattern: dup, branch
|
|
234
|
+
(@insns.size - 1).times do |i|
|
|
235
|
+
next if branch_targets[i + 1]
|
|
236
|
+
insn0, dup_operands = @insns[i]
|
|
237
|
+
insn1, branch_operands = @insns[i + 1]
|
|
238
|
+
if insn0 == :dup && insn1 == :branch
|
|
239
|
+
@insns[i ] = [:nop]
|
|
240
|
+
@insns[i + 1] = [:dup_branch, [dup_operands, branch_operands]]
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
203
244
|
# find a pattern: getlocal, branch
|
|
204
245
|
(@insns.size - 1).times do |i|
|
|
205
246
|
next if branch_targets[i + 1]
|
data/lib/typeprof/method.rb
CHANGED
|
@@ -11,101 +11,121 @@ module TypeProf
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def do_send(recv, mid, aargs, caller_ep, caller_env, scratch, &ctn)
|
|
14
|
+
recv = recv.base_type while recv.respond_to?(:base_type)
|
|
14
15
|
recv = scratch.globalize_type(recv, caller_env, caller_ep)
|
|
15
16
|
aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
locals = [Type.nil] * @iseq.locals.size
|
|
19
|
+
|
|
20
|
+
blk_ty, start_pcs = aargs.setup_formal_arguments(:method, locals, @iseq.fargs_format)
|
|
21
|
+
if blk_ty.is_a?(String)
|
|
22
|
+
scratch.error(caller_ep, blk_ty)
|
|
23
|
+
ctn[Type.any, caller_ep, caller_env]
|
|
24
|
+
return
|
|
25
|
+
end
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
nctx = Context.new(@iseq, @cref, mid)
|
|
28
|
+
callee_ep = ExecutionPoint.new(nctx, 0, nil)
|
|
29
|
+
nenv = Env.new(StaticEnv.new(recv, blk_ty, false), locals, [], Utils::HashWrapper.new({}))
|
|
30
|
+
alloc_site = AllocationSite.new(callee_ep)
|
|
31
|
+
locals.each_with_index do |ty, i|
|
|
32
|
+
alloc_site2 = alloc_site.add_id(i)
|
|
33
|
+
# nenv is top-level, so it is okay to call Type#localize directly
|
|
34
|
+
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
35
|
+
nenv = nenv.local_update(i, ty)
|
|
36
|
+
end
|
|
25
37
|
|
|
26
|
-
|
|
27
|
-
scratch.
|
|
38
|
+
start_pcs.each do |start_pc|
|
|
39
|
+
scratch.merge_env(ExecutionPoint.new(nctx, start_pc, @outer_ep), nenv)
|
|
28
40
|
end
|
|
41
|
+
|
|
42
|
+
scratch.add_iseq_method_call!(self, nctx)
|
|
43
|
+
scratch.add_callsite!(nctx, caller_ep, caller_env, &ctn)
|
|
29
44
|
end
|
|
30
45
|
|
|
31
|
-
def
|
|
46
|
+
def do_check_send(msig, recv, mid, ep, scratch)
|
|
47
|
+
lead_num = @iseq.fargs_format[:lead_num] || 0
|
|
48
|
+
post_num = @iseq.fargs_format[:post_num] || 0
|
|
49
|
+
rest_start = @iseq.fargs_format[:rest_start]
|
|
50
|
+
opt = @iseq.fargs_format[:opt] || [0]
|
|
51
|
+
|
|
52
|
+
# TODO: check keywords
|
|
53
|
+
if rest_start
|
|
54
|
+
# almost ok
|
|
55
|
+
else
|
|
56
|
+
if msig.lead_tys.size + msig.post_tys.size < lead_num + post_num
|
|
57
|
+
scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at least %d arguments" % [msig.lead_tys.size + msig.post_tys.size, lead_num + post_num])
|
|
58
|
+
return
|
|
59
|
+
end
|
|
60
|
+
if msig.lead_tys.size + msig.opt_tys.size + msig.post_tys.size > lead_num + opt.size - 1 + post_num
|
|
61
|
+
scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at most %d arguments" % [msig.lead_tys.size + msig.opt_tys.size + msig.post_tys.size, lead_num + opt.size - 1 + post_num])
|
|
62
|
+
return
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
32
66
|
lead_num = @iseq.fargs_format[:lead_num] || 0
|
|
33
67
|
post_start = @iseq.fargs_format[:post_start]
|
|
34
68
|
rest_start = @iseq.fargs_format[:rest_start]
|
|
35
69
|
kw_start = @iseq.fargs_format[:kwbits]
|
|
36
70
|
kw_start -= @iseq.fargs_format[:keyword].size if kw_start
|
|
71
|
+
kw_rest = @iseq.fargs_format[:kwrest]
|
|
37
72
|
block_start = @iseq.fargs_format[:block_start]
|
|
38
73
|
|
|
39
|
-
# XXX: need to check .rbs
|
|
74
|
+
# XXX: need to check .rbs msig and .rb fargs
|
|
40
75
|
|
|
41
76
|
ctx = Context.new(@iseq, @cref, mid)
|
|
42
|
-
callee_ep = ExecutionPoint.new(ctx,
|
|
77
|
+
callee_ep = ExecutionPoint.new(ctx, 0, nil)
|
|
43
78
|
|
|
44
79
|
locals = [Type.nil] * @iseq.locals.size
|
|
45
|
-
nenv = Env.new(StaticEnv.new(recv,
|
|
80
|
+
nenv = Env.new(StaticEnv.new(recv, msig.blk_ty, false), locals, [], Utils::HashWrapper.new({}))
|
|
46
81
|
alloc_site = AllocationSite.new(callee_ep)
|
|
47
82
|
idx = 0
|
|
48
|
-
|
|
83
|
+
msig.lead_tys.each_with_index do |ty, i|
|
|
49
84
|
alloc_site2 = alloc_site.add_id(idx += 1)
|
|
50
85
|
# nenv is top-level, so it is okay to call Type#localize directly
|
|
51
86
|
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
52
87
|
nenv = nenv.local_update(i, ty)
|
|
53
88
|
end
|
|
54
|
-
if
|
|
55
|
-
|
|
89
|
+
if msig.opt_tys
|
|
90
|
+
msig.opt_tys.each_with_index do |ty, i|
|
|
56
91
|
alloc_site2 = alloc_site.add_id(idx += 1)
|
|
57
92
|
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
58
93
|
nenv = nenv.local_update(lead_num + i, ty)
|
|
59
94
|
end
|
|
60
95
|
end
|
|
61
|
-
if
|
|
96
|
+
if msig.rest_ty
|
|
62
97
|
alloc_site2 = alloc_site.add_id(idx += 1)
|
|
63
|
-
ty = Type::Array.new(Type::Array::Elements.new([],
|
|
98
|
+
ty = Type::Array.new(Type::Array::Elements.new([], msig.rest_ty), Type::Instance.new(Type::Builtin[:ary]))
|
|
64
99
|
nenv, rest_ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
65
100
|
nenv = nenv.local_update(rest_start, rest_ty)
|
|
66
101
|
end
|
|
67
|
-
if
|
|
68
|
-
|
|
102
|
+
if msig.post_tys
|
|
103
|
+
msig.post_tys.each_with_index do |ty, i|
|
|
69
104
|
alloc_site2 = alloc_site.add_id(idx += 1)
|
|
70
105
|
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
71
106
|
nenv = nenv.local_update(post_start + i, ty)
|
|
72
107
|
end
|
|
73
108
|
end
|
|
74
|
-
if
|
|
75
|
-
|
|
76
|
-
alloc_site2 = alloc_site.add_id(
|
|
109
|
+
if msig.kw_tys
|
|
110
|
+
msig.kw_tys.each_with_index do |(_, key, ty), i|
|
|
111
|
+
alloc_site2 = alloc_site.add_id(key)
|
|
77
112
|
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
78
113
|
nenv = nenv.local_update(kw_start + i, ty)
|
|
79
114
|
end
|
|
80
115
|
end
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def do_check_send_core(fargs, recv, mid, ep, scratch)
|
|
90
|
-
lead_num = @iseq.fargs_format[:lead_num] || 0
|
|
91
|
-
post_num = @iseq.fargs_format[:post_num] || 0
|
|
92
|
-
rest_start = @iseq.fargs_format[:rest_start]
|
|
93
|
-
opt = @iseq.fargs_format[:opt] || [0]
|
|
116
|
+
if msig.kw_rest_ty
|
|
117
|
+
ty = msig.kw_rest_ty
|
|
118
|
+
alloc_site2 = alloc_site.add_id(:**)
|
|
119
|
+
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
120
|
+
nenv = nenv.local_update(kw_rest, ty)
|
|
121
|
+
end
|
|
122
|
+
nenv = nenv.local_update(block_start, msig.blk_ty) if block_start
|
|
94
123
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
# almost ok
|
|
98
|
-
else
|
|
99
|
-
if fargs.lead_tys.size + fargs.post_tys.size < lead_num + post_num
|
|
100
|
-
scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at least %d arguments" % [fargs.lead_tys.size + fargs.post_tys.size, lead_num + post_num])
|
|
101
|
-
return
|
|
102
|
-
end
|
|
103
|
-
if fargs.lead_tys.size + fargs.opt_tys.size + fargs.post_tys.size > lead_num + opt.size - 1 + post_num
|
|
104
|
-
scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at most %d arguments" % [fargs.lead_tys.size + fargs.opt_tys.size + fargs.post_tys.size, lead_num + opt.size - 1 + post_num])
|
|
105
|
-
return
|
|
106
|
-
end
|
|
124
|
+
opt.each do |start_pc|
|
|
125
|
+
scratch.merge_env(ExecutionPoint.new(ctx, start_pc, nil), nenv)
|
|
107
126
|
end
|
|
108
|
-
|
|
127
|
+
|
|
128
|
+
ctx
|
|
109
129
|
end
|
|
110
130
|
end
|
|
111
131
|
|
|
@@ -141,8 +161,8 @@ module TypeProf
|
|
|
141
161
|
end
|
|
142
162
|
|
|
143
163
|
class TypedMethodDef < MethodDef
|
|
144
|
-
def initialize(
|
|
145
|
-
@
|
|
164
|
+
def initialize(sig_rets, rbs_source) # sig_rets: Array<[MethodSignature, (return)Type]>
|
|
165
|
+
@sig_rets = sig_rets
|
|
146
166
|
@rbs_source = rbs_source
|
|
147
167
|
end
|
|
148
168
|
|
|
@@ -152,45 +172,78 @@ module TypeProf
|
|
|
152
172
|
recv = scratch.globalize_type(recv_orig, caller_env, caller_ep)
|
|
153
173
|
found = false
|
|
154
174
|
aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
|
|
155
|
-
@
|
|
175
|
+
@sig_rets.each do |msig, ret_ty|
|
|
156
176
|
ncaller_env = caller_env
|
|
157
|
-
#pp [mid, aargs,
|
|
158
|
-
# XXX: support self type in
|
|
159
|
-
subst =
|
|
160
|
-
next unless
|
|
161
|
-
|
|
177
|
+
#pp [mid, aargs, msig]
|
|
178
|
+
# XXX: support self type in msig
|
|
179
|
+
subst = aargs.consistent_with_method_signature?(msig)
|
|
180
|
+
next unless subst
|
|
181
|
+
# need to check self tyvar?
|
|
182
|
+
subst[Type::Var.new(:self)] = recv
|
|
183
|
+
case
|
|
184
|
+
when recv.is_a?(Type::Cell) && recv_orig.is_a?(Type::LocalCell)
|
|
185
|
+
tyvars = recv.base_type.klass.type_params.map {|name,| Type::Var.new(name) }
|
|
186
|
+
tyvars.each_with_index do |tyvar, idx|
|
|
187
|
+
ty = subst[tyvar]
|
|
188
|
+
if ty
|
|
189
|
+
ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
|
|
190
|
+
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
|
|
191
|
+
elems.update(idx, ty)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
tyvars.zip(recv.elems.elems) do |tyvar, elem|
|
|
196
|
+
if subst[tyvar]
|
|
197
|
+
subst[tyvar] = subst[tyvar].union(elem)
|
|
198
|
+
else
|
|
199
|
+
subst[tyvar] = elem
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
when recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
|
|
162
203
|
tyvar_elem = Type::Var.new(:Elem)
|
|
163
204
|
if subst[tyvar_elem]
|
|
164
205
|
ty = subst[tyvar_elem]
|
|
165
|
-
alloc_site = AllocationSite.new(caller_ep).add_id(self)
|
|
166
206
|
ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
|
|
167
|
-
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id) do |elems|
|
|
207
|
+
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
|
|
168
208
|
elems.update(nil, ty)
|
|
169
209
|
end
|
|
170
210
|
end
|
|
171
211
|
subst.merge!({ tyvar_elem => recv.elems.squash })
|
|
172
|
-
|
|
212
|
+
when recv.is_a?(Type::Hash) && recv_orig.is_a?(Type::LocalHash)
|
|
173
213
|
tyvar_k = Type::Var.new(:K)
|
|
174
214
|
tyvar_v = Type::Var.new(:V)
|
|
175
|
-
|
|
176
|
-
|
|
215
|
+
k_ty0, v_ty0 = recv.elems.squash
|
|
216
|
+
if subst[tyvar_k] && subst[tyvar_v]
|
|
217
|
+
k_ty = subst[tyvar_k]
|
|
218
|
+
v_ty = subst[tyvar_v]
|
|
219
|
+
k_ty0 = k_ty0.union(k_ty)
|
|
220
|
+
v_ty0 = v_ty0.union(v_ty)
|
|
221
|
+
alloc_site = AllocationSite.new(caller_ep)
|
|
222
|
+
ncaller_env, k_ty = scratch.localize_type(k_ty, ncaller_env, caller_ep, alloc_site.add_id(:k))
|
|
223
|
+
ncaller_env, v_ty = scratch.localize_type(v_ty, ncaller_env, caller_ep, alloc_site.add_id(:v))
|
|
224
|
+
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
|
|
225
|
+
elems.update(k_ty, v_ty)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
177
228
|
# XXX: need to heuristically replace ret type Hash[K, V] with self, instead of conversative type?
|
|
178
|
-
subst.merge!({ tyvar_k =>
|
|
229
|
+
subst.merge!({ tyvar_k => k_ty0, tyvar_v => v_ty0 })
|
|
179
230
|
end
|
|
180
|
-
ret_ty = ret_ty.substitute(subst, Config.options[:type_depth_limit])
|
|
181
231
|
found = true
|
|
182
|
-
if aargs.blk_ty.is_a?(Type::
|
|
232
|
+
if aargs.blk_ty.is_a?(Type::Proc)
|
|
233
|
+
#raise NotImplementedError unless aargs.blk_ty.block_body.is_a?(ISeqBlock) # XXX
|
|
183
234
|
dummy_ctx = TypedContext.new(caller_ep, mid)
|
|
184
235
|
dummy_ep = ExecutionPoint.new(dummy_ctx, -1, caller_ep)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
236
|
+
s_recv = recv
|
|
237
|
+
s_recv = s_recv.base_type while s_recv.respond_to?(:base_type)
|
|
238
|
+
dummy_env = Env.new(StaticEnv.new(s_recv, msig.blk_ty, false), [], [], Utils::HashWrapper.new({}))
|
|
239
|
+
if msig.blk_ty.is_a?(Type::Proc)
|
|
240
|
+
scratch.add_callsite!(dummy_ctx, caller_ep, ncaller_env, &ctn)
|
|
241
|
+
nfargs = msig.blk_ty.block_body.msig
|
|
189
242
|
alloc_site = AllocationSite.new(caller_ep).add_id(self)
|
|
190
243
|
nlead_tys = (nfargs.lead_tys + nfargs.opt_tys).map.with_index do |ty, i|
|
|
191
244
|
if recv.is_a?(Type::Array)
|
|
192
245
|
tyvar_elem = Type::Var.new(:Elem)
|
|
193
|
-
ty = ty.substitute(subst.merge({ tyvar_elem => recv.elems.
|
|
246
|
+
ty = ty.substitute(subst.merge({ tyvar_elem => recv.elems.squash_or_any }), Config.options[:type_depth_limit])
|
|
194
247
|
else
|
|
195
248
|
ty = ty.substitute(subst, Config.options[:type_depth_limit])
|
|
196
249
|
end
|
|
@@ -200,15 +253,57 @@ module TypeProf
|
|
|
200
253
|
ty
|
|
201
254
|
end
|
|
202
255
|
0.upto(nfargs.opt_tys.size) do |n|
|
|
203
|
-
naargs = ActualArguments.new(nlead_tys[0, nfargs.lead_tys.size + n], nil,
|
|
204
|
-
scratch.do_invoke_block(
|
|
205
|
-
subst2 =
|
|
206
|
-
if
|
|
207
|
-
|
|
256
|
+
naargs = ActualArguments.new(nlead_tys[0, nfargs.lead_tys.size + n], nil, {}, Type.nil) # XXX: support block to block?
|
|
257
|
+
scratch.do_invoke_block(aargs.blk_ty, naargs, dummy_ep, dummy_env) do |blk_ret_ty, _ep, _env|
|
|
258
|
+
subst2 = Type.match?(blk_ret_ty, msig.blk_ty.block_body.ret_ty)
|
|
259
|
+
if subst2
|
|
260
|
+
subst2 = Type.merge_substitution(subst, subst2)
|
|
261
|
+
case
|
|
262
|
+
when recv.is_a?(Type::Cell) && recv_orig.is_a?(Type::LocalCell)
|
|
263
|
+
tyvars = recv.base_type.klass.type_params.map {|name,| Type::Var.new(name) }
|
|
264
|
+
tyvars.each_with_index do |tyvar, idx|
|
|
265
|
+
ty = subst2[tyvar]
|
|
266
|
+
if ty
|
|
267
|
+
ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
|
|
268
|
+
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
|
|
269
|
+
elems.update(idx, ty)
|
|
270
|
+
end
|
|
271
|
+
scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
tyvars.zip(recv.elems.elems) do |tyvar, elem|
|
|
275
|
+
if subst2[tyvar]
|
|
276
|
+
subst2[tyvar] = subst2[tyvar].union(elem)
|
|
277
|
+
else
|
|
278
|
+
subst2[tyvar] = elem
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
|
|
282
|
+
when recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
|
|
208
283
|
tyvar_elem = Type::Var.new(:Elem)
|
|
209
284
|
if subst2[tyvar_elem]
|
|
210
|
-
|
|
211
|
-
|
|
285
|
+
ty = subst2[tyvar_elem]
|
|
286
|
+
ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
|
|
287
|
+
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
|
|
288
|
+
elems.update(nil, ty)
|
|
289
|
+
end
|
|
290
|
+
scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
|
|
291
|
+
end
|
|
292
|
+
ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
|
|
293
|
+
when recv.is_a?(Type::Hash) && recv_orig.is_a?(Type::LocalHash)
|
|
294
|
+
tyvar_k = Type::Var.new(:K)
|
|
295
|
+
tyvar_v = Type::Var.new(:V)
|
|
296
|
+
k_ty0, v_ty0 = recv.elems.squash
|
|
297
|
+
if subst2[tyvar_k] && subst2[tyvar_v]
|
|
298
|
+
k_ty = subst2[tyvar_k]
|
|
299
|
+
v_ty = subst2[tyvar_v]
|
|
300
|
+
k_ty0 = k_ty0.union(k_ty)
|
|
301
|
+
v_ty0 = v_ty0.union(v_ty)
|
|
302
|
+
alloc_site = AllocationSite.new(caller_ep)
|
|
303
|
+
ncaller_env, k_ty = scratch.localize_type(k_ty, ncaller_env, caller_ep, alloc_site.add_id(:k))
|
|
304
|
+
ncaller_env, v_ty = scratch.localize_type(v_ty, ncaller_env, caller_ep, alloc_site.add_id(:v))
|
|
305
|
+
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
|
|
306
|
+
elems.update(k_ty, v_ty)
|
|
212
307
|
end
|
|
213
308
|
scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
|
|
214
309
|
end
|
|
@@ -223,10 +318,10 @@ module TypeProf
|
|
|
223
318
|
end
|
|
224
319
|
ret_ty = ret_ty.remove_type_vars
|
|
225
320
|
# XXX: check the return type from the block
|
|
226
|
-
# sig.blk_ty.ret_ty.eql?(_ret_ty) ???
|
|
227
|
-
scratch.
|
|
321
|
+
# sig.blk_ty.block_body.ret_ty.eql?(_ret_ty) ???
|
|
322
|
+
scratch.add_return_value!(dummy_ctx, ret_ty)
|
|
228
323
|
end
|
|
229
|
-
# scratch.
|
|
324
|
+
# scratch.add_return_value!(dummy_ctx, ret_ty) ?
|
|
230
325
|
# This makes `def foo; 1.times { return "str" }; end` return Integer|String
|
|
231
326
|
end
|
|
232
327
|
else
|
|
@@ -236,6 +331,7 @@ module TypeProf
|
|
|
236
331
|
ctn[ret_ty, caller_ep, ncaller_env]
|
|
237
332
|
end
|
|
238
333
|
else
|
|
334
|
+
ret_ty = ret_ty.substitute(subst, Config.options[:type_depth_limit])
|
|
239
335
|
ret_ty = ret_ty.remove_type_vars
|
|
240
336
|
ctn[ret_ty, caller_ep, ncaller_env]
|
|
241
337
|
end
|
|
@@ -249,8 +345,8 @@ module TypeProf
|
|
|
249
345
|
|
|
250
346
|
def do_match_iseq_mdef(iseq_mdef, recv, mid, env, ep, scratch)
|
|
251
347
|
recv = scratch.globalize_type(recv, env, ep)
|
|
252
|
-
@
|
|
253
|
-
iseq_mdef.
|
|
348
|
+
@sig_rets.each do |msig, _ret_ty|
|
|
349
|
+
iseq_mdef.do_check_send(msig, recv, mid, ep, scratch)
|
|
254
350
|
end
|
|
255
351
|
end
|
|
256
352
|
end
|
data/lib/typeprof/type.rb
CHANGED
|
@@ -3,7 +3,7 @@ module TypeProf
|
|
|
3
3
|
include Utils::StructuralEquality
|
|
4
4
|
|
|
5
5
|
def initialize
|
|
6
|
-
raise "cannot
|
|
6
|
+
raise "cannot instantiate abstract type"
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
Builtin = {}
|
|
@@ -20,16 +20,69 @@ module TypeProf
|
|
|
20
20
|
self
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
def self.match?(ty1, ty2)
|
|
24
|
+
# both ty1 and ty2 should be global
|
|
25
|
+
# ty1 is always concrete; it should not have type variables
|
|
26
|
+
# ty2 might be abstract; it may have type variables
|
|
27
|
+
case ty2
|
|
28
|
+
when Type::Var
|
|
29
|
+
{ ty2 => ty1 }
|
|
30
|
+
when Type::Any
|
|
31
|
+
{}
|
|
27
32
|
when Type::Union
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
subst = nil
|
|
34
|
+
ty2.each_child_global do |ty2|
|
|
35
|
+
# this is very conservative to create subst:
|
|
36
|
+
# Type.match?( int | str, int | X) creates { X => int | str } but should be { X => str }???
|
|
37
|
+
subst2 = Type.match?(ty1, ty2)
|
|
38
|
+
next unless subst2
|
|
39
|
+
subst = Type.merge_substitution(subst, subst2)
|
|
30
40
|
end
|
|
41
|
+
subst
|
|
31
42
|
else
|
|
32
|
-
|
|
43
|
+
case ty1
|
|
44
|
+
when Type::Var then raise "should not occur"
|
|
45
|
+
when Type::Any
|
|
46
|
+
subst = {}
|
|
47
|
+
ty2.each_free_type_variable do |tyvar|
|
|
48
|
+
subst[tyvar] = Type.any
|
|
49
|
+
end
|
|
50
|
+
subst
|
|
51
|
+
when Type::Union
|
|
52
|
+
subst = nil
|
|
53
|
+
ty1.each_child_global do |ty1|
|
|
54
|
+
subst2 = Type.match?(ty1, ty2)
|
|
55
|
+
next unless subst2
|
|
56
|
+
subst = Type.merge_substitution(subst, subst2)
|
|
57
|
+
end
|
|
58
|
+
subst
|
|
59
|
+
else
|
|
60
|
+
if ty2.is_a?(Type::ContainerType)
|
|
61
|
+
# ty2 may have type variables
|
|
62
|
+
return nil if ty1.class != ty2.class
|
|
63
|
+
ty1.match?(ty2)
|
|
64
|
+
elsif ty1.is_a?(Type::ContainerType)
|
|
65
|
+
nil
|
|
66
|
+
else
|
|
67
|
+
ty1.consistent?(ty2) ? {} : nil
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.merge_substitution(subst1, subst2)
|
|
74
|
+
if subst1
|
|
75
|
+
subst1 = subst1.dup
|
|
76
|
+
subst2.each do |tyvar, ty|
|
|
77
|
+
if subst1[tyvar]
|
|
78
|
+
subst1[tyvar] = subst1[tyvar].union(ty)
|
|
79
|
+
else
|
|
80
|
+
subst1[tyvar] = ty
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
subst1
|
|
84
|
+
else
|
|
85
|
+
subst2
|
|
33
86
|
end
|
|
34
87
|
end
|
|
35
88
|
|
|
@@ -41,6 +94,9 @@ module TypeProf
|
|
|
41
94
|
yield self
|
|
42
95
|
end
|
|
43
96
|
|
|
97
|
+
def each_free_type_variable
|
|
98
|
+
end
|
|
99
|
+
|
|
44
100
|
def union(other)
|
|
45
101
|
return self if self == other # fastpath
|
|
46
102
|
|
|
@@ -118,10 +174,8 @@ module TypeProf
|
|
|
118
174
|
nil
|
|
119
175
|
end
|
|
120
176
|
|
|
121
|
-
def consistent?(
|
|
122
|
-
|
|
123
|
-
other.add_subst!(self, subst) if other.is_a?(Type::Var)
|
|
124
|
-
true
|
|
177
|
+
def consistent?(_other)
|
|
178
|
+
raise "should not be called"
|
|
125
179
|
end
|
|
126
180
|
|
|
127
181
|
def substitute(_subst, _depth)
|
|
@@ -129,6 +183,17 @@ module TypeProf
|
|
|
129
183
|
end
|
|
130
184
|
end
|
|
131
185
|
|
|
186
|
+
class Void < Any
|
|
187
|
+
def inspect
|
|
188
|
+
"Type::Void"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def screen_name(scratch)
|
|
192
|
+
"void"
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
|
|
132
197
|
class Union < Type
|
|
133
198
|
def initialize(tys, elems)
|
|
134
199
|
raise unless tys.is_a?(Utils::Set)
|
|
@@ -137,7 +202,7 @@ module TypeProf
|
|
|
137
202
|
# invariant check
|
|
138
203
|
local = nil
|
|
139
204
|
tys.each do |ty|
|
|
140
|
-
raise unless ty.is_a?(Type)
|
|
205
|
+
raise ty.inspect unless ty.is_a?(Type)
|
|
141
206
|
local = true if ty.is_a?(LocalArray) || ty.is_a?(LocalHash)
|
|
142
207
|
end
|
|
143
208
|
raise if local && elems
|
|
@@ -145,6 +210,12 @@ module TypeProf
|
|
|
145
210
|
@elems = elems
|
|
146
211
|
end
|
|
147
212
|
|
|
213
|
+
def each_free_type_variable(&blk)
|
|
214
|
+
each_child_global do |ty|
|
|
215
|
+
ty.each_free_type_variable(&blk)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
148
219
|
def limit_size(limit)
|
|
149
220
|
return Type.any if limit <= 0
|
|
150
221
|
tys = Utils::Set[]
|
|
@@ -215,12 +286,15 @@ module TypeProf
|
|
|
215
286
|
bool = true
|
|
216
287
|
end
|
|
217
288
|
types.delete(Type.any) unless Config.options[:pedantic_output]
|
|
289
|
+
proc_tys, types = types.partition {|ty| ty.is_a?(Proc) }
|
|
218
290
|
types = types.map {|ty| ty.screen_name(scratch) }
|
|
291
|
+
types << scratch.show_proc_signature(proc_tys) unless proc_tys.empty?
|
|
219
292
|
types << "bool" if bool
|
|
220
293
|
types = types.sort
|
|
221
294
|
if optional
|
|
222
|
-
|
|
223
|
-
|
|
295
|
+
case types.size
|
|
296
|
+
when 0 then "nil"
|
|
297
|
+
when 1 then types.first + "?"
|
|
224
298
|
else
|
|
225
299
|
"(#{ types.join (" | ") })?"
|
|
226
300
|
end
|
|
@@ -257,48 +331,20 @@ module TypeProf
|
|
|
257
331
|
def localize(env, alloc_site, depth)
|
|
258
332
|
return env, Type.any if depth <= 0
|
|
259
333
|
tys = @types.map do |ty|
|
|
260
|
-
|
|
261
|
-
env, ty2 = ty.localize(env, alloc_site2, depth - 1)
|
|
334
|
+
env, ty2 = ty.localize(env, alloc_site, depth - 1)
|
|
262
335
|
ty2
|
|
263
336
|
end
|
|
264
337
|
@elems&.each do |(container_kind, base_type), elems|
|
|
265
338
|
ty = container_kind.new(elems, base_type)
|
|
266
|
-
|
|
267
|
-
env, ty = ty.localize(env, alloc_site2, depth - 1)
|
|
339
|
+
env, ty = ty.localize(env, alloc_site, depth - 1)
|
|
268
340
|
tys = tys.add(ty)
|
|
269
341
|
end
|
|
270
342
|
ty = Union.new(tys, nil).normalize
|
|
271
343
|
return env, ty
|
|
272
344
|
end
|
|
273
345
|
|
|
274
|
-
def consistent?(
|
|
275
|
-
|
|
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
|
|
346
|
+
def consistent?(_other)
|
|
347
|
+
raise "should not be called"
|
|
302
348
|
end
|
|
303
349
|
|
|
304
350
|
def substitute(subst, depth)
|
|
@@ -317,7 +363,7 @@ module TypeProf
|
|
|
317
363
|
elems = @elems&.to_h do |(container_kind, base_type), elems|
|
|
318
364
|
[[container_kind, base_type], elems.substitute(subst, depth - 1)]
|
|
319
365
|
end
|
|
320
|
-
ty = Union.new(tys, elems)
|
|
366
|
+
ty = Union.new(tys, elems).normalize
|
|
321
367
|
unions.each do |ty0|
|
|
322
368
|
ty = ty.union(ty0)
|
|
323
369
|
end
|
|
@@ -357,6 +403,10 @@ module TypeProf
|
|
|
357
403
|
"Var[#{ @name }]"
|
|
358
404
|
end
|
|
359
405
|
|
|
406
|
+
def each_free_type_variable
|
|
407
|
+
yield self
|
|
408
|
+
end
|
|
409
|
+
|
|
360
410
|
def substitute(subst, depth)
|
|
361
411
|
if subst[self]
|
|
362
412
|
subst[self].limit_size(depth)
|
|
@@ -365,7 +415,7 @@ module TypeProf
|
|
|
365
415
|
end
|
|
366
416
|
end
|
|
367
417
|
|
|
368
|
-
def consistent?(
|
|
418
|
+
def consistent?(_other)
|
|
369
419
|
raise "should not be called: #{ self }"
|
|
370
420
|
end
|
|
371
421
|
|
|
@@ -406,15 +456,8 @@ module TypeProf
|
|
|
406
456
|
scratch.get_method(self, true, mid)
|
|
407
457
|
end
|
|
408
458
|
|
|
409
|
-
def consistent?(other
|
|
459
|
+
def consistent?(other)
|
|
410
460
|
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
461
|
when Type::Class
|
|
419
462
|
ty = self
|
|
420
463
|
loop do
|
|
@@ -465,17 +508,10 @@ module TypeProf
|
|
|
465
508
|
scratch.get_method(@klass, false, mid)
|
|
466
509
|
end
|
|
467
510
|
|
|
468
|
-
def consistent?(other
|
|
511
|
+
def consistent?(other)
|
|
469
512
|
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
513
|
when Type::Instance
|
|
478
|
-
@klass.consistent?(other.klass
|
|
514
|
+
@klass.consistent?(other.klass)
|
|
479
515
|
when Type::Class
|
|
480
516
|
return true if @klass == Type::Builtin[:obj] || @klass == Type::Builtin[:class] || @klass == Type::Builtin[:module]
|
|
481
517
|
return false
|
|
@@ -489,6 +525,7 @@ module TypeProf
|
|
|
489
525
|
end
|
|
490
526
|
end
|
|
491
527
|
|
|
528
|
+
# This is an internal object in MRI, so a user program cannot create this object explicitly
|
|
492
529
|
class ISeq < Type
|
|
493
530
|
def initialize(iseq)
|
|
494
531
|
@iseq = iseq
|
|
@@ -505,66 +542,53 @@ module TypeProf
|
|
|
505
542
|
end
|
|
506
543
|
end
|
|
507
544
|
|
|
508
|
-
class
|
|
509
|
-
def initialize(
|
|
510
|
-
@
|
|
511
|
-
@ep = ep
|
|
512
|
-
@type = type
|
|
545
|
+
class Proc < Type
|
|
546
|
+
def initialize(block_body, base_type)
|
|
547
|
+
@block_body, @base_type = block_body, base_type
|
|
513
548
|
end
|
|
514
549
|
|
|
515
|
-
attr_reader :
|
|
516
|
-
|
|
517
|
-
def inspect
|
|
518
|
-
"#<ISeqProc>"
|
|
519
|
-
end
|
|
550
|
+
attr_reader :block_body, :base_type
|
|
520
551
|
|
|
521
|
-
def
|
|
522
|
-
|
|
552
|
+
def consistent?(other)
|
|
553
|
+
case other
|
|
554
|
+
when Type::Proc
|
|
555
|
+
@block_body.consistent?(other.block_body)
|
|
556
|
+
else
|
|
557
|
+
self == other
|
|
558
|
+
end
|
|
523
559
|
end
|
|
524
560
|
|
|
525
561
|
def get_method(mid, scratch)
|
|
526
|
-
@
|
|
562
|
+
@base_type.get_method(mid, scratch)
|
|
527
563
|
end
|
|
528
564
|
|
|
529
|
-
def substitute(
|
|
530
|
-
|
|
531
|
-
end
|
|
532
|
-
end
|
|
533
|
-
|
|
534
|
-
class TypedProc < Type
|
|
535
|
-
def initialize(fargs, ret_ty, type)
|
|
536
|
-
@fargs = fargs
|
|
537
|
-
@ret_ty = ret_ty
|
|
538
|
-
@type = type
|
|
565
|
+
def substitute(subst, depth)
|
|
566
|
+
Proc.new(@block_body.substitute(subst, depth), @base_type)
|
|
539
567
|
end
|
|
540
568
|
|
|
541
|
-
attr_reader :fargs, :ret_ty
|
|
542
|
-
|
|
543
569
|
def screen_name(scratch)
|
|
544
|
-
|
|
570
|
+
scratch.show_proc_signature([self])
|
|
545
571
|
end
|
|
546
572
|
end
|
|
547
573
|
|
|
548
574
|
class Symbol < Type
|
|
549
|
-
def initialize(sym,
|
|
575
|
+
def initialize(sym, base_type)
|
|
550
576
|
@sym = sym
|
|
551
|
-
@
|
|
577
|
+
@base_type = base_type
|
|
552
578
|
end
|
|
553
579
|
|
|
554
|
-
attr_reader :sym, :
|
|
580
|
+
attr_reader :sym, :base_type
|
|
555
581
|
|
|
556
582
|
def inspect
|
|
557
|
-
"Type::Symbol[#{ @sym ? @sym.inspect : "(dynamic symbol)" }, #{ @
|
|
583
|
+
"Type::Symbol[#{ @sym ? @sym.inspect : "(dynamic symbol)" }, #{ @base_type.inspect }]"
|
|
558
584
|
end
|
|
559
585
|
|
|
560
|
-
def consistent?(other
|
|
586
|
+
def consistent?(other)
|
|
561
587
|
case other
|
|
562
|
-
when Var
|
|
563
|
-
other.add_subst!(self, subst)
|
|
564
588
|
when Symbol
|
|
565
589
|
@sym == other.sym
|
|
566
590
|
else
|
|
567
|
-
@
|
|
591
|
+
@base_type.consistent?(other)
|
|
568
592
|
end
|
|
569
593
|
end
|
|
570
594
|
|
|
@@ -572,12 +596,12 @@ module TypeProf
|
|
|
572
596
|
if @sym
|
|
573
597
|
@sym.inspect
|
|
574
598
|
else
|
|
575
|
-
@
|
|
599
|
+
@base_type.screen_name(scratch)
|
|
576
600
|
end
|
|
577
601
|
end
|
|
578
602
|
|
|
579
603
|
def get_method(mid, scratch)
|
|
580
|
-
@
|
|
604
|
+
@base_type.get_method(mid, scratch)
|
|
581
605
|
end
|
|
582
606
|
|
|
583
607
|
def substitute(_subst, _depth)
|
|
@@ -585,33 +609,33 @@ module TypeProf
|
|
|
585
609
|
end
|
|
586
610
|
end
|
|
587
611
|
|
|
588
|
-
# local
|
|
612
|
+
# A local type
|
|
589
613
|
class Literal < Type
|
|
590
|
-
def initialize(lit,
|
|
614
|
+
def initialize(lit, base_type)
|
|
591
615
|
@lit = lit
|
|
592
|
-
@
|
|
616
|
+
@base_type = base_type
|
|
593
617
|
end
|
|
594
618
|
|
|
595
|
-
attr_reader :lit, :
|
|
619
|
+
attr_reader :lit, :base_type
|
|
596
620
|
|
|
597
621
|
def inspect
|
|
598
|
-
"Type::Literal[#{ @lit.inspect }, #{ @
|
|
622
|
+
"Type::Literal[#{ @lit.inspect }, #{ @base_type.inspect }]"
|
|
599
623
|
end
|
|
600
624
|
|
|
601
625
|
def screen_name(scratch)
|
|
602
|
-
@
|
|
626
|
+
@base_type.screen_name(scratch) + "<#{ @lit.inspect }>"
|
|
603
627
|
end
|
|
604
628
|
|
|
605
629
|
def globalize(_env, _visited, _depth)
|
|
606
|
-
@
|
|
630
|
+
@base_type
|
|
607
631
|
end
|
|
608
632
|
|
|
609
633
|
def get_method(mid, scratch)
|
|
610
|
-
@
|
|
634
|
+
@base_type.get_method(mid, scratch)
|
|
611
635
|
end
|
|
612
636
|
|
|
613
|
-
def consistent?(
|
|
614
|
-
|
|
637
|
+
def consistent?(_other)
|
|
638
|
+
raise "should not called"
|
|
615
639
|
end
|
|
616
640
|
end
|
|
617
641
|
|
|
@@ -736,66 +760,74 @@ module TypeProf
|
|
|
736
760
|
end
|
|
737
761
|
end
|
|
738
762
|
|
|
739
|
-
|
|
740
|
-
class FormalArguments
|
|
763
|
+
class Signature
|
|
741
764
|
include Utils::StructuralEquality
|
|
742
765
|
|
|
743
|
-
def initialize(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
744
|
-
@lead_tys = lead_tys
|
|
745
|
-
@opt_tys = opt_tys
|
|
746
|
-
@rest_ty = rest_ty
|
|
747
|
-
@post_tys = post_tys
|
|
748
|
-
@kw_tys = kw_tys
|
|
749
|
-
kw_tys.each {|a| raise if a.size != 3 } if kw_tys
|
|
750
|
-
@kw_rest_ty = kw_rest_ty
|
|
751
|
-
@blk_ty = blk_ty
|
|
752
|
-
end
|
|
753
|
-
|
|
754
|
-
attr_reader :lead_tys, :opt_tys, :rest_ty, :post_tys, :kw_tys, :kw_rest_ty, :blk_ty
|
|
755
|
-
|
|
756
|
-
def consistent?(fargs, subst)
|
|
757
|
-
warn "used?"
|
|
758
|
-
return false if @lead_tys.size != fargs.lead_tys.size
|
|
759
|
-
return false unless @lead_tys.zip(fargs.lead_tys).all? {|ty1, ty2| ty1.consistent?(ty2, subst) }
|
|
760
|
-
return false if (@opt_tys || []) != (fargs.opt_tys || []) # ??
|
|
761
|
-
if @rest_ty
|
|
762
|
-
return false unless @rest_ty.consistent?(fargs.rest_ty, subst)
|
|
763
|
-
end
|
|
764
|
-
if @post_tys
|
|
765
|
-
return false if @post_tys.size != fargs.post_tys.size
|
|
766
|
-
return false unless @post_tys.zip(fargs.post_tys).all? {|ty1, ty2| ty1.consistent?(ty2, subst) }
|
|
767
|
-
end
|
|
768
|
-
return false if @kw_tys.size != fargs.kw_tys.size
|
|
769
|
-
return false unless @kw_tys.zip(fargs.kw_tys).all? {|(_, ty1), (_, ty2)| ty1.consistent?(ty2, subst) }
|
|
770
|
-
if @kw_rest_ty
|
|
771
|
-
return false unless @kw_rest_ty.consistent?(fargs.kw_rest_ty, subst)
|
|
772
|
-
end
|
|
773
|
-
# intentionally skip blk_ty
|
|
774
|
-
true
|
|
775
|
-
end
|
|
776
|
-
|
|
777
766
|
def screen_name(scratch)
|
|
778
|
-
|
|
767
|
+
str = @lead_tys.map {|ty| ty.screen_name(scratch) }
|
|
779
768
|
if @opt_tys
|
|
780
|
-
|
|
769
|
+
str += @opt_tys.map {|ty| "?" + ty.screen_name(scratch) }
|
|
781
770
|
end
|
|
782
771
|
if @rest_ty
|
|
783
|
-
|
|
772
|
+
str << ("*" + @rest_ty.screen_name(scratch))
|
|
784
773
|
end
|
|
785
774
|
if @post_tys
|
|
786
|
-
|
|
775
|
+
str += @post_tys.map {|ty| ty.screen_name(scratch) }
|
|
787
776
|
end
|
|
788
777
|
if @kw_tys
|
|
789
778
|
@kw_tys.each do |req, sym, ty|
|
|
790
779
|
opt = req ? "" : "?"
|
|
791
|
-
|
|
780
|
+
str << "#{ opt }#{ sym }: #{ ty.screen_name(scratch) }"
|
|
792
781
|
end
|
|
793
782
|
end
|
|
794
783
|
if @kw_rest_ty
|
|
795
|
-
|
|
784
|
+
str << ("**" + @kw_rest_ty.screen_name(scratch))
|
|
785
|
+
end
|
|
786
|
+
str = str.empty? ? "" : "(#{ str.join(", ") })"
|
|
787
|
+
|
|
788
|
+
optional = false
|
|
789
|
+
blks = []
|
|
790
|
+
@blk_ty.each_child_global do |ty|
|
|
791
|
+
if ty.is_a?(Type::Proc)
|
|
792
|
+
blks << ty
|
|
793
|
+
else
|
|
794
|
+
# XXX: how should we handle types other than Type.nil
|
|
795
|
+
optional = true
|
|
796
|
+
end
|
|
796
797
|
end
|
|
797
|
-
|
|
798
|
-
|
|
798
|
+
if blks != []
|
|
799
|
+
str << " " if str != ""
|
|
800
|
+
str << "?" if optional
|
|
801
|
+
str << scratch.show_block_signature(blks)
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
str
|
|
805
|
+
end
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
class MethodSignature < Signature
|
|
809
|
+
def initialize(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
810
|
+
@lead_tys = lead_tys
|
|
811
|
+
@opt_tys = opt_tys
|
|
812
|
+
@rest_ty = rest_ty
|
|
813
|
+
@post_tys = post_tys
|
|
814
|
+
@kw_tys = kw_tys
|
|
815
|
+
kw_tys.each {|a| raise if a.size != 3 } if kw_tys
|
|
816
|
+
@kw_rest_ty = kw_rest_ty
|
|
817
|
+
@blk_ty = blk_ty
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
attr_reader :lead_tys, :opt_tys, :rest_ty, :post_tys, :kw_tys, :kw_rest_ty, :blk_ty
|
|
821
|
+
|
|
822
|
+
def substitute(subst, depth)
|
|
823
|
+
lead_tys = @lead_tys.map {|ty| ty.substitute(subst, depth - 1) }
|
|
824
|
+
opt_tys = @opt_tys.map {|ty| ty.substitute(subst, depth - 1) }
|
|
825
|
+
rest_ty = @rest_ty&.substitute(subst, depth - 1)
|
|
826
|
+
post_tys = @post_tys.map {|ty| ty.substitute(subst, depth - 1) }
|
|
827
|
+
kw_tys = @kw_tys.map {|req, key, ty| [req, key, ty.substitute(subst, depth - 1)] }
|
|
828
|
+
kw_rest_ty = @kw_rest_ty&.substitute(subst, depth - 1)
|
|
829
|
+
blk_ty = @blk_ty.substitute(subst, depth - 1)
|
|
830
|
+
MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
799
831
|
end
|
|
800
832
|
|
|
801
833
|
def merge(other)
|
|
@@ -810,8 +842,6 @@ module TypeProf
|
|
|
810
842
|
raise if !!kws1[kw] != !!kws2[kw]
|
|
811
843
|
end
|
|
812
844
|
elsif @kw_tys || other.kw_tys
|
|
813
|
-
puts
|
|
814
|
-
p self, other
|
|
815
845
|
(@kw_tys || other.kw_tys).each do |req,|
|
|
816
846
|
raise if req
|
|
817
847
|
end
|
|
@@ -859,231 +889,60 @@ module TypeProf
|
|
|
859
889
|
end
|
|
860
890
|
end
|
|
861
891
|
blk_ty = @blk_ty.union(other.blk_ty) if @blk_ty
|
|
862
|
-
|
|
892
|
+
MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
863
893
|
end
|
|
864
894
|
end
|
|
865
895
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
def initialize(lead_tys, rest_ty, kw_ty, blk_ty)
|
|
896
|
+
class BlockSignature < Signature
|
|
897
|
+
def initialize(lead_tys, opt_tys, rest_ty, blk_ty)
|
|
869
898
|
@lead_tys = lead_tys
|
|
899
|
+
@opt_tys = opt_tys
|
|
870
900
|
@rest_ty = rest_ty
|
|
871
|
-
@kw_ty = kw_ty
|
|
872
901
|
@blk_ty = blk_ty
|
|
873
|
-
|
|
874
|
-
end
|
|
875
|
-
|
|
876
|
-
attr_reader :lead_tys, :
|
|
877
|
-
|
|
878
|
-
def merge(
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
lead_tys = @lead_tys.map {|ty| ty.globalize(caller_env, visited, depth) }
|
|
896
|
-
rest_ty = @rest_ty.globalize(caller_env, visited, depth) if @rest_ty
|
|
897
|
-
kw_ty = @kw_ty.globalize(caller_env, visited, depth) if @kw_ty
|
|
898
|
-
ActualArguments.new(lead_tys, rest_ty, kw_ty, @blk_ty)
|
|
899
|
-
end
|
|
900
|
-
|
|
901
|
-
def limit_size(limit)
|
|
902
|
-
self
|
|
903
|
-
end
|
|
904
|
-
|
|
905
|
-
def each_formal_arguments(fargs_format)
|
|
906
|
-
lead_num = fargs_format[:lead_num] || 0
|
|
907
|
-
post_num = fargs_format[:post_num] || 0
|
|
908
|
-
rest_acceptable = !!fargs_format[:rest_start]
|
|
909
|
-
keyword = fargs_format[:keyword]
|
|
910
|
-
kw_rest_acceptable = !!fargs_format[:kwrest]
|
|
911
|
-
opt = fargs_format[:opt]
|
|
912
|
-
#p fargs_format
|
|
913
|
-
|
|
914
|
-
# TODO: expand tuples to normal arguments
|
|
915
|
-
|
|
916
|
-
# check number of arguments
|
|
917
|
-
if !@rest_ty && lead_num + post_num > @lead_tys.size
|
|
918
|
-
# too less
|
|
919
|
-
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
|
|
920
|
-
return
|
|
921
|
-
end
|
|
922
|
-
if !rest_acceptable
|
|
923
|
-
# too many
|
|
924
|
-
if opt
|
|
925
|
-
if lead_num + post_num + opt.size - 1 < @lead_tys.size
|
|
926
|
-
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num }..#{ lead_num + post_num + opt.size - 1})"
|
|
927
|
-
return
|
|
928
|
-
end
|
|
929
|
-
else
|
|
930
|
-
if lead_num + post_num < @lead_tys.size
|
|
931
|
-
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
|
|
932
|
-
return
|
|
902
|
+
# TODO: kw_tys
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
attr_reader :lead_tys, :opt_tys, :rest_ty, :blk_ty
|
|
906
|
+
|
|
907
|
+
def merge(bsig)
|
|
908
|
+
if @rest_ty && bsig.rest_ty
|
|
909
|
+
rest_ty = @rest_ty.union(bsig.rest_ty)
|
|
910
|
+
BlockSignature.new(@lead_tys, [], rest_ty, @blk_ty.union(bsig.blk_ty))
|
|
911
|
+
elsif @rest_ty || bsig.rest_ty
|
|
912
|
+
rest_ty = @rest_ty || bsig.rest_ty
|
|
913
|
+
rest_ty = @opt_tys.inject(rest_ty, &:union)
|
|
914
|
+
rest_ty = bsig.opt_tys.inject(rest_ty, &:union)
|
|
915
|
+
|
|
916
|
+
lead_tys = []
|
|
917
|
+
[@lead_tys.size, bsig.lead_tys.size].max.times do |i|
|
|
918
|
+
ty1 = @lead_tys[i]
|
|
919
|
+
ty2 = bsig.lead_tys[i]
|
|
920
|
+
if ty1 && ty2
|
|
921
|
+
lead_tys << ty1.union(ty2)
|
|
922
|
+
else
|
|
923
|
+
rest_ty = rest_ty.union(ty1 || ty2)
|
|
933
924
|
end
|
|
934
925
|
end
|
|
935
|
-
end
|
|
936
926
|
|
|
937
|
-
|
|
938
|
-
lower_bound = [lead_num + post_num - @lead_tys.size, 0].max
|
|
939
|
-
upper_bound = lead_num + post_num - @lead_tys.size + (opt ? opt.size - 1 : 0) + (rest_acceptable ? 1 : 0)
|
|
940
|
-
rest_elem = @rest_ty.is_a?(Type::Array) ? @rest_ty.elems.squash : Type.any
|
|
927
|
+
BlockSignature.new(lead_tys, [], rest_ty, @blk_ty.union(bsig.blk_ty))
|
|
941
928
|
else
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
kw_tys = []
|
|
947
|
-
keyword.each do |kw|
|
|
948
|
-
case
|
|
949
|
-
when kw.is_a?(Symbol) # required keyword
|
|
950
|
-
key = kw
|
|
951
|
-
req = true
|
|
952
|
-
when kw.size == 2 # optional keyword (default value is a literal)
|
|
953
|
-
key, default_ty = *kw
|
|
954
|
-
default_ty = Type.guess_literal_type(default_ty)
|
|
955
|
-
default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
|
|
956
|
-
req = false
|
|
957
|
-
else # optional keyword (default value is an expression)
|
|
958
|
-
key, = kw
|
|
959
|
-
req = false
|
|
960
|
-
end
|
|
961
|
-
|
|
962
|
-
sym = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
|
|
963
|
-
ty = Type.bot
|
|
964
|
-
if @kw_ty.is_a?(Type::Hash)
|
|
965
|
-
# XXX: consider Union
|
|
966
|
-
ty = @kw_ty.elems[sym]
|
|
967
|
-
# XXX: remove the key
|
|
968
|
-
end
|
|
969
|
-
if ty == Type.bot
|
|
970
|
-
yield "no argument for required keywords"
|
|
971
|
-
return
|
|
972
|
-
end
|
|
973
|
-
ty = ty.union(default_ty) if default_ty
|
|
974
|
-
kw_tys << [req, key, ty]
|
|
975
|
-
end
|
|
976
|
-
end
|
|
977
|
-
if kw_rest_acceptable
|
|
978
|
-
kw_rest_ty = @kw_ty
|
|
979
|
-
if kw_rest_ty == Type.any
|
|
980
|
-
kw_rest_ty = Type.gen_hash {|h| h[Type.any] = Type.any }
|
|
981
|
-
end
|
|
982
|
-
end
|
|
983
|
-
#if @kw_ty
|
|
984
|
-
# yield "passed a keyword to non-keyword method"
|
|
985
|
-
#end
|
|
986
|
-
|
|
987
|
-
(lower_bound .. upper_bound).each do |rest_len|
|
|
988
|
-
aargs = @lead_tys + [rest_elem] * rest_len
|
|
989
|
-
lead_tys = aargs.shift(lead_num)
|
|
990
|
-
lead_tys << rest_elem until lead_tys.size == lead_num
|
|
991
|
-
post_tys = aargs.pop(post_num)
|
|
992
|
-
post_tys.unshift(rest_elem) until post_tys.size == post_num
|
|
993
|
-
start_pc = 0
|
|
994
|
-
if opt
|
|
995
|
-
tmp_opt = opt[1..]
|
|
996
|
-
opt_tys = []
|
|
997
|
-
until aargs.empty? || tmp_opt.empty?
|
|
998
|
-
opt_tys << aargs.shift
|
|
999
|
-
start_pc = tmp_opt.shift
|
|
1000
|
-
end
|
|
1001
|
-
end
|
|
1002
|
-
if rest_acceptable
|
|
1003
|
-
acc = aargs.inject {|acc, ty| acc.union(ty) }
|
|
1004
|
-
acc = acc ? acc.union(rest_elem) : rest_elem if rest_elem
|
|
1005
|
-
acc ||= Type.bot
|
|
1006
|
-
rest_ty = acc
|
|
1007
|
-
aargs.clear
|
|
1008
|
-
end
|
|
1009
|
-
if !aargs.empty?
|
|
1010
|
-
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
|
|
1011
|
-
return
|
|
929
|
+
lead_tys = []
|
|
930
|
+
n = [@lead_tys.size, bsig.lead_tys.size].min
|
|
931
|
+
n.times do |i|
|
|
932
|
+
lead_tys << @lead_tys[i].union(bsig.lead_tys[i])
|
|
1012
933
|
end
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
# fargs: lead_tys, opt_tys, rest_ty, post_tys
|
|
1022
|
-
if @rest_ty
|
|
1023
|
-
lower_bound = [0, fargs.lead_tys.size + fargs.post_tys.size - aargs.size].max
|
|
1024
|
-
upper_bound = [0, lower_bound + fargs.opt_tys.size].max
|
|
1025
|
-
(lower_bound..upper_bound).each do |n|
|
|
1026
|
-
tmp_aargs = ActualArguments.new(@lead_tys + [@rest_ty] * n, nil, @kw_ty, @blk_ty)
|
|
1027
|
-
if tmp_aargs.consistent_with_formal_arguments?(fargs, subst)
|
|
1028
|
-
return true
|
|
934
|
+
opt_tys1 = @lead_tys[n..] + @opt_tys
|
|
935
|
+
opt_tys2 = bsig.lead_tys[n..] + bsig.opt_tys
|
|
936
|
+
opt_tys = []
|
|
937
|
+
[opt_tys1.size, opt_tys2.size].max.times do |i|
|
|
938
|
+
if opt_tys1[i] && opt_tys2[i]
|
|
939
|
+
opt_tys << opt_tys1[i].union(opt_tys2[i])
|
|
940
|
+
else
|
|
941
|
+
opt_tys << (opt_tys1[i] || opt_tys2[i])
|
|
1029
942
|
end
|
|
1030
943
|
end
|
|
1031
|
-
|
|
1032
|
-
end
|
|
1033
|
-
|
|
1034
|
-
if fargs.rest_ty
|
|
1035
|
-
return false if aargs.size < fargs.lead_tys.size + fargs.post_tys.size
|
|
1036
|
-
aargs.shift(fargs.lead_tys.size).zip(fargs.lead_tys) do |aarg, farg|
|
|
1037
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1038
|
-
end
|
|
1039
|
-
aargs.pop(fargs.post_tys.size).zip(fargs.post_tys) do |aarg, farg|
|
|
1040
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1041
|
-
end
|
|
1042
|
-
fargs.opt_tys.each do |farg|
|
|
1043
|
-
aarg = aargs.shift
|
|
1044
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1045
|
-
end
|
|
1046
|
-
aargs.each do |aarg|
|
|
1047
|
-
return false unless aarg.consistent?(fargs.rest_ty, subst)
|
|
1048
|
-
end
|
|
1049
|
-
else
|
|
1050
|
-
return false if aargs.size < fargs.lead_tys.size + fargs.post_tys.size
|
|
1051
|
-
return false if aargs.size > fargs.lead_tys.size + fargs.post_tys.size + fargs.opt_tys.size
|
|
1052
|
-
aargs.shift(fargs.lead_tys.size).zip(fargs.lead_tys) do |aarg, farg|
|
|
1053
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1054
|
-
end
|
|
1055
|
-
aargs.pop(fargs.post_tys.size).zip(fargs.post_tys) do |aarg, farg|
|
|
1056
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1057
|
-
end
|
|
1058
|
-
aargs.zip(fargs.opt_tys) do |aarg, farg|
|
|
1059
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1060
|
-
end
|
|
1061
|
-
end
|
|
1062
|
-
# XXX: fargs.keyword_tys
|
|
1063
|
-
|
|
1064
|
-
case fargs.blk_ty
|
|
1065
|
-
when Type::TypedProc
|
|
1066
|
-
return false if @blk_ty == Type.nil
|
|
1067
|
-
when Type.nil
|
|
1068
|
-
return false if @blk_ty != Type.nil
|
|
1069
|
-
when Type::Any
|
|
1070
|
-
else
|
|
1071
|
-
raise "unknown typo of formal block signature"
|
|
1072
|
-
end
|
|
1073
|
-
true
|
|
1074
|
-
end
|
|
1075
|
-
|
|
1076
|
-
def screen_name(scratch)
|
|
1077
|
-
aargs = @lead_tys.map {|ty| ty.screen_name(scratch) }
|
|
1078
|
-
if @rest_ty
|
|
1079
|
-
aargs << ("*" + @rest_ty.screen_name(scratch))
|
|
1080
|
-
end
|
|
1081
|
-
if @kw_ty
|
|
1082
|
-
aargs << ("**" + @kw_ty.screen_name(scratch)) # TODO: Hash notation -> keyword notation
|
|
944
|
+
BlockSignature.new(lead_tys, opt_tys, nil, @blk_ty.union(bsig.blk_ty))
|
|
1083
945
|
end
|
|
1084
|
-
s = "(#{ aargs.join(", ") })"
|
|
1085
|
-
s << " { #{ scratch.proc_screen_name(@blk_ty) } }" if @blk_ty != Type.nil
|
|
1086
|
-
s
|
|
1087
946
|
end
|
|
1088
947
|
end
|
|
1089
948
|
end
|