typeprof 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -25,6 +25,13 @@ module TypeProf
25
25
  ntrace
26
26
  end
27
27
 
28
+ def show_message(terminated, output)
29
+ if terminated
30
+ output.puts "# CAUTION: Type profiling was terminated prematurely because of the limitation"
31
+ output.puts
32
+ end
33
+ end
34
+
28
35
  def show_error(errors, backward_edge, output)
29
36
  return if errors.empty?
30
37
  return unless Config.options[:show_errors]
@@ -87,8 +94,6 @@ module TypeProf
87
94
  def show(stat_eps, output)
88
95
  output.puts "# Classes" # and Modules
89
96
 
90
- stat_classes = {}
91
- stat_methods = {}
92
97
  first = true
93
98
 
94
99
  @class_defs.each_value do |class_def|
@@ -97,6 +102,11 @@ module TypeProf
97
102
  mod_def.name
98
103
  end
99
104
 
105
+ extended_mods = class_def.modules[true].filter_map do |mod_def, absolute_paths|
106
+ next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
107
+ mod_def.name
108
+ end
109
+
100
110
  explicit_methods = {}
101
111
  iseq_methods = {}
102
112
  attr_methods = {}
@@ -161,7 +171,7 @@ module TypeProf
161
171
  end
162
172
 
163
173
  if !class_def.absolute_path || Config.check_dir_filter(class_def.absolute_path) == :exclude
164
- next if included_mods.empty? && ivars.empty? && cvars.empty? && iseq_methods.empty? && attr_methods.empty?
174
+ next if included_mods.empty? && extended_mods.empty? && ivars.empty? && cvars.empty? && iseq_methods.empty? && attr_methods.empty?
165
175
  end
166
176
 
167
177
  output.puts unless first
@@ -176,6 +186,9 @@ module TypeProf
176
186
  included_mods.sort.each do |ty|
177
187
  output.puts " include #{ ty }"
178
188
  end
189
+ extended_mods.sort.each do |ty|
190
+ output.puts " extend #{ ty }"
191
+ end
179
192
  ivars.each do |var, ty, rbs_declared|
180
193
  s = rbs_declared ? "# " : " "
181
194
  output.puts s + "#{ var } : #{ ty }" unless var.start_with?("_")
@@ -199,10 +212,9 @@ module TypeProf
199
212
  end
200
213
 
201
214
  if ENV["TP_STAT"]
202
- output.puts "statistics:"
203
- output.puts " %d execution points" % stat_eps.size
204
- output.puts " %d classes" % stat_classes.size
205
- output.puts " %d methods (in total)" % stat_methods.size
215
+ output.puts ""
216
+ output.puts "# TypeProf statistics:"
217
+ output.puts "# %d execution points" % stat_eps.size
206
218
  end
207
219
  if ENV["TP_COVERAGE"]
208
220
  coverage = {}
@@ -212,7 +224,7 @@ module TypeProf
212
224
  (coverage[path] ||= [])[lineno] ||= 0
213
225
  (coverage[path] ||= [])[lineno] += 1
214
226
  end
215
- File.binwrite("coverage.dump", Marshal.dump(coverage))
227
+ File.binwrite("typeprof-analysis-coverage.dump", Marshal.dump(coverage))
216
228
  end
217
229
  end
218
230
  end
@@ -89,12 +89,13 @@ module TypeProf
89
89
  json = {}
90
90
 
91
91
  each_class_decl do |name, decls|
92
- super_class_name = get_super_class_name(name)
92
+ super_class_name = get_super_class_name(name, decls)
93
93
  klass = conv_type_name(name)
94
94
  superclass = super_class_name ? conv_type_name(super_class_name) : nil
95
95
 
96
96
  type_params = nil
97
97
  included_modules = []
98
+ extended_modules = []
98
99
  methods = {}
99
100
  ivars = {}
100
101
  cvars = {}
@@ -103,8 +104,6 @@ module TypeProf
103
104
  decls.each do |decl|
104
105
  decl = decl.decl
105
106
 
106
- raise NotImplementedError if decl.is_a?(RBS::AST::Declarations::Interface) # XXX
107
-
108
107
  type_params2 = decl.type_params.params.map {|param| [param.name, param.variance] }
109
108
  raise "inconsistent type parameter declaration" if type_params && type_params != type_params2
