typeprof 0.2.0 → 0.3.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.
@@ -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