typeprof 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -65,10 +65,14 @@ module TypeProf
65
65
 
66
66
  def object_s_new(recv, mid, aargs, ep, env, scratch, &ctn)
67
67
  ty = Type::Instance.new(recv)
68
+ if recv.type_params.size >= 1
69
+ ty = Type::Cell.new([Type.bot] * recv.type_params.size, ty)
70
+ env, ty = scratch.localize_type(ty, env, ep, AllocationSite.new(ep).add_id(:object_s_new))
71
+ end
68
72
  meths = scratch.get_method(recv, false, :initialize)
69
73
  meths.flat_map do |meth|
70
74
  meth.do_send(ty, :initialize, aargs, ep, env, scratch) do |ret_ty, ep, env|
71
- ctn[Type::Instance.new(recv), ep, env]
75
+ ctn[ty, ep, env]
72
76
  end
73
77
  end
74
78
  end
@@ -118,7 +122,7 @@ module TypeProf
118
122
  else
119
123
  mid_ty = aargs.rest_ty
120
124
  end
121
- aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs.kw_ty, aargs.blk_ty)
125
+ aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs.kw_tys, aargs.blk_ty)
122
126
  found = false
123
127
  mid_ty.each_child do |mid|
124
128
  if mid.is_a?(Type::Symbol)
@@ -138,7 +142,7 @@ module TypeProf
138
142
  ctn[type.any, ep, env]
139
143
  return
140
144
  end
141
- naargs = ActualArguments.new([recv], Type.nil, nil, Type.nil)
145
+ naargs = ActualArguments.new([recv], Type.nil, {}, Type.nil)
142
146
  given_block = env.static_env.blk_ty == recv
143
147
  scratch.do_invoke_block(given_block, aargs.blk_ty, naargs, ep, env, replace_recv_ty: recv) do |_ret_ty, ep|
144
148
  ctn[recv, ep, scratch.return_envs[ep]]
@@ -244,7 +248,10 @@ module TypeProf
244
248
  def array_aset(recv, mid, aargs, ep, env, scratch, &ctn)
245
249
  return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalArray)
246
250
 
247
- raise NotImplementedError if aargs.lead_tys.size != 2
251
+ if aargs.lead_tys.size != 2
252
+ #raise NotImplementedError # XXX
253
+ ctn[Type.any, ep, env]
254
+ end
248
255
 
249
256
  idx = aargs.lead_tys.first
250
257
  if idx.is_a?(Type::Literal)
@@ -316,14 +323,12 @@ module TypeProf
316
323
 
317
324
  def struct_initialize(recv, mid, aargs, ep, env, scratch, &ctn)
318
325
  recv = Type::Instance.new(recv)
319
- scratch.get_instance_variable(recv, :_members, ep, env) do |ty, nenv|
320
- ty = scratch.globalize_type(ty, nenv, ep)
321
- ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
326
+ scratch.add_ivar_read!(recv, :_members, ep) do |member_ary_ty, ep|
327
+ member_ary_ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
322
328
  ty ||= Type.nil
323
329
  scratch.set_instance_variable(recv, sym.sym, ty, ep, env)
324
330
  end
325
331
  end
326
- #scratch.set_instance_variable(recv, , ty, ep, env)
327
332
  ctn[recv, ep, env]
328
333
  end
329
334
 
@@ -337,9 +342,8 @@ module TypeProf
337
342
  return
338
343
  end
339
344
  if struct_klass != recv
340
- scratch.get_instance_variable(Type::Instance.new(struct_klass), :_members, ep, env) do |ty, nenv|
341
- ty = scratch.globalize_type(ty, nenv, ep)
342
- scratch.set_instance_variable(Type::Instance.new(recv), :_members, ty, ep, env)
345
+ scratch.add_ivar_read!(Type::Instance.new(struct_klass), :_members, ep) do |ty, ep|
346
+ scratch.add_ivar_write!(Type::Instance.new(recv), :_members, ty, ep)
343
347
  end
344
348
  end
345
349
  meths = scratch.get_method(recv, false, :initialize)
@@ -364,7 +368,7 @@ module TypeProf
364
368
  fields = fields.map {|field| Type::Symbol.new(field, Type::Instance.new(Type::Builtin[:sym])) }
365
369
  base_ty = Type::Instance.new(Type::Builtin[:ary])