110
109
  type_params = type_params2
@@ -160,6 +159,15 @@ module TypeProf
160
159
  # including an interface is not supported yet
161
160
  end
162
161
 
162
+ when RBS::AST::Members::Extend
163
+ name = member.name
164
+ if name.kind == :class
165
+ mod = conv_type_name(name)
166
+ extended_modules << mod
167
+ else
168
+ # extending a module with an interface is not supported yet
169
+ end
170
+
163
171
  when RBS::AST::Members::InstanceVariable
164
172
  ivars[member.name] = conv_type(member.type)
165
173
  when RBS::AST::Members::ClassVariable
@@ -184,6 +192,7 @@ module TypeProf
184
192
  superclass: superclass,
185
193
  members: {
186
194
  included_modules: included_modules,
195
+ extended_modules: extended_modules,
187
196
  methods: methods,
188
197
  ivars: ivars,
189
198
  cvars: cvars,
@@ -196,8 +205,6 @@ module TypeProf
196
205
  end
197
206
 
198
207
  def each_class_decl
199
- classes = []
200
-
201
208
  # topological sort
202
209
  # * superclasses and modules appear earlier than their subclasses (Object is earlier than String)
203
210
  # * namespace module appers earlier than its children (Process is earlier than Process::Status)
@@ -223,7 +230,9 @@ module TypeProf
223
230
  end
224
231
  end
225
232
 
226
- classes
233
+ @cur_env.interface_decls.each do |name, decl|
234
+ yield name, [decl]
235
+ end
227
236
  end
228
237
 
229
238
  def each_ancestor(decl, &blk)
@@ -236,15 +245,15 @@ module TypeProf
236
245
  end
237
246
  end
238
247
 
239
- def get_super_class_name(name)
248
+ def get_super_class_name(name, decls)
240
249
  return nil if name == RBS::BuiltinNames::BasicObject.name
241
250
 
242
- @all_env.class_decls[name].decls.each do |decl|
251
+ decls.each do |decl|
243
252
  decl = decl.decl
244
253
  case decl
245
254
  when RBS::AST::Declarations::Class
246
255
  return decl.super_class.name if decl.super_class
247
- when RBS::AST::Declarations::Module
256
+ when RBS::AST::Declarations::Module, RBS::AST::Declarations::Interface
248
257
  return nil
249
258
  else
250
259
  raise "unknown declaration: %p" % decl.class
@@ -320,6 +329,8 @@ module TypeProf
320
329
  raise NotImplementedError unless type.required_keywords.empty?
321
330
  raise NotImplementedError if type.rest_keywords
322
331
 
332
+ req = rbs_block.required
333
+
323
334
  lead_tys = type.required_positionals.map do |type|
324
335
  conv_type(type.type)
325
336
  end
@@ -329,7 +340,7 @@ module TypeProf
329
340
 
330
341
  ret_ty = conv_type(type.return_type)
331
342
 
332
- [lead_tys, opt_tys, ret_ty]
343
+ [req, lead_tys, opt_tys, ret_ty]
333
344
  end
334
345
 
335
346
  def conv_type(ty)
@@ -354,7 +365,7 @@ module TypeProf
354
365
  end
355
366
  when RBS::Types::Bases::Bool then [:bool]
356
367
  when RBS::Types::Bases::Any then [:any]
357
- when RBS::Types::Bases::Void then [:any]
368
+ when RBS::Types::Bases::Void then [:void]
358
369
  when RBS::Types::Bases::Self then [:self]
359
370
  when RBS::Types::Bases::Nil then [:nil]
360
371
  when RBS::Types::Bases::Bottom then [:union, []]
@@ -387,7 +398,7 @@ module TypeProf
387
398
  when "::_ToInt" then [:int]
388
399
  when "::_ToAry[U]" then [:array, [:Array], [], [:var, :U]]
389
400
  else
390
- [:any]
401
+ [:instance, conv_type_name(ty.name)]
391
402
  end
392
403
  when RBS::Types::Bases::Instance then [:any] # XXX: not implemented yet
393
404
  when RBS::Types::Record then [:any] # XXX: not implemented yet
@@ -419,7 +430,8 @@ module TypeProf
419
430
  end
420
431
 
421
432
  def self.import_rbs_file(scratch, rbs_path)
422
- Import.new(scratch, scratch.rbs_reader.load_path(Pathname(rbs_path))).import(true)
433
+ rbs_path = Pathname(rbs_path) unless rbs_path.is_a?(Pathname)
434
+ Import.new(scratch, scratch.rbs_reader.load_path(rbs_path)).import(true)
423
435
  end
424
436
 
425
437
  def initialize(scratch, json)
@@ -459,6 +471,7 @@ module TypeProf
459
471
 
460
472
  classes.each do |klass, members|
461
473
  included_modules = members[:included_modules]
474
+ extended_modules = members[:extended_modules]
462
475
  methods = members[:methods]
463
476
  ivars = members[:ivars]
464
477
  cvars = members[:cvars]
@@ -468,6 +481,10 @@ module TypeProf
468
481
  @scratch.include_module(klass, path_to_klass(mod), nil)
469
482
  end
470
483
 
484
+ extended_modules.each do |mod|
485
+ @scratch.extend_module(klass, path_to_klass(mod), nil)
486
+ end
487
+
471
488
  methods.each do |(singleton, method_name), mdef|
472
489
  rbs_source = explicit ? rbs_sources[[singleton, method_name]] : nil
473
490
  mdef = conv_method_def(method_name, mdef, rbs_source)
@@ -499,7 +516,7 @@ module TypeProf
499
516
  end
500
517
 
501
518
  def conv_method_def(method_name, mdef, rbs_source)
502
- sig_rets = mdef.map do |sig_ret|
519
+ sig_rets = mdef.flat_map do |sig_ret|
503
520
  #type_params = sig_ret[:type_params] # XXX
504
521
  lead_tys = sig_ret[:lead_tys]
505
522
  opt_tys = sig_ret[:opt_tys]
@@ -517,24 +534,29 @@ module TypeProf
517
534
  req_kw_tys.each {|key, ty| kw_tys << [true, key, conv_type(ty)] }
518
535
  opt_kw_tys.each {|key, ty| kw_tys << [false, key, conv_type(ty)] }
519
536
  kw_rest_ty = conv_type(rest_kw_ty) if rest_kw_ty
520
- blk = blk ? conv_block(blk) : Type.nil
521
- fargs = FormalArguments.new(lead_tys, opt_tys, rest_ty, [], kw_tys, kw_rest_ty, blk)
537
+
538
+ blks = conv_block(blk)
522
539
 
523
540
  ret_ty = conv_type(ret_ty)
524
541
 
525
- [fargs, ret_ty]
526
- end.compact
542
+ blks.map do |blk|
543
+ [FormalArguments.new(lead_tys, opt_tys, rest_ty, [], kw_tys, kw_rest_ty, blk), ret_ty]
544
+ end
545
+ end
527
546
 
528
547
  TypedMethodDef.new(sig_rets, rbs_source)
529
548
  end
530
549
 
531
550
  def conv_block(blk)
532
- lead_tys, opt_tys, ret_ty = blk
551
+ return [Type.nil] unless blk
552
+ req, lead_tys, opt_tys, ret_ty = blk
533
553
  lead_tys = lead_tys.map {|ty| conv_type(ty) }
534
554
  opt_tys = opt_tys.map {|ty| conv_type(ty) }
535
555
  fargs = FormalArguments.new(lead_tys, opt_tys, nil, nil, nil, nil, nil)
536
556
  ret_ty = conv_type(ret_ty)
537
- Type::TypedProc.new(fargs, ret_ty, Type::Builtin[:proc])
557
+ ret = [Type::TypedProc.new(fargs, ret_ty, Type::Builtin[:proc])]
558
+ ret << Type.nil unless req
559
+ ret
538
560
  end
539
561
 
540
562
  def conv_type(ty)
@@ -542,6 +564,7 @@ module TypeProf
542
564
  when :class then path_to_klass(ty[1])
543
565
  when :instance then Type::Instance.new(path_to_klass(ty[1]))
544
566
  when :any then Type.any
567
+ when :void then Type::Void.new
545
568
  when :nil then Type.nil
546
569
  when :optional then Type.optional(conv_type(ty[1]))
547
570
  when :bool then Type.bool
@@ -32,6 +32,22 @@ module TypeProf
32
32
  @name, @path, @absolute_path, @start_lineno, @type,
33
33
  @locals, @fargs_format, catch_table, insns = *iseq
34
34
 
35
+ if @type == :method
36
+ if @fargs_format[:opt]
37
+ label = @fargs_format[:opt].last
38
+ i = insns.index(label) + 1
39
+ else
40
+ i = insns.find_index {|insn| insn.is_a?(Array) }
41
+ end
42
+ # skip keyword initialization
43
+ while insns[i][0] == :checkkeyword
44
+ raise if insns[i + 1][0] != :branchif
45
+ label = insns[i + 1][1]
46
+ i = insns.index(label) + 1
47
+ end
48
+ insns[i, 0] = [[:_method_body]]
49
+ end
50
+
35
51
  @insns = []
36
52
  @linenos = []
37
53
 
@@ -79,7 +95,7 @@ module TypeProf
79
95
  nil
80
96
  when Array
81
97
  insn, *operands = e
82
- operands = INSN_TABLE[insn].zip(operands).map do |type, operand|
98
+ operands = (INSN_TABLE[insn] || []).zip(operands).map do |type, operand|
83
99
  case type
84
100
  when "ISEQ"
85
101
  operand && ISeq.new(operand)
@@ -24,7 +24,7 @@ module TypeProf
24
24
  callee_ep = do_send_core(fargs, start_pc, recv, mid, scratch)
25
25
 
26
26
  scratch.add_iseq_method_call!(self, callee_ep.ctx)
27
- scratch.add_callsite!(callee_ep.ctx, fargs, caller_ep, caller_env, &ctn)
27
+ scratch.add_callsite!(callee_ep.ctx, caller_ep, caller_env, &ctn)
28
28
  end
29
29
  end
30
30
 
@@ -34,6 +34,7 @@ module TypeProf
34
34
  rest_start = @iseq.fargs_format[:rest_start]
35
35
  kw_start = @iseq.fargs_format[:kwbits]
36
36
  kw_start -= @iseq.fargs_format[:keyword].size if kw_start
37
+ kw_rest = @iseq.fargs_format[:kwrest]
37
38
  block_start = @iseq.fargs_format[:block_start]
38
39
 
39
40
  # XXX: need to check .rbs fargs and .rb fargs
@@ -72,13 +73,18 @@ module TypeProf
72
73
  end
73
74
  end
74
75
  if fargs.kw_tys
75
- fargs.kw_tys.each_with_index do |(_, _, ty), i|
76
- alloc_site2 = alloc_site.add_id(idx += 1)
76
+ fargs.kw_tys.each_with_index do |(_, key, ty), i|
77
+ alloc_site2 = alloc_site.add_id(key)
77
78
  nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
78
79
  nenv = nenv.local_update(kw_start + i, ty)
79
80
  end
80
81
  end
81
- # kwrest
82
+ if fargs.kw_rest_ty
83
+ ty = fargs.kw_rest_ty
84
+ alloc_site2 = alloc_site.add_id(:**)
85
+ nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
86
+ nenv = nenv.local_update(kw_rest, ty)
87
+ end
82
88
  nenv = nenv.local_update(block_start, fargs.blk_ty) if block_start
83
89
 
84
90
  scratch.merge_env(callee_ep, nenv)
@@ -158,18 +164,36 @@ module TypeProf
158
164
  # XXX: support self type in fargs
159
165
  subst = { Type::Var.new(:self) => recv }
160
166
  next unless aargs.consistent_with_formal_arguments?(fargs, subst)
161
- if recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
167
+ case
168
+ when recv.is_a?(Type::Cell) && recv_orig.is_a?(Type::LocalCell)
169
+ tyvars = recv.base_type.klass.type_params.map {|name,| Type::Var.new(name) }
170
+ tyvars.each_with_index do |tyvar, idx|
171
+ ty = subst[tyvar]
172
+ if ty
173
+ ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
174
+ ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id) do |elems|
175
+ Utils.array_update(elems, idx, elems[idx].union(ty))
176
+ end
177
+ end
178
+ end
179
+ tyvars.zip(recv.elems) do |tyvar, elem|
180
+ if subst[tyvar]
181
+ subst[tyvar] = subst[tyvar].union(elem)
182
+ else
183
+ subst[tyvar] = elem
184
+ end
185
+ end
186
+ when recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
162
187
  tyvar_elem = Type::Var.new(:Elem)
