typeprof 0.1.2 → 0.4.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/.gitignore +3 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +10 -21
- data/LICENSE +21 -0
- data/README.md +1 -1
- data/doc/demo.md +398 -0
- data/doc/doc.ja.md +11 -1
- data/doc/doc.md +16 -7
- data/exe/typeprof +2 -1
- data/lib/typeprof.rb +9 -0
- data/lib/typeprof/analyzer.rb +455 -364
- data/lib/typeprof/arguments.rb +397 -0
- data/lib/typeprof/block.rb +133 -0
- data/lib/typeprof/builtin.rb +125 -116
- data/lib/typeprof/cli.rb +62 -71
- data/lib/typeprof/config.rb +114 -0
- data/lib/typeprof/container-type.rb +208 -27
- data/lib/typeprof/export.rb +201 -96
- data/lib/typeprof/import.rb +451 -365
- data/lib/typeprof/iseq.rb +43 -2
- data/lib/typeprof/method.rb +139 -100
- data/lib/typeprof/type.rb +138 -297
- 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/block11.rb +1 -1
- data/smoke/block13.rb +9 -0
- data/smoke/block13.rbs +3 -0
- 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/class.rb +2 -0
- data/smoke/constant1.rb +13 -5
- data/smoke/constant2.rb +2 -0
- data/smoke/cvar.rb +1 -0
- data/smoke/demo10.rb +1 -1
- data/smoke/demo5.rb +3 -0
- 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/gvar.rb +1 -1
- data/smoke/gvar2.rb +17 -0
- data/smoke/gvar2.rbs +1 -0
- data/smoke/hash4.rb +1 -1
- data/smoke/inheritance2.rb +6 -0
- data/smoke/instance_eval.rb +1 -1
- data/smoke/int_times.rb +1 -1
- data/smoke/ivar3.rb +16 -0
- data/smoke/ivar3.rbs +3 -0
- data/smoke/keyword3.rb +1 -2
- data/smoke/keyword4.rb +1 -1
- data/smoke/manual-rbs2.rb +1 -1
- data/smoke/manual-rbs3.rb +12 -0
- data/smoke/manual-rbs3.rbs +3 -0
- data/smoke/module4.rb +5 -0
- data/smoke/multiple-superclass.rb +12 -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/proc4.rb +1 -1
- data/smoke/rbs-alias.rb +9 -0
- data/smoke/rbs-alias.rbs +4 -0
- data/smoke/rbs-attr.rb +26 -0
- data/smoke/rbs-attr.rbs +5 -0
- 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 +25 -0
- data/smoke/rbs-tyvar3.rbs +4 -0
- data/smoke/rbs-vars.rb +39 -0
- data/smoke/rbs-vars.rbs +7 -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 +6 -2
- data/smoke/struct3.rb +14 -0
- data/smoke/super1.rb +18 -0
- data/smoke/symbol-proc.rb +24 -0
- data/smoke/union-recv.rb +6 -0
- data/smoke/user-demo.rb +15 -0
- data/smoke/wrong-extend.rb +1 -0
- data/tools/setup-insns-def.rb +1 -1
- data/tools/stackprof-wrapper.rb +1 -1
- data/typeprof.gemspec +12 -4
- metadata +68 -10
- data/.gitmodules +0 -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)
|
|
@@ -125,7 +142,7 @@ module TypeProf
|
|
|
125
142
|
"#{ @path }:#{ @linenos[pc] }"
|
|
126
143
|
end
|
|
127
144
|
|
|
128
|
-
attr_reader :name, :path, :
|
|
145
|
+
attr_reader :name, :path, :absolute_path, :start_lineno, :type, :locals, :fargs_format, :catch_table, :insns, :linenos
|
|
129
146
|
attr_reader :id
|
|
130
147
|
|
|
131
148
|
def pretty_print(q)
|
|
@@ -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
|
@@ -14,108 +14,128 @@ module TypeProf
|
|
|
14
14
|
recv = scratch.globalize_type(recv, caller_env, caller_ep)
|
|
15
15
|
aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
if fargs.is_a?(String)
|
|
19
|
-
scratch.error(caller_ep, fargs)
|
|
20
|
-
ctn[Type.any, caller_ep, caller_env]
|
|
21
|
-
next
|
|
22
|
-
end
|
|
17
|
+
locals = [Type.nil] * @iseq.locals.size
|
|
23
18
|
|
|
24
|
-
|
|
19
|
+
blk_ty, start_pcs = aargs.setup_formal_arguments(:method, locals, @iseq.fargs_format)
|
|
20
|
+
if blk_ty.is_a?(String)
|
|
21
|
+
scratch.error(caller_ep, blk_ty)
|
|
22
|
+
ctn[Type.any, caller_ep, caller_env]
|
|
23
|
+
return
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
nctx = Context.new(@iseq, @cref, mid)
|
|
27
|
+
callee_ep = ExecutionPoint.new(nctx, 0, nil)
|
|
28
|
+
nenv = Env.new(StaticEnv.new(recv, blk_ty, false), locals, [], Utils::HashWrapper.new({}))
|
|
29
|
+
alloc_site = AllocationSite.new(callee_ep)
|
|
30
|
+
locals.each_with_index do |ty, i|
|
|
31
|
+
alloc_site2 = alloc_site.add_id(i)
|
|
32
|
+
# nenv is top-level, so it is okay to call Type#localize directly
|
|
33
|
+
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
34
|
+
nenv = nenv.local_update(i, ty)
|
|
35
|
+
end
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
scratch.
|
|
37
|
+
start_pcs.each do |start_pc|
|
|
38
|
+
scratch.merge_env(ExecutionPoint.new(nctx, start_pc, @outer_ep), nenv)
|
|
28
39
|
end
|
|
40
|
+
|
|
41
|
+
scratch.add_iseq_method_call!(self, nctx)
|
|
42
|
+
scratch.add_callsite!(nctx, caller_ep, caller_env, &ctn)
|
|
29
43
|
end
|
|
30
44
|
|
|
31
|
-
def
|
|
45
|
+
def do_check_send(msig, recv, mid, ep, scratch)
|
|
46
|
+
lead_num = @iseq.fargs_format[:lead_num] || 0
|
|
47
|
+
post_num = @iseq.fargs_format[:post_num] || 0
|
|
48
|
+
rest_start = @iseq.fargs_format[:rest_start]
|
|
49
|
+
opt = @iseq.fargs_format[:opt] || [0]
|
|
50
|
+
|
|
51
|
+
# TODO: check keywords
|
|
52
|
+
if rest_start
|
|
53
|
+
# almost ok
|
|
54
|
+
else
|
|
55
|
+
if msig.lead_tys.size + msig.post_tys.size < lead_num + post_num
|
|
56
|
+
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])
|
|
57
|
+
return
|
|
58
|
+
end
|
|
59
|
+
if msig.lead_tys.size + msig.opt_tys.size + msig.post_tys.size > lead_num + opt.size - 1 + post_num
|
|
60
|
+
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])
|
|
61
|
+
return
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
32
65
|
lead_num = @iseq.fargs_format[:lead_num] || 0
|
|
33
66
|
post_start = @iseq.fargs_format[:post_start]
|
|
34
67
|
rest_start = @iseq.fargs_format[:rest_start]
|
|
35
68
|
kw_start = @iseq.fargs_format[:kwbits]
|
|
36
69
|
kw_start -= @iseq.fargs_format[:keyword].size if kw_start
|
|
70
|
+
kw_rest = @iseq.fargs_format[:kwrest]
|
|
37
71
|
block_start = @iseq.fargs_format[:block_start]
|
|
38
72
|
|
|
39
|
-
# XXX: need to check .rbs
|
|
73
|
+
# XXX: need to check .rbs msig and .rb fargs
|
|
40
74
|
|
|
41
|
-
ctx = Context.new(@iseq, @cref, mid)
|
|
42
|
-
callee_ep = ExecutionPoint.new(ctx,
|
|
75
|
+
ctx = Context.new(@iseq, @cref, mid)
|
|
76
|
+
callee_ep = ExecutionPoint.new(ctx, 0, nil)
|
|
43
77
|
|
|
44
78
|
locals = [Type.nil] * @iseq.locals.size
|
|
45
|
-
nenv = Env.new(StaticEnv.new(recv,
|
|
79
|
+
nenv = Env.new(StaticEnv.new(recv, msig.blk_ty, false), locals, [], Utils::HashWrapper.new({}))
|
|
46
80
|
alloc_site = AllocationSite.new(callee_ep)
|
|
47
81
|
idx = 0
|
|
48
|
-
|
|
82
|
+
msig.lead_tys.each_with_index do |ty, i|
|
|
49
83
|
alloc_site2 = alloc_site.add_id(idx += 1)
|
|
50
84
|
# nenv is top-level, so it is okay to call Type#localize directly
|
|
51
85
|
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
52
86
|
nenv = nenv.local_update(i, ty)
|
|
53
87
|
end
|
|
54
|
-
if
|
|
55
|
-
|
|
88
|
+
if msig.opt_tys
|
|
89
|
+
msig.opt_tys.each_with_index do |ty, i|
|
|
56
90
|
alloc_site2 = alloc_site.add_id(idx += 1)
|
|
57
91
|
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
58
92
|
nenv = nenv.local_update(lead_num + i, ty)
|
|
59
93
|
end
|
|
60
94
|
end
|
|
61
|
-
if
|
|
95
|
+
if msig.rest_ty
|
|
62
96
|
alloc_site2 = alloc_site.add_id(idx += 1)
|
|
63
|
-
ty = Type::Array.new(Type::Array::Elements.new([],
|
|
97
|
+
ty = Type::Array.new(Type::Array::Elements.new([], msig.rest_ty), Type::Instance.new(Type::Builtin[:ary]))
|
|
64
98
|
nenv, rest_ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
65
99
|
nenv = nenv.local_update(rest_start, rest_ty)
|
|
66
100
|
end
|
|
67
|
-
if
|
|
68
|
-
|
|
101
|
+
if msig.post_tys
|
|
102
|
+
msig.post_tys.each_with_index do |ty, i|
|
|
69
103
|
alloc_site2 = alloc_site.add_id(idx += 1)
|
|
70
104
|
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
71
105
|
nenv = nenv.local_update(post_start + i, ty)
|
|
72
106
|
end
|
|
73
107
|
end
|
|
74
|
-
if
|
|
75
|
-
|
|
76
|
-
alloc_site2 = alloc_site.add_id(
|
|
108
|
+
if msig.kw_tys
|
|
109
|
+
msig.kw_tys.each_with_index do |(_, key, ty), i|
|
|
110
|
+
alloc_site2 = alloc_site.add_id(key)
|
|
77
111
|
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
78
112
|
nenv = nenv.local_update(kw_start + i, ty)
|
|
79
113
|
end
|
|
80
114
|
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]
|
|
115
|
+
if msig.kw_rest_ty
|
|
116
|
+
ty = msig.kw_rest_ty
|
|
117
|
+
alloc_site2 = alloc_site.add_id(:**)
|
|
118
|
+
nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
|
|
119
|
+
nenv = nenv.local_update(kw_rest, ty)
|
|
120
|
+
end
|
|
121
|
+
nenv = nenv.local_update(block_start, msig.blk_ty) if block_start
|
|
94
122
|
|
|
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
|
|
123
|
+
opt.each do |start_pc|
|
|
124
|
+
scratch.merge_env(ExecutionPoint.new(ctx, start_pc, nil), nenv)
|
|
107
125
|
end
|
|
108
|
-
|
|
126
|
+
|
|
127
|
+
ctx
|
|
109
128
|
end
|
|
110
129
|
end
|
|
111
130
|
|
|
112
131
|
class AttrMethodDef < MethodDef
|
|
113
|
-
def initialize(ivar, kind)
|
|
132
|
+
def initialize(ivar, kind, absolute_path)
|
|
114
133
|
@ivar = ivar
|
|
115
134
|
@kind = kind # :reader | :writer
|
|
135
|
+
@absolute_path = absolute_path
|
|
116
136
|
end
|
|
117
137
|
|
|
118
|
-
attr_reader :ivar, :kind
|
|
138
|
+
attr_reader :ivar, :kind, :absolute_path
|
|
119
139
|
|
|
120
140
|
def do_send(recv, mid, aargs, caller_ep, caller_env, scratch, &ctn)
|
|
121
141
|
case @kind
|
|
@@ -140,8 +160,8 @@ module TypeProf
|
|
|
140
160
|
end
|
|
141
161
|
|
|
142
162
|
class TypedMethodDef < MethodDef
|
|
143
|
-
def initialize(
|
|
144
|
-
@
|
|
163
|
+
def initialize(sig_rets, rbs_source) # sig_rets: Array<[MethodSignature, (return)Type]>
|
|
164
|
+
@sig_rets = sig_rets
|
|
145
165
|
@rbs_source = rbs_source
|
|
146
166
|
end
|
|
147
167
|
|
|
@@ -151,25 +171,42 @@ module TypeProf
|
|
|
151
171
|
recv = scratch.globalize_type(recv_orig, caller_env, caller_ep)
|
|
152
172
|
found = false
|
|
153
173
|
aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
|
|
154
|
-
@
|
|
174
|
+
@sig_rets.each do |msig, ret_ty|
|
|
155
175
|
ncaller_env = caller_env
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
# XXX: support self type in fargs
|
|
176
|
+
#pp [mid, aargs, msig]
|
|
177
|
+
# XXX: support self type in msig
|
|
159
178
|
subst = { Type::Var.new(:self) => recv }
|
|
160
|
-
next unless aargs.
|
|
161
|
-
|
|
179
|
+
next unless aargs.consistent_with_method_signature?(msig, subst)
|
|
180
|
+
case
|
|
181
|
+
when recv.is_a?(Type::Cell) && recv_orig.is_a?(Type::LocalCell)
|
|
182
|
+
tyvars = recv.base_type.klass.type_params.map {|name,| Type::Var.new(name) }
|
|
183
|
+
tyvars.each_with_index do |tyvar, idx|
|
|
184
|
+
ty = subst[tyvar]
|
|
185
|
+
if ty
|
|
186
|
+
ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
|
|
187
|
+
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
|
|
188
|
+
elems.update(idx, ty)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
tyvars.zip(recv.elems.elems) do |tyvar, elem|
|
|
193
|
+
if subst[tyvar]
|
|
194
|
+
subst[tyvar] = subst[tyvar].union(elem)
|
|
195
|
+
else
|
|
196
|
+
subst[tyvar] = elem
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
when recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
|
|
162
200
|
tyvar_elem = Type::Var.new(:Elem)
|
|
163
201
|
if subst[tyvar_elem]
|
|
164
202
|
ty = subst[tyvar_elem]
|
|
165
|
-
alloc_site = AllocationSite.new(caller_ep).add_id(self)
|
|
166
203
|
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|
|
|
204
|
+
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
|
|
168
205
|
elems.update(nil, ty)
|
|
169
206
|
end
|
|
170
207
|
end
|
|
171
|
-
subst.merge!({ tyvar_elem => recv.elems.
|
|
172
|
-
|
|
208
|
+
subst.merge!({ tyvar_elem => recv.elems.squash_or_any })
|
|
209
|
+
when recv.is_a?(Type::Hash) && recv_orig.is_a?(Type::LocalHash)
|
|
173
210
|
tyvar_k = Type::Var.new(:K)
|
|
174
211
|
tyvar_v = Type::Var.new(:V)
|
|
175
212
|
# XXX: need to support destructive operation
|
|
@@ -179,54 +216,57 @@ module TypeProf
|
|
|
179
216
|
end
|
|
180
217
|
ret_ty = ret_ty.substitute(subst, Config.options[:type_depth_limit])
|
|
181
218
|
found = true
|
|
182
|
-
if aargs.blk_ty.is_a?(Type::
|
|
219
|
+
if aargs.blk_ty.is_a?(Type::Proc)
|
|
220
|
+
#raise NotImplementedError unless aargs.blk_ty.block_body.is_a?(ISeqBlock) # XXX
|
|
183
221
|
dummy_ctx = TypedContext.new(caller_ep, mid)
|
|
184
222
|
dummy_ep = ExecutionPoint.new(dummy_ctx, -1, caller_ep)
|
|
185
|
-
dummy_env = Env.new(StaticEnv.new(recv,
|
|
186
|
-
if
|
|
187
|
-
scratch.add_callsite!(dummy_ctx,
|
|
188
|
-
nfargs =
|
|
223
|
+
dummy_env = Env.new(StaticEnv.new(recv, msig.blk_ty, false), [], [], Utils::HashWrapper.new({}))
|
|
224
|
+
if msig.blk_ty.is_a?(Type::Proc)
|
|
225
|
+
scratch.add_callsite!(dummy_ctx, caller_ep, ncaller_env, &ctn)
|
|
226
|
+
nfargs = msig.blk_ty.block_body.msig
|
|
189
227
|
alloc_site = AllocationSite.new(caller_ep).add_id(self)
|
|
190
|
-
|
|
228
|
+
nlead_tys = (nfargs.lead_tys + nfargs.opt_tys).map.with_index do |ty, i|
|
|
191
229
|
if recv.is_a?(Type::Array)
|
|
192
230
|
tyvar_elem = Type::Var.new(:Elem)
|
|
193
|
-
|
|
231
|
+
ty = ty.substitute(subst.merge({ tyvar_elem => recv.elems.squash_or_any }), Config.options[:type_depth_limit])
|
|
194
232
|
else
|
|
195
|
-
|
|
233
|
+
ty = ty.substitute(subst, Config.options[:type_depth_limit])
|
|
196
234
|
end
|
|
197
|
-
|
|
235
|
+
ty = ty.remove_type_vars
|
|
198
236
|
alloc_site2 = alloc_site.add_id(i)
|
|
199
|
-
dummy_env,
|
|
200
|
-
|
|
237
|
+
dummy_env, ty = scratch.localize_type(ty, dummy_env, dummy_ep, alloc_site2)
|
|
238
|
+
ty
|
|
201
239
|
end
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
240
|
+
0.upto(nfargs.opt_tys.size) do |n|
|
|
241
|
+
naargs = ActualArguments.new(nlead_tys[0, nfargs.lead_tys.size + n], nil, {}, Type.nil) # XXX: support block to block?
|
|
242
|
+
scratch.do_invoke_block(aargs.blk_ty, naargs, dummy_ep, dummy_env) do |blk_ret_ty, _ep, _env|
|
|
243
|
+
subst2 = {}
|
|
244
|
+
if blk_ret_ty.consistent?(msig.blk_ty.block_body.ret_ty, subst2)
|
|
245
|
+
if recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
|
|
246
|
+
tyvar_elem = Type::Var.new(:Elem)
|
|
247
|
+
if subst2[tyvar_elem]
|
|
248
|
+
ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
|
|
249
|
+
elems.update(nil, subst2[tyvar_elem])
|
|
250
|
+
end
|
|
251
|
+
scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
|
|
211
252
|
end
|
|
212
|
-
|
|
253
|
+
ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
|
|
254
|
+
else
|
|
255
|
+
ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
|
|
213
256
|
end
|
|
214
|
-
ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
|
|
215
257
|
else
|
|
216
|
-
|
|
258
|
+
# raise "???"
|
|
259
|
+
# XXX: need warning
|
|
260
|
+
ret_ty = Type.any
|
|
217
261
|
end
|
|
218
|
-
|
|
219
|
-
#
|
|
220
|
-
#
|
|
221
|
-
ret_ty
|
|
262
|
+
ret_ty = ret_ty.remove_type_vars
|
|
263
|
+
# XXX: check the return type from the block
|
|
264
|
+
# sig.blk_ty.block_body.ret_ty.eql?(_ret_ty) ???
|
|
265
|
+
scratch.add_return_value!(dummy_ctx, ret_ty)
|
|
222
266
|
end
|
|
223
|
-
|
|
224
|
-
#
|
|
225
|
-
# sig.blk_ty.ret_ty.eql?(_ret_ty) ???
|
|
226
|
-
scratch.add_return_type!(dummy_ctx, ret_ty)
|
|
267
|
+
# scratch.add_return_value!(dummy_ctx, ret_ty) ?
|
|
268
|
+
# This makes `def foo; 1.times { return "str" }; end` return Integer|String
|
|
227
269
|
end
|
|
228
|
-
# scratch.add_return_type!(dummy_ctx, ret_ty) ?
|
|
229
|
-
# This makes `def foo; 1.times { return "str" }; end` return Integer|String
|
|
230
270
|
else
|
|
231
271
|
# XXX: a block is passed to a method that does not accept block.
|
|
232
272
|
# Should we call the passed block with any arguments?
|
|
@@ -247,8 +287,8 @@ module TypeProf
|
|
|
247
287
|
|
|
248
288
|
def do_match_iseq_mdef(iseq_mdef, recv, mid, env, ep, scratch)
|
|
249
289
|
recv = scratch.globalize_type(recv, env, ep)
|
|
250
|
-
@
|
|
251
|
-
iseq_mdef.
|
|
290
|
+
@sig_rets.each do |msig, _ret_ty|
|
|
291
|
+
iseq_mdef.do_check_send(msig, recv, mid, ep, scratch)
|
|
252
292
|
end
|
|
253
293
|
end
|
|
254
294
|
end
|
|
@@ -259,7 +299,6 @@ module TypeProf
|
|
|
259
299
|
end
|
|
260
300
|
|
|
261
301
|
def do_send(recv, mid, aargs, caller_ep, caller_env, scratch, &ctn)
|
|
262
|
-
# XXX: ctn?
|
|
263
302
|
scratch.merge_return_env(caller_ep) {|env| env ? env.merge(caller_env) : caller_env } # for Kernel#lambda
|
|
264
303
|
@impl[recv, mid, aargs, caller_ep, caller_env, scratch, &ctn]
|
|
265
304
|
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 = {}
|
|
@@ -129,6 +129,17 @@ module TypeProf
|
|
|
129
129
|
end
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
class Void < Any
|
|
133
|
+
def inspect
|
|
134
|
+
"Type::Void"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def screen_name(scratch)
|
|
138
|
+
"void"
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
|
|
132
143
|
class Union < Type
|
|
133
144
|
def initialize(tys, elems)
|
|
134
145
|
raise unless tys.is_a?(Utils::Set)
|
|
@@ -137,7 +148,7 @@ module TypeProf
|
|
|
137
148
|
# invariant check
|
|
138
149
|
local = nil
|
|
139
150
|
tys.each do |ty|
|
|
140
|
-
raise unless ty.is_a?(Type)
|
|
151
|
+
raise ty.inspect unless ty.is_a?(Type)
|
|
141
152
|
local = true if ty.is_a?(LocalArray) || ty.is_a?(LocalHash)
|
|
142
153
|
end
|
|
143
154
|
raise if local && elems
|
|
@@ -215,12 +226,15 @@ module TypeProf
|
|
|
215
226
|
bool = true
|
|
216
227
|
end
|
|
217
228
|
types.delete(Type.any) unless Config.options[:pedantic_output]
|
|
229
|
+
proc_tys, types = types.partition {|ty| ty.is_a?(Proc) }
|
|
218
230
|
types = types.map {|ty| ty.screen_name(scratch) }
|
|
231
|
+
types << scratch.show_proc_signature(proc_tys) unless proc_tys.empty?
|
|
219
232
|
types << "bool" if bool
|
|
220
233
|
types = types.sort
|
|
221
234
|
if optional
|
|
222
|
-
|
|
223
|
-
|
|
235
|
+
case types.size
|
|
236
|
+
when 0 then "nil"
|
|
237
|
+
when 1 then types.first + "?"
|
|
224
238
|
else
|
|
225
239
|
"(#{ types.join (" | ") })?"
|
|
226
240
|
end
|
|
@@ -505,58 +519,51 @@ module TypeProf
|
|
|
505
519
|
end
|
|
506
520
|
end
|
|
507
521
|
|
|
508
|
-
class
|
|
509
|
-
def initialize(
|
|
510
|
-
@
|
|
511
|
-
@ep = ep
|
|
512
|
-
@type = type
|
|
522
|
+
class Proc < Type
|
|
523
|
+
def initialize(block_body, type)
|
|
524
|
+
@block_body, @type = block_body, type
|
|
513
525
|
end
|
|
514
526
|
|
|
515
|
-
attr_reader :
|
|
516
|
-
|
|
517
|
-
def inspect
|
|
518
|
-
"#<ISeqProc>"
|
|
519
|
-
end
|
|
527
|
+
attr_reader :block_body, :type
|
|
520
528
|
|
|
521
|
-
def
|
|
522
|
-
|
|
529
|
+
def consistent?(other, subst)
|
|
530
|
+
case other
|
|
531
|
+
when Type::Any then true
|
|
532
|
+
when Type::Var then other.add_subst!(self, subst)
|
|
533
|
+
when Type::Union
|
|
534
|
+
other.types.each do |ty2|
|
|
535
|
+
return true if consistent?(ty2, subst)
|
|
536
|
+
end
|
|
537
|
+
when Type::Proc
|
|
538
|
+
@block_body.consistent?(other.block_body)
|
|
539
|
+
else
|
|
540
|
+
self == other
|
|
541
|
+
end
|
|
523
542
|
end
|
|
524
543
|
|
|
525
544
|
def get_method(mid, scratch)
|
|
526
545
|
@type.get_method(mid, scratch)
|
|
527
546
|
end
|
|
528
547
|
|
|
529
|
-
def substitute(
|
|
530
|
-
|
|
531
|
-
end
|
|
532
|
-
end
|
|
533
|
-
|
|
534
|
-
class TypedProc < Type
|
|
535
|
-
def initialize(fargs, ret_ty, type)
|
|
536
|
-
# XXX: need to receive blk_ty?
|
|
537
|
-
# XXX: may refactor "arguments = arg_tys * blk_ty" out
|
|
538
|
-
@fargs = fargs
|
|
539
|
-
@ret_ty = ret_ty
|
|
540
|
-
@type = type
|
|
548
|
+
def substitute(subst, depth)
|
|
549
|
+
Proc.new(@block_body.substitute(subst, depth), @type)
|
|
541
550
|
end
|
|
542
551
|
|
|
543
|
-
attr_reader :fargs, :ret_ty
|
|
544
|
-
|
|
545
552
|
def screen_name(scratch)
|
|
546
|
-
|
|
553
|
+
scratch.show_proc_signature([self])
|
|
547
554
|
end
|
|
548
555
|
end
|
|
549
556
|
|
|
550
557
|
class Symbol < Type
|
|
551
|
-
def initialize(sym,
|
|
558
|
+
def initialize(sym, base_type)
|
|
552
559
|
@sym = sym
|
|
553
|
-
@
|
|
560
|
+
@base_type = base_type
|
|
554
561
|
end
|
|
555
562
|
|
|
556
|
-
attr_reader :sym, :
|
|
563
|
+
attr_reader :sym, :base_type
|
|
557
564
|
|
|
558
565
|
def inspect
|
|
559
|
-
"Type::Symbol[#{ @sym ? @sym.inspect : "(dynamic symbol)" }, #{ @
|
|
566
|
+
"Type::Symbol[#{ @sym ? @sym.inspect : "(dynamic symbol)" }, #{ @base_type.inspect }]"
|
|
560
567
|
end
|
|
561
568
|
|
|
562
569
|
def consistent?(other, subst)
|
|
@@ -566,7 +573,7 @@ module TypeProf
|
|
|
566
573
|
when Symbol
|
|
567
574
|
@sym == other.sym
|
|
568
575
|
else
|
|
569
|
-
@
|
|
576
|
+
@base_type.consistent?(other, subst)
|
|
570
577
|
end
|
|
571
578
|
end
|
|
572
579
|
|
|
@@ -574,12 +581,12 @@ module TypeProf
|
|
|
574
581
|
if @sym
|
|
575
582
|
@sym.inspect
|
|
576
583
|
else
|
|
577
|
-
@
|
|
584
|
+
@base_type.screen_name(scratch)
|
|
578
585
|
end
|
|
579
586
|
end
|
|
580
587
|
|
|
581
588
|
def get_method(mid, scratch)
|
|
582
|
-
@
|
|
589
|
+
@base_type.get_method(mid, scratch)
|
|
583
590
|
end
|
|
584
591
|
|
|
585
592
|
def substitute(_subst, _depth)
|
|
@@ -639,10 +646,9 @@ module TypeProf
|
|
|
639
646
|
end
|
|
640
647
|
end
|
|
641
648
|
|
|
642
|
-
def self.gen_hash
|
|
649
|
+
def self.gen_hash(base_ty = Type::Instance.new(Type::Builtin[:hash]))
|
|
643
650
|
hg = HashGenerator.new
|
|
644
651
|
yield hg
|
|
645
|
-
base_ty = Type::Instance.new(Type::Builtin[:hash])
|
|
646
652
|
Type::Hash.new(Type::Hash::Elements.new(hg.map_tys), base_ty)
|
|
647
653
|
end
|
|
648
654
|
|
|
@@ -739,66 +745,74 @@ module TypeProf
|
|
|
739
745
|
end
|
|
740
746
|
end
|
|
741
747
|
|
|
742
|
-
|
|
743
|
-
class FormalArguments
|
|
748
|
+
class Signature
|
|
744
749
|
include Utils::StructuralEquality
|
|
745
750
|
|
|
746
|
-
def initialize(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
747
|
-
@lead_tys = lead_tys
|
|
748
|
-
@opt_tys = opt_tys
|
|
749
|
-
@rest_ty = rest_ty
|
|
750
|
-
@post_tys = post_tys
|
|
751
|
-
@kw_tys = kw_tys
|
|
752
|
-
kw_tys.each {|a| raise if a.size != 3 } if kw_tys
|
|
753
|
-
@kw_rest_ty = kw_rest_ty
|
|
754
|
-
@blk_ty = blk_ty
|
|
755
|
-
end
|
|
756
|
-
|
|
757
|
-
attr_reader :lead_tys, :opt_tys, :rest_ty, :post_tys, :kw_tys, :kw_rest_ty, :blk_ty
|
|
758
|
-
|
|
759
|
-
def consistent?(fargs, subst)
|
|
760
|
-
warn "used?"
|
|
761
|
-
return false if @lead_tys.size != fargs.lead_tys.size
|
|
762
|
-
return false unless @lead_tys.zip(fargs.lead_tys).all? {|ty1, ty2| ty1.consistent?(ty2, subst) }
|
|
763
|
-
return false if (@opt_tys || []) != (fargs.opt_tys || []) # ??
|
|
764
|
-
if @rest_ty
|
|
765
|
-
return false unless @rest_ty.consistent?(fargs.rest_ty, subst)
|
|
766
|
-
end
|
|
767
|
-
if @post_tys
|
|
768
|
-
return false if @post_tys.size != fargs.post_tys.size
|
|
769
|
-
return false unless @post_tys.zip(fargs.post_tys).all? {|ty1, ty2| ty1.consistent?(ty2, subst) }
|
|
770
|
-
end
|
|
771
|
-
return false if @kw_tys.size != fargs.kw_tys.size
|
|
772
|
-
return false unless @kw_tys.zip(fargs.kw_tys).all? {|(_, ty1), (_, ty2)| ty1.consistent?(ty2, subst) }
|
|
773
|
-
if @kw_rest_ty
|
|
774
|
-
return false unless @kw_rest_ty.consistent?(fargs.kw_rest_ty, subst)
|
|
775
|
-
end
|
|
776
|
-
# intentionally skip blk_ty
|
|
777
|
-
true
|
|
778
|
-
end
|
|
779
|
-
|
|
780
751
|
def screen_name(scratch)
|
|
781
|
-
|
|
752
|
+
str = @lead_tys.map {|ty| ty.screen_name(scratch) }
|
|
782
753
|
if @opt_tys
|
|
783
|
-
|
|
754
|
+
str += @opt_tys.map {|ty| "?" + ty.screen_name(scratch) }
|
|
784
755
|
end
|
|
785
756
|
if @rest_ty
|
|
786
|
-
|
|
757
|
+
str << ("*" + @rest_ty.screen_name(scratch))
|
|
787
758
|
end
|
|
788
759
|
if @post_tys
|
|
789
|
-
|
|
760
|
+
str += @post_tys.map {|ty| ty.screen_name(scratch) }
|
|
790
761
|
end
|
|
791
762
|
if @kw_tys
|
|
792
763
|
@kw_tys.each do |req, sym, ty|
|
|
793
764
|
opt = req ? "" : "?"
|
|
794
|
-
|
|
765
|
+
str << "#{ opt }#{ sym }: #{ ty.screen_name(scratch) }"
|
|
795
766
|
end
|
|
796
767
|
end
|
|
797
768
|
if @kw_rest_ty
|
|
798
|
-
|
|
769
|
+
str << ("**" + @kw_rest_ty.screen_name(scratch))
|
|
770
|
+
end
|
|
771
|
+
str = str.empty? ? "" : "(#{ str.join(", ") })"
|
|
772
|
+
|
|
773
|
+
optional = false
|
|
774
|
+
blks = []
|
|
775
|
+
@blk_ty.each_child_global do |ty|
|
|
776
|
+
if ty.is_a?(Type::Proc)
|
|
777
|
+
blks << ty
|
|
778
|
+
else
|
|
779
|
+
# XXX: how should we handle types other than Type.nil
|
|
780
|
+
optional = true
|
|
781
|
+
end
|
|
782
|
+
end
|
|
783
|
+
if blks != []
|
|
784
|
+
str << " " if str != ""
|
|
785
|
+
str << "?" if optional
|
|
786
|
+
str << scratch.show_block_signature(blks)
|
|
799
787
|
end
|
|
800
|
-
|
|
801
|
-
|
|
788
|
+
|
|
789
|
+
str
|
|
790
|
+
end
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
class MethodSignature < Signature
|
|
794
|
+
def initialize(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
795
|
+
@lead_tys = lead_tys
|
|
796
|
+
@opt_tys = opt_tys
|
|
797
|
+
@rest_ty = rest_ty
|
|
798
|
+
@post_tys = post_tys
|
|
799
|
+
@kw_tys = kw_tys
|
|
800
|
+
kw_tys.each {|a| raise if a.size != 3 } if kw_tys
|
|
801
|
+
@kw_rest_ty = kw_rest_ty
|
|
802
|
+
@blk_ty = blk_ty
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
attr_reader :lead_tys, :opt_tys, :rest_ty, :post_tys, :kw_tys, :kw_rest_ty, :blk_ty
|
|
806
|
+
|
|
807
|
+
def substitute(subst, depth)
|
|
808
|
+
lead_tys = @lead_tys.map {|ty| ty.substitute(subst, depth - 1) }
|
|
809
|
+
opt_tys = @opt_tys.map {|ty| ty.substitute(subst, depth - 1) }
|
|
810
|
+
rest_ty = @rest_ty&.substitute(subst, depth - 1)
|
|
811
|
+
post_tys = @post_tys.map {|ty| ty.substitute(subst, depth - 1) }
|
|
812
|
+
kw_tys = @kw_tys.map {|req, key, ty| [req, key, ty.substitute(subst, depth - 1)] }
|
|
813
|
+
kw_rest_ty = @kw_rest_ty&.substitute(subst, depth - 1)
|
|
814
|
+
blk_ty = @blk_ty.substitute(subst, depth - 1)
|
|
815
|
+
MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
802
816
|
end
|
|
803
817
|
|
|
804
818
|
def merge(other)
|
|
@@ -813,8 +827,6 @@ module TypeProf
|
|
|
813
827
|
raise if !!kws1[kw] != !!kws2[kw]
|
|
814
828
|
end
|
|
815
829
|
elsif @kw_tys || other.kw_tys
|
|
816
|
-
puts
|
|
817
|
-
p self, other
|
|
818
830
|
(@kw_tys || other.kw_tys).each do |req,|
|
|
819
831
|
raise if req
|
|
820
832
|
end
|
|
@@ -862,231 +874,60 @@ module TypeProf
|
|
|
862
874
|
end
|
|
863
875
|
end
|
|
864
876
|
blk_ty = @blk_ty.union(other.blk_ty) if @blk_ty
|
|
865
|
-
|
|
877
|
+
MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
866
878
|
end
|
|
867
879
|
end
|
|
868
880
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
def initialize(lead_tys, rest_ty, kw_ty, blk_ty)
|
|
881
|
+
class BlockSignature < Signature
|
|
882
|
+
def initialize(lead_tys, opt_tys, rest_ty, blk_ty)
|
|
872
883
|
@lead_tys = lead_tys
|
|
884
|
+
@opt_tys = opt_tys
|
|
873
885
|
@rest_ty = rest_ty
|
|
874
|
-
@kw_ty = kw_ty
|
|
875
886
|
@blk_ty = blk_ty
|
|
876
|
-
|
|
877
|
-
end
|
|
878
|
-
|
|
879
|
-
attr_reader :lead_tys, :
|
|
880
|
-
|
|
881
|
-
def merge(
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
lead_tys = @lead_tys.map {|ty| ty.globalize(caller_env, visited, depth) }
|
|
899
|
-
rest_ty = @rest_ty.globalize(caller_env, visited, depth) if @rest_ty
|
|
900
|
-
kw_ty = @kw_ty.globalize(caller_env, visited, depth) if @kw_ty
|
|
901
|
-
ActualArguments.new(lead_tys, rest_ty, kw_ty, @blk_ty)
|
|
902
|
-
end
|
|
903
|
-
|
|
904
|
-
def limit_size(limit)
|
|
905
|
-
self
|
|
906
|
-
end
|
|
907
|
-
|
|
908
|
-
def each_formal_arguments(fargs_format)
|
|
909
|
-
lead_num = fargs_format[:lead_num] || 0
|
|
910
|
-
post_num = fargs_format[:post_num] || 0
|
|
911
|
-
rest_acceptable = !!fargs_format[:rest_start]
|
|
912
|
-
keyword = fargs_format[:keyword]
|
|
913
|
-
kw_rest_acceptable = !!fargs_format[:kwrest]
|
|
914
|
-
opt = fargs_format[:opt]
|
|
915
|
-
#p fargs_format
|
|
916
|
-
|
|
917
|
-
# TODO: expand tuples to normal arguments
|
|
918
|
-
|
|
919
|
-
# check number of arguments
|
|
920
|
-
if !@rest_ty && lead_num + post_num > @lead_tys.size
|
|
921
|
-
# too less
|
|
922
|
-
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
|
|
923
|
-
return
|
|
924
|
-
end
|
|
925
|
-
if !rest_acceptable
|
|
926
|
-
# too many
|
|
927
|
-
if opt
|
|
928
|
-
if lead_num + post_num + opt.size - 1 < @lead_tys.size
|
|
929
|
-
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num }..#{ lead_num + post_num + opt.size - 1})"
|
|
930
|
-
return
|
|
931
|
-
end
|
|
932
|
-
else
|
|
933
|
-
if lead_num + post_num < @lead_tys.size
|
|
934
|
-
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
|
|
935
|
-
return
|
|
887
|
+
# TODO: kw_tys
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
attr_reader :lead_tys, :opt_tys, :rest_ty, :blk_ty
|
|
891
|
+
|
|
892
|
+
def merge(bsig)
|
|
893
|
+
if @rest_ty && bsig.rest_ty
|
|
894
|
+
rest_ty = @rest_ty.union(bsig.rest_ty)
|
|
895
|
+
BlockSignature.new(@lead_tys, [], rest_ty, @blk_ty.union(bsig.blk_ty))
|
|
896
|
+
elsif @rest_ty || bsig.rest_ty
|
|
897
|
+
rest_ty = @rest_ty || bsig.rest_ty
|
|
898
|
+
rest_ty = @opt_tys.inject(rest_ty, &:union)
|
|
899
|
+
rest_ty = bsig.opt_tys.inject(rest_ty, &:union)
|
|
900
|
+
|
|
901
|
+
lead_tys = []
|
|
902
|
+
[@lead_tys.size, bsig.lead_tys.size].max.times do |i|
|
|
903
|
+
ty1 = @lead_tys[i]
|
|
904
|
+
ty2 = bsig.lead_tys[i]
|
|
905
|
+
if ty1 && ty2
|
|
906
|
+
lead_tys << ty1.union(ty2)
|
|
907
|
+
else
|
|
908
|
+
rest_ty = rest_ty.union(ty1 || ty2)
|
|
936
909
|
end
|
|
937
910
|
end
|
|
938
|
-
end
|
|
939
911
|
|
|
940
|
-
|
|
941
|
-
lower_bound = [lead_num + post_num - @lead_tys.size, 0].max
|
|
942
|
-
upper_bound = lead_num + post_num - @lead_tys.size + (opt ? opt.size - 1 : 0) + (rest_acceptable ? 1 : 0)
|
|
943
|
-
rest_elem = @rest_ty.is_a?(Type::Array) ? @rest_ty.elems.squash : Type.any
|
|
912
|
+
BlockSignature.new(lead_tys, [], rest_ty, @blk_ty.union(bsig.blk_ty))
|
|
944
913
|
else
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
kw_tys = []
|
|
950
|
-
keyword.each do |kw|
|
|
951
|
-
case
|
|
952
|
-
when kw.is_a?(Symbol) # required keyword
|
|
953
|
-
key = kw
|
|
954
|
-
req = true
|
|
955
|
-
when kw.size == 2 # optional keyword (default value is a literal)
|
|
956
|
-
key, default_ty = *kw
|
|
957
|
-
default_ty = Type.guess_literal_type(default_ty)
|
|
958
|
-
default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
|
|
959
|
-
req = false
|
|
960
|
-
else # optional keyword (default value is an expression)
|
|
961
|
-
key, = kw
|
|
962
|
-
req = false
|
|
963
|
-
end
|
|
964
|
-
|
|
965
|
-
sym = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
|
|
966
|
-
ty = Type.bot
|
|
967
|
-
if @kw_ty.is_a?(Type::Hash)
|
|
968
|
-
# XXX: consider Union
|
|
969
|
-
ty = @kw_ty.elems[sym]
|
|
970
|
-
# XXX: remove the key
|
|
971
|
-
end
|
|
972
|
-
if ty == Type.bot
|
|
973
|
-
yield "no argument for required keywords"
|
|
974
|
-
return
|
|
975
|
-
end
|
|
976
|
-
ty = ty.union(default_ty) if default_ty
|
|
977
|
-
kw_tys << [req, key, ty]
|
|
914
|
+
lead_tys = []
|
|
915
|
+
n = [@lead_tys.size, bsig.lead_tys.size].min
|
|
916
|
+
n.times do |i|
|
|
917
|
+
lead_tys << @lead_tys[i].union(bsig.lead_tys[i])
|
|
978
918
|
end
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
# yield "passed a keyword to non-keyword method"
|
|
988
|
-
#end
|
|
989
|
-
|
|
990
|
-
(lower_bound .. upper_bound).each do |rest_len|
|
|
991
|
-
aargs = @lead_tys + [rest_elem] * rest_len
|
|
992
|
-
lead_tys = aargs.shift(lead_num)
|
|
993
|
-
lead_tys << rest_elem until lead_tys.size == lead_num
|
|
994
|
-
post_tys = aargs.pop(post_num)
|
|
995
|
-
post_tys.unshift(rest_elem) until post_tys.size == post_num
|
|
996
|
-
start_pc = 0
|
|
997
|
-
if opt
|
|
998
|
-
tmp_opt = opt[1..]
|
|
999
|
-
opt_tys = []
|
|
1000
|
-
until aargs.empty? || tmp_opt.empty?
|
|
1001
|
-
opt_tys << aargs.shift
|
|
1002
|
-
start_pc = tmp_opt.shift
|
|
1003
|
-
end
|
|
1004
|
-
end
|
|
1005
|
-
if rest_acceptable
|
|
1006
|
-
acc = aargs.inject {|acc, ty| acc.union(ty) }
|
|
1007
|
-
acc = acc ? acc.union(rest_elem) : rest_elem if rest_elem
|
|
1008
|
-
acc ||= Type.bot
|
|
1009
|
-
rest_ty = acc
|
|
1010
|
-
aargs.clear
|
|
1011
|
-
end
|
|
1012
|
-
if !aargs.empty?
|
|
1013
|
-
yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
|
|
1014
|
-
return
|
|
1015
|
-
end
|
|
1016
|
-
yield FormalArguments.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, @blk_ty), start_pc
|
|
1017
|
-
end
|
|
1018
|
-
end
|
|
1019
|
-
|
|
1020
|
-
def consistent_with_formal_arguments?(fargs, subst)
|
|
1021
|
-
aargs = @lead_tys.dup
|
|
1022
|
-
|
|
1023
|
-
# aargs: lead_tys, rest_ty
|
|
1024
|
-
# fargs: lead_tys, opt_tys, rest_ty, post_tys
|
|
1025
|
-
if @rest_ty
|
|
1026
|
-
lower_bound = [0, fargs.lead_tys.size + fargs.post_tys.size - aargs.size].max
|
|
1027
|
-
upper_bound = [0, lower_bound + fargs.opt_tys.size].max
|
|
1028
|
-
(lower_bound..upper_bound).each do |n|
|
|
1029
|
-
tmp_aargs = ActualArguments.new(@lead_tys + [@rest_ty] * n, nil, @kw_ty, @blk_ty)
|
|
1030
|
-
if tmp_aargs.consistent_with_formal_arguments?(fargs, subst)
|
|
1031
|
-
return true
|
|
919
|
+
opt_tys1 = @lead_tys[n..] + @opt_tys
|
|
920
|
+
opt_tys2 = bsig.lead_tys[n..] + bsig.opt_tys
|
|
921
|
+
opt_tys = []
|
|
922
|
+
[opt_tys1.size, opt_tys2.size].max.times do |i|
|
|
923
|
+
if opt_tys1[i] && opt_tys2[i]
|
|
924
|
+
opt_tys << opt_tys1[i].union(opt_tys2[i])
|
|
925
|
+
else
|
|
926
|
+
opt_tys << (opt_tys1[i] || opt_tys2[i])
|
|
1032
927
|
end
|
|
1033
928
|
end
|
|
1034
|
-
|
|
1035
|
-
end
|
|
1036
|
-
|
|
1037
|
-
if fargs.rest_ty
|
|
1038
|
-
return false if aargs.size < fargs.lead_tys.size + fargs.post_tys.size
|
|
1039
|
-
aargs.shift(fargs.lead_tys.size).zip(fargs.lead_tys) do |aarg, farg|
|
|
1040
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1041
|
-
end
|
|
1042
|
-
aargs.pop(fargs.post_tys.size).zip(fargs.post_tys) do |aarg, farg|
|
|
1043
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1044
|
-
end
|
|
1045
|
-
fargs.opt_tys.each do |farg|
|
|
1046
|
-
aarg = aargs.shift
|
|
1047
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1048
|
-
end
|
|
1049
|
-
aargs.each do |aarg|
|
|
1050
|
-
return false unless aarg.consistent?(fargs.rest_ty, subst)
|
|
1051
|
-
end
|
|
1052
|
-
else
|
|
1053
|
-
return false if aargs.size < fargs.lead_tys.size + fargs.post_tys.size
|
|
1054
|
-
return false if aargs.size > fargs.lead_tys.size + fargs.post_tys.size + fargs.opt_tys.size
|
|
1055
|
-
aargs.shift(fargs.lead_tys.size).zip(fargs.lead_tys) do |aarg, farg|
|
|
1056
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1057
|
-
end
|
|
1058
|
-
aargs.pop(fargs.post_tys.size).zip(fargs.post_tys) do |aarg, farg|
|
|
1059
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1060
|
-
end
|
|
1061
|
-
aargs.zip(fargs.opt_tys) do |aarg, farg|
|
|
1062
|
-
return false unless aarg.consistent?(farg, subst)
|
|
1063
|
-
end
|
|
1064
|
-
end
|
|
1065
|
-
# XXX: fargs.keyword_tys
|
|
1066
|
-
|
|
1067
|
-
case fargs.blk_ty
|
|
1068
|
-
when Type::TypedProc
|
|
1069
|
-
return false if @blk_ty == Type.nil
|
|
1070
|
-
when Type.nil
|
|
1071
|
-
return false if @blk_ty != Type.nil
|
|
1072
|
-
when Type::Any
|
|
1073
|
-
else
|
|
1074
|
-
raise "unknown typo of formal block signature"
|
|
1075
|
-
end
|
|
1076
|
-
true
|
|
1077
|
-
end
|
|
1078
|
-
|
|
1079
|
-
def screen_name(scratch)
|
|
1080
|
-
aargs = @lead_tys.map {|ty| ty.screen_name(scratch) }
|
|
1081
|
-
if @rest_ty
|
|
1082
|
-
aargs << ("*" + @rest_ty.screen_name(scratch))
|
|
1083
|
-
end
|
|
1084
|
-
if @kw_ty
|
|
1085
|
-
aargs << ("**" + @kw_ty.screen_name(scratch)) # TODO: Hash notation -> keyword notation
|
|
929
|
+
BlockSignature.new(lead_tys, opt_tys, nil, @blk_ty.union(bsig.blk_ty))
|
|
1086
930
|
end
|
|
1087
|
-
s = "(#{ aargs.join(", ") })"
|
|
1088
|
-
s << " { #{ scratch.proc_screen_name(@blk_ty) } }" if @blk_ty != Type.nil
|
|
1089
|
-
s
|
|
1090
931
|
end
|
|
1091
932
|
end
|
|
1092
933
|
end
|