366
370
  fields = Type::Array.new(Type::Array::Elements.new(fields), base_ty)
367
- scratch.set_instance_variable(Type::Instance.new(struct_klass), :_members, fields, ep, env)
371
+ scratch.add_ivar_write!(Type::Instance.new(struct_klass), :_members, fields, ep)
368
372
  #set_singleton_custom_method(struct_klass, :members, Builtin.method(:...))
369
373
 
370
374
  ctn[struct_klass, ep, env]
@@ -372,10 +376,10 @@ module TypeProf
372
376
 
373
377
  def self.file_load(path, ep, env, scratch, &ctn)
374
378
  iseq = ISeq.compile(path)
375
- callee_ep, callee_env = CLI.starting_state(iseq)
379
+ callee_ep, callee_env = TypeProf.starting_state(iseq)
376
380
  scratch.merge_env(callee_ep, callee_env)
377
381
 
378
- scratch.add_callsite!(callee_ep.ctx, nil, ep, env) do |_ret_ty, ep|
382
+ scratch.add_callsite!(callee_ep.ctx, ep, env) do |_ret_ty, ep|
379
383
  ret_ty = Type::Instance.new(Type::Builtin[:true])
380
384
  ctn[ret_ty, ep, env]
381
385
  end
@@ -482,8 +486,8 @@ module TypeProf
482
486
  def self.setup_initial_global_env(scratch)
483
487
  klass_basic_obj = scratch.new_class(nil, :BasicObject, [], :__root__, nil) # cbase, name, superclass
484
488
  klass_obj = scratch.new_class(nil, :Object, [], klass_basic_obj, nil)
485
- scratch.add_constant(klass_obj, "Object", klass_obj)
486
- scratch.add_constant(klass_obj, "BasicObject", klass_basic_obj)
489
+ scratch.add_constant(klass_obj, :Object, klass_obj)
490
+ scratch.add_constant(klass_obj, :BasicObject, klass_basic_obj)
487
491
 
488
492
  Type::Builtin[:basic_obj] = klass_basic_obj
489
493
  Type::Builtin[:obj] = klass_obj
@@ -550,9 +554,6 @@ module TypeProf
550
554
  scratch.set_custom_method(klass_obj, :require, Builtin.method(:kernel_require))
551
555
  scratch.set_custom_method(klass_obj, :require_relative, Builtin.method(:kernel_require_relative))
552
556
  scratch.set_custom_method(klass_obj, :Array, Builtin.method(:kernel_Array))
553
-
554
- fargs, ret_ty = FormalArguments.new([], [], nil, [], nil, nil, Type.any), Type.any
555
- scratch.add_method(klass_obj, :initialize, false, TypedMethodDef.new([[fargs, ret_ty]], nil))
556
557
  end
557
558
  end
558
559
  end
@@ -1,64 +1,62 @@
1
1
  require "optparse"
2
- require "rbconfig"
3
2
 
4
3
  module TypeProf
5
- class CLI
6
- DEFAULT_DIR_FILTER = [
7
- [:include],
8
- [:exclude, RbConfig::CONFIG["prefix"]],
9
- [:exclude, Gem.dir],
10
- [:exclude, Gem.user_dir],
11
- ]
4
+ module CLI
5
+ module_function
12
6
 
13
- def initialize(argv)
7
+ def parse(argv)
14
8
  opt = OptionParser.new
15
9
 
16
- @output = nil
10
+ output = nil
17
11
 
18
12
  # Verbose level:
19
13
  # * 0: no output
20
14
  # * 1: show indicator
21
15
  # * 2: debug print
22
- @verbose = 1
23
-
24
- @options = {
25
- type_depth_limit: 5,
26
- pedantic_output: false,
27
- show_errors: false,
28
- }
29
- @dir_filter = nil
30
- @rbs_features_to_load = []
31
-
32
- opt.on("-o OUTFILE") {|v| @output = v }
33
- opt.on("-q", "--quiet") { @verbose = 0 }
34
- opt.on("-v", "--verbose") { @verbose = 2 }
16
+ verbose = 1
17
+
18
+ options = {}
19
+ dir_filter = nil
20
+ gem_rbs_features = []
21
+ version = false
22
+ max_sec = max_iter = nil
23
+
24
+ opt.on("-o OUTFILE") {|v| output = v }
25
+ opt.on("-q", "--quiet") { verbose = 0 }
26
+ opt.on("-v", "--verbose") { options[:show_errors] = true }
27
+ opt.on("--version") { version = true }
28
+ opt.on("-d", "--debug") { verbose = 2 }
35
29
  opt.on("-I DIR") {|v| $LOAD_PATH << v }