163
188
  if subst[tyvar_elem]
164
189
  ty = subst[tyvar_elem]
165
- alloc_site = AllocationSite.new(caller_ep).add_id(self)
166
190
  ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
167
191
  ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id) do |elems|
168
192
  elems.update(nil, ty)
169
193
  end
170
194
  end
171
195
  subst.merge!({ tyvar_elem => recv.elems.squash })
172
- elsif recv.is_a?(Type::Hash) && recv_orig.is_a?(Type::LocalHash)
196
+ when recv.is_a?(Type::Hash) && recv_orig.is_a?(Type::LocalHash)
173
197
  tyvar_k = Type::Var.new(:K)
174
198
  tyvar_v = Type::Var.new(:V)
175
199
  # XXX: need to support destructive operation
@@ -184,7 +208,7 @@ module TypeProf
184
208
  dummy_ep = ExecutionPoint.new(dummy_ctx, -1, caller_ep)
185
209
  dummy_env = Env.new(StaticEnv.new(recv, fargs.blk_ty, false), [], [], Utils::HashWrapper.new({}))
186
210
  if fargs.blk_ty.is_a?(Type::TypedProc)
187
- scratch.add_callsite!(dummy_ctx, nil, caller_ep, ncaller_env, &ctn)
211
+ scratch.add_callsite!(dummy_ctx, caller_ep, ncaller_env, &ctn)
188
212
  nfargs = fargs.blk_ty.fargs
