typeprof 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c01f41e130c6e3bbc112520904ed710a75dea1b167ea112e96a321cd1bceadf
4
- data.tar.gz: 1b61e75c07cc8d24570bd998ea852f850b22264aaa1eed1bda425efe11660fba
3
+ metadata.gz: 3a0f0b09ed34d40d00fc280008ae562863d6cb3e19bd0a421e1fc5ac5023a5ea
4
+ data.tar.gz: dd7ee377c5dee6d2ad7bbd274844a5136c59f7fab5f7491cde11fcac628d44c2
5
5
  SHA512:
6
- metadata.gz: ba66753bc6fe6b21939449ede398ee31c27aa06893884a3e93baebcf60439c9552fa0b1da1b8fd6158f83ef012966f275c83055ba94d2974c10f694e031c40ab
7
- data.tar.gz: 6f6d9e688c024eefead28c9fe847686d172919a0c3ae0f5f6441bcb79d947710265e77dc52e4c5d44be699d7ea6bcd978943d0a898621956e6991a19c3874ac0
6
+ metadata.gz: 66e931b5f52fb1436707623ad1fba698bed0cf6e922971fbd0b5c99e28e4c14f5737e5f92406a457c5b96205ef8ad58f31aa739465f3aa57ca8149695c1bda42
7
+ data.tar.gz: 679c4b14a7bad664ba17f2aa27aa999a27584d7b9fdbe40bbb82fc4ea836ef867621a34077de5931c6c26fc9e9c9a2908bb4901914fa587f1b84cdd75dcde0ea
data/.gitignore CHANGED
@@ -7,3 +7,4 @@ testbed/diff-lcs
7
7
  pkg/
8
8
  *.log
9
9
  .*.sw*
10
+ *.dump
data/Gemfile CHANGED
@@ -7,7 +7,7 @@ group :development do
7
7
  gem "rake"
8
8
  gem "stackprof"
9
9
  gem "test-unit"
10
- gem "simplecov", github: "simplecov-ruby/simplecov"
11
- gem "simplecov-html", github: "simplecov-ruby/simplecov-html", ref: "6567856c6940e285bbb70cce98edca60c7dfefdd"
10
+ gem "simplecov"
11
+ gem "simplecov-html"
12
12
  gem "coverage-helpers"
13
13
  end
@@ -1,23 +1,8 @@
1
- GIT
2
- remote: https://github.com/simplecov-ruby/simplecov-html.git
3
- revision: 6567856c6940e285bbb70cce98edca60c7dfefdd
4
- ref: 6567856c6940e285bbb70cce98edca60c7dfefdd
5
- specs:
6
- simplecov-html (0.12.2)
7
-
8
- GIT
9
- remote: https://github.com/simplecov-ruby/simplecov.git
10
- revision: 80700ec9f9b5ae426c22d06f62620f7e7b71ff42
11
- specs:
12
- simplecov (0.18.5)
13
- docile (~> 1.1)
14
- simplecov-html (~> 0.11)
15
-
16
1
  PATH
17
2
  remote: .
18
3
  specs:
19
- typeprof (0.1.4)
20
- rbs (>= 0.12.0)
4
+ typeprof (0.3.0)
5
+ rbs (>= 0.13.1)
21
6
 
22
7
  GEM
23
8
  remote: https://rubygems.org/
@@ -27,7 +12,11 @@ GEM
27
12
  power_assert (1.2.0)
28
13
  rake (13.0.1)
29
14
  rbs (0.14.0)
30
- stackprof (0.2.15)
15
+ simplecov (0.19.1)
16
+ docile (~> 1.1)
17
+ simplecov-html (~> 0.11)
18
+ simplecov-html (0.12.3)
19
+ stackprof (0.2.16)
31
20
  test-unit (3.3.6)
32
21
  power_assert
33
22
 
@@ -37,8 +26,8 @@ PLATFORMS
37
26
  DEPENDENCIES
38
27
  coverage-helpers
39
28
  rake
40
- simplecov!
41
- simplecov-html!
29
+ simplecov
30
+ simplecov-html
42
31
  stackprof
43
32
  test-unit
44
33
  typeprof!
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Yusuke Endoh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -24,7 +24,8 @@ $ typeprof sig/app.rbs app.rb -o sig/app.gen.rbs
24
24
 