36
- opt.on("-r FEATURE") {|v| @rbs_features_to_load << v }
30
+ opt.on("-r FEATURE") {|v| gem_rbs_features << v }
31
+ opt.on("--max-second SECOND", Float) {|v| max_sec = v }
32
+ opt.on("--max-iteration TIMES", Integer) {|v| max_iter = v }
37
33
 
38
34
  opt.on("--include-dir DIR") do |dir|
39
35
  # When `--include-dir` option is specified as the first directory option,
40
36
  # typeprof will exclude any files by default unless a file path matches the explicit option
41
- @dir_filter ||= [[:exclude]]
42
- @dir_filter << [:include, File.expand_path(dir)]
37
+ dir_filter ||= [[:exclude]]
38
+ dir_filter << [:include, File.expand_path(dir)]
43
39
  end
44
40
  opt.on("--exclude-dir DIR") do |dir|
45
41
  # When `--exclude-dir` option is specified as the first directory option,
46
42
  # typeprof will include any files by default, except Ruby's install directory and Gem directories
47
- @dir_filter ||= DEFAULT_DIR_FILTER
48
- @dir_filter << [:exclude, File.expand_path(dir)]
43
+ dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
44
+ dir_filter << [:exclude, File.expand_path(dir)]
49
45
  end
50
46
 
51
47
  opt.on("-f OPTION") do |v|
52
48
  key, args = v.split("=", 2)
53
49
  case key
54
50
  when "type-depth-limit"
55
- @options[:type_depth_limit] = Integer(args)
51
+ options[:type_depth_limit] = Integer(args)
56
52
  when "pedantic-output"
57
- @options[:pedantic_output] = true
53
+ options[:pedantic_output] = true
58
54
  when "show-errors"
59
- @options[:show_errors] = true
55
+ options[:show_errors] = true
60
56
  when "show-container-raw-elements"
61
- @options[:show_container_raw_elements] = true
57
+ options[:show_container_raw_elements] = true
58
+ when "stackprof"
59
+ options[:stackprof] = args ? args.to_sym : :cpu
62
60
  else
63
61
  raise OptionParser::InvalidOption.new("unknown option: #{ key }")
64
62
  end
@@ -66,83 +64,38 @@ module TypeProf
66
64
 
67
65
  opt.parse!(argv)
68
66
 
69
- @dir_filter ||= DEFAULT_DIR_FILTER
70
- @rb_files = []
71
- @rbs_files = []
67
+ dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
68
+ rb_files = []
69
+ rbs_files = []
72
70
  argv.each do |path|
73
71
  if File.extname(path) == ".rbs"
74
- @rbs_files << path
72
+ rbs_files << path
75
73
  else
76
- @rb_files << path
74
+ rb_files << path
77
75
  end
78
76
  end
79
77
 
80
- raise OptionParser::InvalidOption.new("no input files") if @rb_files.empty?
78
+ puts "typeprof #{ VERSION }" if version
79
+ if rb_files.empty?
80
+ exit if version
81
+ raise OptionParser::InvalidOption.new("no input files")
82
+ end
81
83
 
82
- TypeProf.const_set(:Config, self)
84
+ ConfigData.new(
85
+ rb_files: rb_files,
86
+ rbs_files: rbs_files,
87
+ output: output,
88
+ gem_rbs_features: gem_rbs_features,
89
+ verbose: verbose,
90
+ dir_filter: dir_filter,
91
+ max_sec: max_sec,
92
+ max_iter: max_iter,
93
+ options: options,
94
+ )
83
95
 
84
96
  rescue OptionParser::InvalidOption
85
97
  puts $!
86
98
  exit
87
99
  end