189
213
  alloc_site = AllocationSite.new(caller_ep).add_id(self)
190
214
  nlead_tys = (nfargs.lead_tys + nfargs.opt_tys).map.with_index do |ty, i|
@@ -200,7 +224,7 @@ module TypeProf
200
224
  ty
201
225
  end
202
226
  0.upto(nfargs.opt_tys.size) do |n|
203
- naargs = ActualArguments.new(nlead_tys[0, nfargs.lead_tys.size + n], nil, nil, Type.nil) # XXX: support block to block?
227
+ naargs = ActualArguments.new(nlead_tys[0, nfargs.lead_tys.size + n], nil, {}, Type.nil) # XXX: support block to block?
204
228
  scratch.do_invoke_block(false, aargs.blk_ty, naargs, dummy_ep, dummy_env) do |blk_ret_ty, _ep, _env|
205
229
  subst2 = {}
206
230
  if blk_ret_ty.consistent?(fargs.blk_ty.ret_ty, subst2)
@@ -3,7 +3,7 @@ module TypeProf
3
3
  include Utils::StructuralEquality
4
4
 
5
5
  def initialize
6
- raise "cannot instanciate abstract type"
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)
@@ -810,8 +821,6 @@ module TypeProf
810
821
  raise if !!kws1[kw] != !!kws2[kw]