25
25
  * `-o OUTFILE`: 標準出力ではなく、指定ファイル名に出力する
26
26
  * `-q`: 解析の進捗を表示しない
27
- * `-v`: 解析の詳細ログを表示する(現状ではデバッグ用出力に近い)
27
+ * `-v`: `-fshow-errors`の別名
28
+ * `-d`: 解析の詳細ログを表示する(現状ではデバッグ用出力に近い)
28
29
  * `-I DIR`: `require`のファイル探索ディレクトリを追加する
29
30
  * `-r GEMNAME`: `GEMNAME`に対応するRBSをロードする
30
31
  * `--exclude-dir DIR`: `DIR`以下のファイルの解析結果を出力から省略する。後に指定されているほうが優先される(`--include-dir foo --exclude-dir foo/bar`の場合う、foo/bar/baz.rbの結果は出力されず、foo/baz.rbの結果は出力される)。
data/doc/doc.md CHANGED
@@ -24,7 +24,8 @@ Here is a list of currently avaiable options:
24
24
 
25
25
  * `-o OUTFILE`: Write the analyze result to OUTFILE instead of standard output
26
26
  * `-q`: Hide the progress indicator
27
- * `-v`: Show the analysis log (Currently, the log is just for debugging and may become very huge)
27
+ * `-v`: Alias to `-fshow-errors`
28
+ * `-d`: Show the analysis log (Currently, the log is just for debugging and may become very huge)
28
29
  * `-I DIR`: Add `DIR` to the file search path of `require`
29
30
  * `-r GEMNAME`: Load the RBS files of `GEMNAME`
30
31
  * `--exclude-dir DIR`: Omit the result of files that are placed under the directory `DIR`. If there are some directory specifications, the latter one is stronger. (Assuming that `--include-dir foo --exclude-dir foo/bar` is specified, the analysis result of foo/bar/baz.rb is omitted, but foo/baz.rb is shown.)
@@ -234,7 +235,7 @@ If a method returns different abstract values, it can lead to retrospective exec
234
235
  Even after TypeProf traced all programs as possible, there may be methods or blocks that aren't executed.
235
236
  For example, a method is not executed if it is called from nowhere; this is typical for library method that has no test.
236
237
  (Basically, when you use TypeProf, it is recommended to invoke all methods with supposed argument types.)
237
- TypeProf forcibly calls these unreachable methods and blocks with `untyped` as argumetns.
238
+ TypeProf forcibly calls these unreachable methods and blocks with `untyped` as arguments.
238
239
 