88
-
89
- attr_reader :verbose, :options, :dir_filter
90
- attr_accessor :output
91
-
92
- def check_dir_filter(path)
93
- @dir_filter.reverse_each do |cond, dir|
94
- return cond unless dir
95
- return cond if path.start_with?(dir)
96
- end
97
- end
98
-
99
- def run
100
- scratch = Scratch.new
101
- Builtin.setup_initial_global_env(scratch)
102
-
103
- @rbs_features_to_load.each do |feature|
104
- Import.import_library(scratch, feature)
105
- end
106
-
107
- prologue_ctx = Context.new(nil, nil, nil)
108
- prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
109
- prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
110
-
111
- @rb_files.each do |path|
112
- if path == "-"
113
- iseq = ISeq.compile_str($<.read)
114
- else
115
- iseq = ISeq.compile(path)
116
- end
117
- ep, env = CLI.starting_state(iseq)
118
- scratch.merge_env(ep, env)
119
- scratch.add_callsite!(ep.ctx, nil, prologue_ep, prologue_env) {|ty, ep| }
120
- end
121
-
122
- @rbs_files.each do |path|
123
- Import.import_rbs_file(scratch, path)
124
- end
125
-
126
- result = scratch.type_profile
127
-
128
- if @output
129
- open(@output, "w") do |output|
130
- scratch.report(result, output)
131
- end
132
- else
133
- scratch.report(result, $stdout)
134
- end
135
- end
136
-
137
- def self.starting_state(iseq)
138
- cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
139
- recv = Type::Instance.new(Type::Builtin[:obj])
140
- ctx = Context.new(iseq, cref, nil)
141
- ep = ExecutionPoint.new(ctx, 0, nil)
142
- locals = [Type.nil] * iseq.locals.size
143
- env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
144
-
145
- return ep, env
146
- end
147
100
  end
148
101
  end
@@ -0,0 +1,114 @@
1
+ require "rbconfig"
2
+
3
+ module TypeProf
4
+ ConfigData = Struct.new(
5
+ :rb_files,
6
+ :rbs_files,
7
+ :output,
8
+ :gem_rbs_features,
9
+ :verbose,
10
+ :dir_filter,
11
+ :max_iter,
12
+ :max_sec,
13
+ :options,
14
+ keyword_init: true
15
+ )
16
+
17
+ class ConfigData
18
+ def initialize(**opt)
19
+ opt[:output] ||= $stdout
20
+ opt[:gem_rbs_features] ||= []
21
+ opt[:dir_filter] ||= DEFAULT_DIR_FILTER
22
+ opt[:verbose] ||= 0
23
+ opt[:options] ||= {}
24
+ opt[:options] = {
25
+ type_depth_limit: 5,
26
+ pedantic_output: false,
27
+ show_errors: false,
28
+ stackprof: nil,
29
+ }.merge(opt[:options])
30
+ super(**opt)
31
+ end
32
+
33
+ def check_dir_filter(path)
34
+ dir_filter.reverse_each do |cond, dir|
35
+ return cond unless dir
36
+ return cond if path.start_with?(dir)
37
+ end
38
+ end
39
+
40
+ DEFAULT_DIR_FILTER = [
41
+ [:include],
42
+ [:exclude, RbConfig::CONFIG["prefix"]],
43
+ [:exclude, Gem.dir],
44
+ [:exclude, Gem.user_dir],
45
+ ]
46
+ end
47
+
48
+ def self.analyze(config)
49
+ # Deploy the config to the TypeProf::Config (Note: This is thread unsafe)
50
+ if TypeProf.const_defined?(:Config)
51
+ TypeProf.send(:remove_const, :Config)
52
+ end
53
+ TypeProf.const_set(:Config, config)
54
+
55
+ if Config.options[:stackprof]
56
+ require "stackprof"
57
+ out = "typeprof-stackprof-#{ Config.options[:stackprof] }.dump"
58
+ StackProf.start(mode: Config.options[:stackprof], out: out, raw: true)
59
+ end
60
+
61
+ scratch = Scratch.new
62
+ Builtin.setup_initial_global_env(scratch)
63
+
64
+ Config.gem_rbs_features.each do |feature|
65
+ Import.import_library(scratch, feature)
66
+ end
67
+
68
+ prologue_ctx = Context.new(nil, nil, nil)
69
+ prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
70
+ prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
71
+
72
+ Config.rb_files.each do |file|
73
+ if file.respond_to?(:read)
74
+ iseq = ISeq.compile_str(file.read, file.to_s)
75
+ else
76
+ iseq = ISeq.compile(file)
77
+ end
78
+ ep, env = TypeProf.starting_state(iseq)
79
+ scratch.merge_env(ep, env)
80
+ scratch.add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
81
+ end
82
+
83
+ Config.rbs_files.each do |path|
84
+ Import.import_rbs_file(scratch, path)
85
+ end
86
+
87
+ result = scratch.type_profile
88
+
89
+ if Config.output.respond_to?(:write)
90
+ scratch.report(result, Config.output)
91
+ else
92
+ open(Config.output, "w") do |output|
93
+ scratch.report(result, output)
94
+ end
95
+ end
96
+
97
+ ensure
98
+ if Config.options[:stackprof] && defined?(StackProf)
99
+ StackProf.stop
100
+ StackProf.results
101
+ end
102
+ end
103
+
104
+ def self.starting_state(iseq)
105
+ cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
106
+ recv = Type::Instance.new(Type::Builtin[:obj])
107
+ ctx = Context.new(iseq, cref, nil)
108
+ ep = ExecutionPoint.new(ctx, 0, nil)
109
+ locals = [Type.nil] * iseq.locals.size
110
+ env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
111
+
112
+ return ep, env
113
+ end
114
+ end
@@ -6,22 +6,123 @@ module TypeProf
6
6
  raise if !val.is_a?(Utils::StructuralEquality) && !val.is_a?(Integer) && !val.is_a?(Symbol)