811
822
  end
812
823
  elsif @kw_tys || other.kw_tys
813
- puts
814
- p self, other
815
824
  (@kw_tys || other.kw_tys).each do |req,|
816
825
  raise if req
817
826
  end
@@ -865,15 +874,16 @@ module TypeProf
865
874
 
866
875
  # Arguments from caller side
867
876
  class ActualArguments
868
- def initialize(lead_tys, rest_ty, kw_ty, blk_ty)
877
+ def initialize(lead_tys, rest_ty, kw_tys, blk_ty)
869
878
  @lead_tys = lead_tys
870
879
  @rest_ty = rest_ty
871
- @kw_ty = kw_ty
880
+ @kw_tys = kw_tys # kw_tys should be {:key1 => Type.bool, :key2 => Type.bool, ...} or {nil => Type.bool}
881
+ raise if !kw_tys.is_a?(::Hash)
872
882
  @blk_ty = blk_ty
873
883
  raise unless blk_ty
874
884
  end
875
885
 
876
- attr_reader :lead_tys, :rest_ty, :kw_ty, :blk_ty
886
+ attr_reader :lead_tys, :rest_ty, :kw_tys, :blk_ty
877
887
 
878
888
  def merge(aargs)
879
889
  len = [@lead_tys.size, aargs.lead_tys.size].min
@@ -886,16 +896,25 @@ module TypeProf
886
896
  rest_ty = rest_ty.union(ty)
887
897
  end
888
898
  rest_ty = nil if rest_ty == Type.bot