239
240
  ```
240
241
  def foo(n)
@@ -2,4 +2,5 @@
2
2
 
3
3
  require_relative "../lib/typeprof"
4
4
 
5
- TypeProf::CLI.new(ARGV).run
5
+ config = TypeProf::CLI.parse(ARGV)
6
+ TypeProf.analyze(config)
@@ -1,5 +1,6 @@
1
1
  module TypeProf end
2
2
 
3
+ require_relative "typeprof/config"
3
4
  require_relative "typeprof/insns-def"
4
5
  require_relative "typeprof/utils"
5
6
  require_relative "typeprof/type"
@@ -128,7 +128,13 @@ module TypeProf
128
128
  other.type_params.internal_hash.each do |id, elems|
129
129
  elems2 = type_params[id]
130
130
  if elems2
131
- type_params[id] = elems.union(elems2) if elems != elems2
131
+ if elems != elems2
132
+ if elems.is_a?(Array) # should be refactored as Cell::Elements
133
+ type_params[id] = elems.zip(elems2).map {|elem1, elem2| elem1.union(elem2) }
134
+ else
135
+ type_params[id] = elems.union(elems2)
136
+ end
137
+ end
132
138
  else
133
139
  type_params[id] = elems
134
140
  end
@@ -176,6 +182,13 @@ module TypeProf
176
182
  Env.new(@static_env, Utils.array_update(@locals, idx, ty), @stack, @type_params)
177
183
  end
178
184
 
185
+ def deploy_cell_type(alloc_site, elems, base_ty)
186
+ local_ty = Type::LocalCell.new(alloc_site, base_ty)
187
+ type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ alloc_site => elems }))
188
+ nenv = Env.new(@static_env, @locals, @stack, type_params)
189
+ return nenv, local_ty
190
+ end
191
+
179
192
  def deploy_array_type(alloc_site, elems, base_ty)
180
193
  local_ty = Type::LocalArray.new(alloc_site, base_ty)
181
194
  type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ alloc_site => elems }))
@@ -245,6 +258,8 @@ module TypeProf
245
258
  @loaded_features = {}
246
259
 
247
260
  @rbs_reader = RBSReader.new
261
+
262
+ @terminated = false
248
263
  end
249
264
 
250
265
  attr_reader :return_envs, :loaded_features, :rbs_reader
@@ -259,7 +274,7 @@ module TypeProf
259
274
  env2 = @ep2env[ep]
260
275
  if env2
261
276
  nenv = env2.merge(env)
262
- if !nenv.eql?(env2) && !@worklist.member?(ep)
277
+ if nenv != env2 && !@worklist.member?(ep)
263
278
  @worklist.insert(ep.key, ep)
264
279
  end
265
280
  @ep2env[ep] = nenv
@@ -550,26 +565,29 @@ module TypeProf
550
565
  @iseq_method_to_ctxs[iseq_mdef] << ctx
551
566
  end
552
567
 
553
- def add_callsite!(callee_ctx, fargs, caller_ep, caller_env, &ctn)
568
+ def add_callsite!(callee_ctx, caller_ep, caller_env, &ctn)
554
569
  @executed_iseqs << callee_ctx.iseq if callee_ctx.is_a?(Context)
555
570
 
556
571
  @callsites[callee_ctx] ||= {}
557
572
  @callsites[callee_ctx][caller_ep] = ctn
558
573
  merge_return_env(caller_ep) {|env| env ? env.merge(caller_env) : caller_env }
559
574
 
560
- if @sig_fargs[callee_ctx]
561
- @sig_fargs[callee_ctx] = @sig_fargs[callee_ctx].merge(fargs)
562
- else
563
- @sig_fargs[callee_ctx] = fargs
564
- end
565
575
  ret_ty = @sig_ret[callee_ctx] ||= Type.bot
566
- unless ret_ty.eql?(Type.bot)
576
+ if ret_ty != Type.bot
567
577
  @callsites[callee_ctx].each do |caller_ep, ctn|
568
578
  ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
569
579
  end
570
580
  end
571
581
  end
572
582
 
583
+ def add_signature!(callee_ctx, fargs)
584
+ if @sig_fargs[callee_ctx]
585
+ @sig_fargs[callee_ctx] = @sig_fargs[callee_ctx].merge(fargs)
586
+ else
587
+ @sig_fargs[callee_ctx] = fargs
588
+ end
589
+ end
590
+
573
591
  def merge_return_env(caller_ep)
574
592
  @return_envs[caller_ep] = yield @return_envs[caller_ep]
575
593
  end
@@ -769,27 +787,35 @@ module TypeProf
769
787
  end
770
788
 
771
789
  def type_profile
772
- time = Time.now
773
- step_counter = 0
790
+ start_time = tick = Time.now
791
+ iter_counter = 0
774
792
  stat_eps = Utils::MutableSet.new
793
+
775
794
  while true
776
795
  until @worklist.empty?
777
- @ep = @worklist.deletemin
796
+ ep = @worklist.deletemin
778
797
 
779
- step_counter += 1
798
+ iter_counter += 1
780
799
  if Config.verbose >= 1
781
- time2 = Time.now
782
- if time2 - time >= 1
783
- time = time2
784
- $stderr << "\rType Profiling... (%d steps @ %s)\e[K" % [step_counter, @ep.source_location]
800
+ tick2 = Time.now
801
+ if tick2 - tick >= 1
802
+ tick = tick2
803
+ $stderr << "\rType Profiling... (%d steps @ %s)\e[K" % [iter_counter, ep.source_location]
785
804
  $stderr.flush
786
805
  end
787
806
  end
788
807
 
789
- stat_eps << @ep
790
- step(@ep)
808
+ if (Config.max_sec && Time.now - start_time >= Config.max_sec) || (Config.max_iter && Config.max_iter <= iter_counter)
809
+ @terminated = true
810
+ break
811
+ end
812
+
813
+ stat_eps << ep
814
+ step(ep)
791
815
  end
792
816
 
817
+ break if @terminated
818
+
793
819
  # XXX: it would be good to provide no-dummy-execution mode.
794
820
  # It should work as a bit smarter "rbs prototype rb";
795
821
  # show all method definitions as "untyped" arguments and return values
@@ -809,37 +835,6 @@ module TypeProf
809
835
  merge_env(ep, env)
810
836
  add_iseq_method_call!(meth, ep.ctx)
811
837
 
812
- fargs_format = iseq.fargs_format
813
- lead_tys = [Type.any] * (fargs_format[:lead_num] || 0)
814
- opt_tys = fargs_format[:opt] ? [] : nil
815
- post_tys = [Type.any] * (fargs_format[:post_num] || 0)
816
- if fargs_format[:kwbits]
817
- kw_tys = []
818
- fargs_format[:keyword].each do |kw|
819
- case
820
- when kw.is_a?(Symbol) # required keyword
821
- key = kw
822
- req = true
823
- ty = Type.any
824
- when kw.size == 2 # optional keyword (default value is a literal)
825
- key, ty = *kw
826
- ty = Type.guess_literal_type(ty)
827
- ty = ty.type if ty.is_a?(Type::Literal)
828
- else # optional keyword
829
- key, = kw
830
- req = false
831
- ty = Type.any
832
- end
833
- kw_tys << [req, key, ty]
834
- end
835
- else
836
- kw_tys = nil
837
- end
838
- fargs = FormalArguments.new(lead_tys, opt_tys, nil, post_tys, kw_tys, nil, nil)
839
- add_callsite!(ep.ctx, fargs, nil, nil) do |_ret_ty, _ep, _env|
840
- # ignore
841
- end
842
-
843
838
  when :block
844
839
  epenvs = dummy_continuation
845
840
  epenvs.each do |ep, env|
@@ -853,6 +848,8 @@ module TypeProf
853
848
  end
854
849
 
855
850
  def report(stat_eps, output)
851
+ Reporters.show_message(@terminated, output)
852
+
856
853
  Reporters.show_error(@errors, @backward_edges, output)
857
854
 
858
855
  Reporters.show_reveal_types(self, @reveal_types, output)
@@ -895,6 +892,24 @@ module TypeProf
895
892
  ctx = Context.new(iseq, cref, mid)
896
893
  ep = ExecutionPoint.new(ctx, 0, nil)
897
894
  locals = [Type.any] * iseq.locals.size
895
+
896
+ keyword = iseq.fargs_format[:keyword]
897
+ if keyword
898
+ kw_start = iseq.fargs_format[:kwbits]
899
+ kw_start -= iseq.fargs_format[:keyword].size if kw_start
900
+ keyword.each_with_index do |kw, i|
901
+ case
902
+ when kw.is_a?(Symbol) # required keyword
903
+ when kw.size == 2 # optional keyword (default value is a literal)
904
+ _key, default_ty = *kw
905
+ default_ty = Type.guess_literal_type(default_ty)
906
+ default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
907
+ locals[kw_start + i] = default_ty.union(Type.any)
908
+ else # optional keyword (default value is an expression)
909
+ end
910
+ end
911
+ end
912
+
898
913
  env = Env.new(StaticEnv.new(recv, Type.any, false), locals, [], Utils::HashWrapper.new({}))
899
914
 
900
915
  @pending_execution[iseq] ||= [:method, [meth, ep, env]]
@@ -939,6 +954,61 @@ module TypeProf
939
954
  end
940
955
 
941
956
  case insn
957
+ when :_method_body
958
+ # XXX: reconstruct and record the method signature
959
+ iseq = ep.ctx.iseq
960
+ lead_num = iseq.fargs_format[:lead_num] || 0
961
+ opt = iseq.fargs_format[:opt] || [0]
962
+ rest_start = iseq.fargs_format[:rest_start]
963
+ post_start = iseq.fargs_format[:post_start]
964
+ post_num = iseq.fargs_format[:post_num] || 0
965
+ kw_start = iseq.fargs_format[:kwbits]
966
+ keyword = iseq.fargs_format[:keyword]
967
+ kw_start -= keyword.size if kw_start
968
+ kw_rest = iseq.fargs_format[:kwrest]
969
+ block_start = iseq.fargs_format[:block_start]
970
+
971
+ lead_tys = env.locals[0, lead_num].map {|ty| globalize_type(ty, env, ep) }
972
+ opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : nil
973
+ if rest_start # XXX:squash
974
+ ty = globalize_type(env.locals[lead_num + opt.size - 1], env, ep)
975
+ rest_ty = Type.bot
976
+ ty.each_child_global do |ty|
977
+ if ty.is_a?(Type::Array)
978
+ rest_ty = rest_ty.union(ty.elems.squash)
979
+ else
980
+ # XXX: to_ary?
981
+ rest_ty = rest_ty.union(ty)
982
+ end
983
+ end
984
+ end
985
+ post_tys = (post_start ? env.locals[post_start, post_num] : []).map {|ty| globalize_type(ty, env, ep) }
986
+ if keyword
987
+ kw_tys = []
988
+ keyword.each_with_index do |kw, i|
989
+ case
990
+ when kw.is_a?(Symbol) # required keyword
991
+ key = kw
992
+ req = true
993
+ when kw.size == 2 # optional keyword (default value is a literal)
994
+ key, default_ty = *kw
995
+ default_ty = Type.guess_literal_type(default_ty)
996
+ default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
997
+ req = false
998
+ else # optional keyword (default value is an expression)
999
+ key, = kw
1000
+ req = false
1001
+ end
1002
+ ty = env.locals[kw_start + i]
1003
+ ty = ty.union(default_ty) if default_ty
1004
+ ty = globalize_type(ty, env, ep)
1005
+ kw_tys << [req, key, ty]
1006
+ end
1007
+ end
1008
+ kw_rest_ty = globalize_type(env.locals[kw_rest], env, ep) if kw_rest
1009
+ blk_ty = block_start ? globalize_type(env.locals[block_start], env, ep) : Type.nil
1010
+ fargs = FormalArguments.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
1011
+ add_signature!(ep.ctx, fargs)
942
1012
  when :putspecialobject
943
1013
  kind, = operands
944
1014
  ty = case kind
@@ -1071,7 +1141,7 @@ module TypeProf
1071
1141
  elsif superclass == Type.any
1072
1142
  warn(ep, "superclass is any; Object is used instead")
1073
1143
  superclass = Type::Builtin[:obj]
1074
- elsif superclass.eql?(Type.nil)
1144
+ elsif superclass == Type.nil
1075
1145
  superclass = Type::Builtin[:obj]
1076
1146
  elsif superclass.is_a?(Type::Instance)
1077
1147
  warn(ep, "superclass is an instance; Object is used instead")
@@ -1111,7 +1181,7 @@ module TypeProf
1111
1181
  locals = [Type.nil] * iseq.locals.size
1112
1182
  nenv = Env.new(StaticEnv.new(recv, blk, false), locals, [], Utils::HashWrapper.new({}))
1113
1183
  merge_env(nep, nenv)
1114
- add_callsite!(nep.ctx, nil, ep, env) do |ret_ty, ep, env|
1184
+ add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
1115
1185
  nenv, ret_ty = localize_type(ret_ty, env, ep)
1116
1186
  nenv = nenv.push(ret_ty)
1117
1187
  merge_env(ep.next, nenv)
@@ -1164,15 +1234,15 @@ module TypeProf
1164
1234
  env, aargs = env.pop(orig_argc)
1165
1235
  blk = env.static_env.blk_ty
1166
1236
  case
1167
- when blk.eql?(Type.nil)
1237
+ when blk == Type.nil
1168
1238
  env = env.push(Type.any)
1169
- when blk.eql?(Type.any)
1239
+ when blk == Type.any
1170
1240
  #warn(ep, "block is any")
1171
1241
  env = env.push(Type.any)
1172
1242
  else # Proc
1173
1243
  blk_nil = Type.nil
1174
1244
  #
1175
- aargs = ActualArguments.new(aargs, nil, nil, blk_nil)
1245
+ aargs = ActualArguments.new(aargs, nil, {}, blk_nil)
1176
1246
  do_invoke_block(true, env.static_env.blk_ty, aargs, ep, env) do |ret_ty, ep, env|
1177
1247
  nenv, ret_ty, = localize_type(ret_ty, env, ep)
1178
1248
  nenv = nenv.push(ret_ty)
@@ -1266,7 +1336,7 @@ module TypeProf
1266
1336
  raise if iseq.locals != []
1267
1337
  nenv = Env.new(env.static_env, [], [], nil)
1268
1338
  merge_env(nep, nenv)
1269
- add_callsite!(nep.ctx, nil, ep, env) do |ret_ty, ep, env|
1339
+ add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
1270
1340
  nenv, ret_ty = localize_type(ret_ty, env, ep)
1271
1341
  nenv = nenv.push(ret_ty)
1272
1342
  merge_env(ep.next, nenv)
@@ -1282,7 +1352,7 @@ module TypeProf
1282
1352
 
1283
1353
  # TODO: it works for only simple cases: `x = nil; x || 1`
1284
1354
  # It would be good to merge "dup; branchif" to make it context-sensitive-like
1285
- falsy = ty.eql?(Type.nil)
1355
+ falsy = ty == Type.nil
1286
1356
 
1287
1357
  merge_env(ep_then, env)
1288
1358
  merge_env(ep_else, env) unless branchtype == :if && falsy
@@ -1428,11 +1498,11 @@ module TypeProf
1428
1498
  when :getconstant
1429
1499
  name, = operands
1430
1500
  env, (cbase, _allow_nil,) = env.pop(2)
1431
- if cbase.eql?(Type.nil)
1501
+ if cbase == Type.nil
1432
1502
  ty = search_constant(ep.ctx.cref, name)
1433
1503
  env, ty = localize_type(ty, env, ep)
1434
1504
  env = env.push(ty)
1435
- elsif cbase.eql?(Type.any)
1505
+ elsif cbase == Type.any
1436
1506
  env = env.push(Type.any) # XXX: warning needed?
1437
1507
  else
1438
1508
  ty = get_constant(cbase, name)
@@ -1612,7 +1682,7 @@ module TypeProf
1612
1682
  locals = [Type.nil] * iseq.locals.size
1613
1683
  nenv = Env.new(env.static_env, locals, [], Utils::HashWrapper.new({}))
1614
1684
  merge_env(nep, nenv)
1615
- add_callsite!(nep.ctx, nil, cont_ep, cont_env) do |ret_ty, ep, env|
1685
+ add_callsite!(nep.ctx, cont_ep, cont_env) do |ret_ty, ep, env|
1616
1686
  nenv, ret_ty = localize_type(ret_ty, env, ep)
1617
1687
  nenv = nenv.push(ret_ty)
1618
1688
  merge_env(ep.jump(cont), nenv)
@@ -1699,33 +1769,38 @@ module TypeProf
1699
1769
  _, (ty,) = ty.elems.take_last(1)
1700
1770
  case ty
1701
1771
  when Type::Hash
1702
- kw_ty = ty
1772
+ kw_tys = ty.elems.to_keywords
1703
1773
  when Type::Union
1704
1774
  hash_elems = nil
1705
1775
  ty.elems&.each do |(container_kind, base_type), elems|
1706
1776
  if container_kind == Type::Hash
1777
+ elems.to_keywords
1707
1778
  hash_elems = hash_elems ? hash_elems.union(elems) : elems
1708
1779
  end
1709
1780
  end
1710
- hash_elems ||= Type::Hash::Elements.new({Type.any => Type.any})
1711
- kw_ty = Type::Hash.new(hash_elems, Type::Instance.new(Type::Builtin[:hash]))
1781
+ if hash_elems
1782
+ kw_tys = hash_elems.to_keywords
1783
+ else
1784
+ kw_tys = { nil => Type.any }
1785
+ end
1712
1786
  else
1713
1787
  warn(ep, "non hash is passed to **kwarg?") unless ty == Type.any
1714
- kw_ty = nil
1788
+ kw_tys = { nil => Type.any }
1715
1789
  end
1716
1790
  else
1717
1791
  raise NotImplementedError
1718
1792
  end
1719
- # XXX: should we remove kw_ty from rest_ty?
1793
+ else
1794
+ kw_tys = {}
1720
1795
  end
1721
- aargs = ActualArguments.new(aargs, rest_ty, kw_ty, blk_ty)
1796
+ aargs = ActualArguments.new(aargs, rest_ty, kw_tys, blk_ty)
1722
1797
  elsif flag_args_kw_splat
1723
1798
  last = aargs.last
1724
1799
  ty = globalize_type(last, env, ep)
1725
1800
  case ty
1726
1801
  when Type::Hash
1727
1802
  aargs = aargs[0..-2]
1728
- kw_ty = ty
1803
+ kw_tys = ty.elems.to_keywords
1729
1804
  when Type::Union
1730
1805
  hash_elems = nil
1731
1806
  ty.elems.each do |(container_kind, base_type), elems|
@@ -1733,31 +1808,30 @@ module TypeProf
1733
1808
  hash_elems = hash_elems ? hash_elems.union(elems) : elems
1734
1809
  end
1735
1810
  end
1736
- hash_elems ||= Type::Hash::Elements.new({Type.any => Type.any})
1737
- kw_ty = Type::Hash.new(hash_elems, Type::Instance.new(Type::Builtin[:hash]))
1811
+ if hash_elems
1812
+ kw_tys = hash_elems.to_keywords
1813
+ else
1814
+ kw_tys = { nil => Type.any }
1815
+ end
1738
1816
  when Type::Any
1739
1817
  aargs = aargs[0..-2]
1740
- kw_ty = ty
1818
+ kw_tys = { nil => Type.any }
1741
1819
  else
1742
1820
  warn(ep, "non hash is passed to **kwarg?")
1743
- kw_ty = nil
1821
+ kw_tys = { nil => Type.any }
1744
1822
  end
1745
- aargs = ActualArguments.new(aargs, nil, kw_ty, blk_ty)
1823
+ aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
1746
1824
  elsif flag_args_kwarg
1747
1825
  kw_vals = aargs.pop(kw_arg.size)
1748
1826
 
1749
- kw_ty = Type.gen_hash do |h|
1750
- kw_arg.zip(kw_vals) do |key, v_ty|
1751
- k_ty = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
1752
- h[k_ty] = v_ty
1753
- end
1827
+ kw_tys = {}
1828
+ kw_arg.zip(kw_vals) do |key, v_ty|
1829
+ kw_tys[key] = v_ty
1754
1830
  end
1755
1831
 
1756
- # kw_ty is Type::Hash, but we don't have to localize it, maybe?
1757
-
1758
- aargs = ActualArguments.new(aargs, nil, kw_ty, blk_ty)
1832
+ aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
1759
1833
  else
1760
- aargs = ActualArguments.new(aargs, nil, nil, blk_ty)
1834
+ aargs = ActualArguments.new(aargs, nil, {}, blk_ty)
1761
1835
  end
1762
1836
 
1763
1837
  if blk_iseq
@@ -1781,7 +1855,11 @@ module TypeProf
1781
1855
  meth.do_send(recv, mid, aargs, ep, env, self, &ctn)
1782
1856
  end
1783
1857
  else
1784
- if recv != Type.any
1858
+ case recv
1859
+ when Type::Void
1860
+ error(ep, "void's method is called: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
1861
+ when Type::Any
1862
+ else
1785
1863
  error(ep, "undefined method: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
1786
1864
  end
1787
1865
  ctn[Type.any, ep, env]
@@ -1800,7 +1878,7 @@ module TypeProf
1800
1878
  blk_env = blk_env.replace_recv_ty(replace_recv_ty) if replace_recv_ty
1801
1879
  arg_blk = aargs.blk_ty
1802
1880
  aargs_ = aargs.lead_tys.map {|aarg| globalize_type(aarg, env, ep) }
1803
- # XXX: aargs.opt_tys and aargs.kw_ty
1881
+ # XXX: aargs.opt_tys and aargs.kw_tys
1804
1882
  argc = blk_iseq.fargs_format[:lead_num] || 0
1805
1883
  # actual argc == 1, not array, formal argc == 1: yield 42 => do |x| : x=42
1806
1884
  # actual argc == 1, array, formal argc == 1: yield [42,43,44] => do |x| : x=[42,43,44]
@@ -1879,7 +1957,8 @@ module TypeProf
1879
1957
 
1880
1958
  add_yield!(ep.ctx, globalize_type(aargs, env, ep), nep.ctx) if given_block
1881
1959
  add_block_to_ctx!(blk, nep.ctx)
1882
- add_callsite!(nep.ctx, nfargs, ep, env, &ctn)
1960
+ add_callsite!(nep.ctx, ep, env, &ctn)
1961
+ add_signature!(nep.ctx, nfargs)
1883
1962
  end
1884
1963
  end
1885
1964