7
7
  @val = val
8
8
  @parent = parent
9
- @_hash ||= (@val.hash ^ @parent.hash)
10
9
  end
11
10
 
12
11
  attr_reader :val, :parent
13
12
 
14
- def hash
15
- @_hash
16
- end
17
-
18
13
  def add_id(val)
19
14
  AllocationSite.new(val, self)
20
15
  end
21
16
  end
22
17
 
23
18
  class Type # or AbstractValue
24
- # This is a type for global interface, e.g., TypedISeq.
19
+ # Cell, Array, and Hash are types for global interface, e.g., TypedISeq.
20
+ # Do not push such types to local environment, stack, etc.
21
+
22
+ # The most basic container type for default type parameter class
23
+ class Cell < Type
24
+ def initialize(elems, base_type)
25
+ @elems = elems # Array[Symbol]
26
+ raise unless base_type
27
+ @base_type = base_type
28
+ end
29
+
30
+ attr_reader :elems, :base_type
31
+
32
+ def inspect
33
+ "Type::Cell[#{ @elems.inspect }, base_type: #{ @base_type.inspect }]"
34
+ end
35
+
36
+ def screen_name(scratch)
37
+ "#{ @base_type.screen_name(scratch) }[#{ @elems.map {|elem| elem.screen_name(scratch) }.join(", ") }]"
38
+ end
39
+
40
+ def localize(env, alloc_site, depth)
41
+ return env, Type.any if depth <= 0
42
+ alloc_site = alloc_site.add_id(:cell)
43
+ elems = @elems.map.with_index do |elem, idx|
44
+ env, elem = elem.localize(env, alloc_site.add_id(idx), depth - 1)
45
+ elem
46
+ end
47
+ env.deploy_cell_type(alloc_site, elems, @base_type)
48
+ end
49
+
50
+ def limit_size(limit)
51
+ return Type.any if limit <= 0
52
+ Cell.new(@elems.map {|elem| elem.limit_size(limit - 1) }, @base_type)
53
+ end
54
+
55
+ def get_method(mid, scratch)
56
+ raise
57
+ end
58
+
59
+ def consistent?(other, subst)
60
+ case other
61
+ when Type::Any then true
62
+ when Type::Var then other.add_subst!(self, subst)
63
+ when Type::Union
64
+ other.types.each do |ty2|
65
+ return true if consistent?(ty2, subst)
66
+ end
67
+ return false
68
+ when Type::Cell
69
+ @elems.size == other.elems.size &&
70
+ @base_type.consistent?(other.base_type, subst) &&
71
+ @elems.zip(other.elems).all? {|elem1, elem2| elem1..consistent?(elem2, subst) }
72
+ else
73
+ self == other
74
+ end
75
+ end
76
+
77
+ def substitute(subst, depth)
78
+ return Type.any if depth <= 0
79
+ elems = @elems.map {|elem| elem.substitute(subst, depth - 1) }
80
+ Cell.new(elems, @base_type)
81
+ end
82
+ end
83
+
84
+ class LocalCell < Type
85
+ def initialize(id, base_type)
86
+ @id = id
87
+ raise unless base_type
88
+ @base_type = base_type
89
+ end
90
+
91
+ attr_reader :id, :base_type
92
+
93
+ def inspect
94
+ "Type::LocalCell[#{ @id }, base_type: #{ @base_type.inspect }]"
95
+ end
96
+
97
+ def screen_name(scratch)
98
+ #raise "LocalArray must not be included in signature"
99
+ "LocalCell!"
100
+ end
101
+
102
+ def globalize(env, visited, depth)
103
+ if visited[self] || depth <= 0
104
+ Type.any
105
+ else
106
+ visited[self] = true
107
+ elems = env.get_container_elem_types(@id)
108
+ if elems
109
+ elems = elems.map {|elem| elem.globalize(env, visited, depth - 1) }
110
+ else
111
+ elems = [] # XXX
112
+ end
113
+ Cell.new(elems, @base_type)
114
+ end
115
+ end
116
+
117
+ def get_method(mid, scratch)
118
+ @base_type.get_method(mid, scratch)
119
+ end
120
+
121
+ def consistent?(other, subst)
122
+ raise "must not be used"
123
+ end
124
+ end
125
+
25
126
  # Do not insert Array type to local environment, stack, etc.