889
- #kw_ty = @kw_ty.union(aargs.kw_ty) # TODO
899
+ kw_tys = @kw_tys.dup
900
+ aargs.kw_tys.each do |sym, ty|
901
+ if kw_tys[sym]
902
+ kw_tys[sym] = kw_tys[sym].union(ty)
903
+ else
904
+ kw_tys[sym] = ty
905
+ end
906
+ end
890
907
  blk_ty = @blk_ty.union(aargs.blk_ty)
891
- ActualArguments.new(lead_tys, rest_ty, kw_ty, blk_ty)
908
+ ActualArguments.new(lead_tys, rest_ty, kw_tys, blk_ty)
892
909
  end
893
910
 
894
911
  def globalize(caller_env, visited, depth)
895
912
  lead_tys = @lead_tys.map {|ty| ty.globalize(caller_env, visited, depth) }
896
913
  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)
914
+ kw_tys = @kw_tys.to_h do |key, ty|
915
+ [key, ty.globalize(caller_env, visited, depth)]
916
+ end
917
+ ActualArguments.new(lead_tys, rest_ty, kw_tys, @blk_ty)
899
918
  end
900
919
 
901
920
  def limit_size(limit)
@@ -942,6 +961,7 @@ module TypeProf
942
961
  lower_bound = upper_bound = 0
943
962
  end
944
963
 
964
+ a_kw_tys = @kw_tys.dup
945
965
  if keyword
946
966
  kw_tys = []
947
967
  keyword.each do |kw|
@@ -959,14 +979,12 @@ module TypeProf
959
979
  req = false
960
980
  end
961
981
 
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
982
+ if a_kw_tys.key?(key)
983
+ ty = a_kw_tys.delete(key)
984
+ else
985
+ ty = a_kw_tys[nil] || Type.bot
968
986
  end
969
- if ty == Type.bot
987
+ if ty == Type.bot && req
970
988
  yield "no argument for required keywords"
971
989
  return
972
990
  end
@@ -975,12 +993,18 @@ module TypeProf
975
993
  end
976
994
  end
977
995
  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 }
996
+ if a_kw_tys.key?(nil)
997
+ kw_rest_ty = Type.gen_hash {|h| h[Type.any] = a_kw_tys[nil] }
998
+ else
999
+ kw_rest_ty = Type.gen_hash do |h|
1000
+ a_kw_tys.each do |key, ty|
1001
+ sym = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
1002
+ h[sym] = ty
1003
+ end
1004
+ end
981
1005
  end
982
1006
  end
983
- #if @kw_ty
1007
+ #if @kw_tys
984
1008
  # yield "passed a keyword to non-keyword method"
985
1009
  #end
986
1010
 
@@ -1023,7 +1047,7 @@ module TypeProf
1023
1047
  lower_bound = [0, fargs.lead_tys.size + fargs.post_tys.size - aargs.size].max
1024
1048
  upper_bound = [0, lower_bound + fargs.opt_tys.size].max
1025
1049
  (lower_bound..upper_bound).each do |n|
1026
- tmp_aargs = ActualArguments.new(@lead_tys + [@rest_ty] * n, nil, @kw_ty, @blk_ty)
1050
+ tmp_aargs = ActualArguments.new(@lead_tys + [@rest_ty] * n, nil, @kw_tys, @blk_ty)
1027
1051
  if tmp_aargs.consistent_with_formal_arguments?(fargs, subst)
1028
1052
  return true
1029
1053
  end
@@ -1078,8 +1102,12 @@ module TypeProf
1078
1102
  if @rest_ty
1079
1103
  aargs << ("*" + @rest_ty.screen_name(scratch))
1080
1104
  end
1081
- if @kw_ty
1082
- aargs << ("**" + @kw_ty.screen_name(scratch)) # TODO: Hash notation -> keyword notation
1105
+ if @kw_tys.key?(nil)
1106
+ aargs << "(unknown key): #{ @kw_tys[nil].screen_name(scratch) }"
1107
+ else
1108
+ @kw_tys.sort.each do |key, ty|
1109
+ aargs << "#{ key }: #{ ty.screen_name(scratch) }"
1110
+ end
1083
1111
  end
1084
1112
  s = "(#{ aargs.join(", ") })"
1085
1113
  s << " { #{ scratch.proc_screen_name(@blk_ty) } }" if @blk_ty != Type.nil