26
127
  class Array < Type
27
128
  def initialize(elems, base_type)
@@ -29,7 +130,6 @@ module TypeProf
29
130
  @elems = elems # Array::Elements
30
131
  raise unless base_type
31
132
  @base_type = base_type
32
- # XXX: need infinite recursion
33
133
  end
34
134
 
35
135
  attr_reader :elems, :base_type
@@ -47,13 +147,6 @@ module TypeProf
47
147
  str
48
148
  end
49
149
 
50
- def globalize(env, visited, depth)
51
- return Type.any if depth <= 0
52
- elems = @elems.globalize(env, visited, depth - 1)
53
- base_ty = @base_type.globalize(env, visited, depth - 1)
54
- Array.new(elems, base_ty)
55
- end
56
-
57
150
  def localize(env, alloc_site, depth)
58
151
  return env, Type.any if depth <= 0
59
152
  alloc_site = alloc_site.add_id(:ary)
@@ -78,6 +171,7 @@ module TypeProf
78
171
  other.types.each do |ty2|
79
172
  return true if consistent?(ty2, subst)
80
173
  end
174
+ return false
81
175
  when Type::Array
82
176
  @base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
83
177
  else
@@ -365,13 +459,6 @@ module TypeProf
365
459
  @elems.screen_name(scratch)
366
460
  end
367
461
 
368
- def globalize(env, visited, depth)
369
- return Type.any if depth <= 0
370
- elems = @elems.globalize(env, visited, depth - 1)
371
- base_ty = @base_type.globalize(env, visited, depth - 1)
372
- Hash.new(elems, base_ty)
373
- end
374
-
375
462
  def localize(env, alloc_site, depth)
376
463
  return env, Type.any if depth <= 0
377
464
  alloc_site = alloc_site.add_id(:hash)
@@ -396,6 +483,7 @@ module TypeProf
396
483
  other.types.each do |ty2|
397
484
  return true if consistent?(ty2, subst)
398
485
  end
486
+ return false
399
487
  when Type::Hash
400
488
  @base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
401
489
  else
@@ -579,6 +667,22 @@ module TypeProf
579
667
 
580
668
  Elements.new(map_tys)
581
669
  end
670
+
671
+ def to_keywords
672
+ kw_tys = {}
673
+ @map_tys.each do |key_ty, val_ty|
674
+ if key_ty.is_a?(Type::Symbol)
675
+ kw_tys[key_ty.sym] = val_ty
676
+ else
677
+ all_val_ty = Type.bot
678
+ @map_tys.each do |_key_ty, val_ty|
679
+ all_val_ty = all_val_ty.union(val_ty)
680
+ end
681
+ return { nil => all_val_ty }
682
+ end
683
+ end
684
+ kw_tys
685
+ end
582
686
  end
583
687
  end
584
688