typeprof 0.21.11 → 0.30.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -31
  3. data/bin/typeprof +5 -0
  4. data/doc/doc.ja.md +134 -0
  5. data/doc/doc.md +136 -0
  6. data/lib/typeprof/cli/cli.rb +178 -0
  7. data/lib/typeprof/cli.rb +3 -133
  8. data/lib/typeprof/code_range.rb +112 -0
  9. data/lib/typeprof/core/ast/base.rb +263 -0
  10. data/lib/typeprof/core/ast/call.rb +259 -0
  11. data/lib/typeprof/core/ast/const.rb +126 -0
  12. data/lib/typeprof/core/ast/control.rb +433 -0
  13. data/lib/typeprof/core/ast/meta.rb +150 -0
  14. data/lib/typeprof/core/ast/method.rb +339 -0
  15. data/lib/typeprof/core/ast/misc.rb +263 -0
  16. data/lib/typeprof/core/ast/module.rb +123 -0
  17. data/lib/typeprof/core/ast/pattern.rb +140 -0
  18. data/lib/typeprof/core/ast/sig_decl.rb +471 -0
  19. data/lib/typeprof/core/ast/sig_type.rb +663 -0
  20. data/lib/typeprof/core/ast/value.rb +319 -0
  21. data/lib/typeprof/core/ast/variable.rb +315 -0
  22. data/lib/typeprof/core/ast.rb +472 -0
  23. data/lib/typeprof/core/builtin.rb +146 -0
  24. data/lib/typeprof/core/env/method.rb +137 -0
  25. data/lib/typeprof/core/env/method_entity.rb +55 -0
  26. data/lib/typeprof/core/env/module_entity.rb +408 -0
  27. data/lib/typeprof/core/env/static_read.rb +155 -0
  28. data/lib/typeprof/core/env/type_alias_entity.rb +27 -0
  29. data/lib/typeprof/core/env/value_entity.rb +32 -0
  30. data/lib/typeprof/core/env.rb +366 -0
  31. data/lib/typeprof/core/graph/box.rb +998 -0
  32. data/lib/typeprof/core/graph/change_set.rb +224 -0
  33. data/lib/typeprof/core/graph/filter.rb +155 -0
  34. data/lib/typeprof/core/graph/vertex.rb +225 -0
  35. data/lib/typeprof/core/service.rb +514 -0
  36. data/lib/typeprof/core/type.rb +352 -0
  37. data/lib/typeprof/core/util.rb +81 -0
  38. data/lib/typeprof/core.rb +31 -0
  39. data/lib/typeprof/diagnostic.rb +35 -0
  40. data/lib/typeprof/lsp/messages.rb +415 -0
  41. data/lib/typeprof/lsp/server.rb +203 -0
  42. data/lib/typeprof/lsp/text.rb +69 -0
  43. data/lib/typeprof/lsp/util.rb +51 -0
  44. data/lib/typeprof/lsp.rb +4 -907
  45. data/lib/typeprof/version.rb +1 -1
  46. data/lib/typeprof.rb +4 -18
  47. data/typeprof.gemspec +5 -7
  48. metadata +47 -33
  49. data/.github/dependabot.yml +0 -6
  50. data/.github/workflows/main.yml +0 -39
  51. data/.gitignore +0 -9
  52. data/Gemfile +0 -17
  53. data/Gemfile.lock +0 -41
  54. data/Rakefile +0 -10
  55. data/exe/typeprof +0 -10
  56. data/lib/typeprof/analyzer.rb +0 -2598
  57. data/lib/typeprof/arguments.rb +0 -414
  58. data/lib/typeprof/block.rb +0 -176
  59. data/lib/typeprof/builtin.rb +0 -893
  60. data/lib/typeprof/code-range.rb +0 -177
  61. data/lib/typeprof/config.rb +0 -158
  62. data/lib/typeprof/container-type.rb +0 -912
  63. data/lib/typeprof/export.rb +0 -589
  64. data/lib/typeprof/import.rb +0 -852
  65. data/lib/typeprof/insns-def.rb +0 -65
  66. data/lib/typeprof/iseq.rb +0 -864
  67. data/lib/typeprof/method.rb +0 -355
  68. data/lib/typeprof/type.rb +0 -1140
  69. data/lib/typeprof/utils.rb +0 -212
  70. data/tools/coverage.rb +0 -14
  71. data/tools/setup-insns-def.rb +0 -30
  72. data/typeprof-lsp +0 -3
@@ -0,0 +1,224 @@
1
+ module TypeProf::Core
2
+ class ChangeSet
3
+ def initialize(node, target)
4
+ @node = node
5
+ @target = target
6
+ @covariant_types = {}
7
+ @contravariant_types = {}
8
+ @edges = []
9
+ @new_edges = []
10
+ @boxes = {}
11
+ @new_boxes = {}
12
+ @diagnostics = []
13
+ @new_diagnostics = []
14
+ @depended_value_entities = []
15
+ @new_depended_value_entities = []
16
+ @depended_method_entities = []
17
+ @new_depended_method_entities = []
18
+ @depended_static_reads = []
19
+ @new_depended_static_reads = []
20
+ @depended_superclasses = []
21
+ @new_depended_superclasses = []
22
+ end
23
+
24
+ attr_reader :node, :covariant_types, :edges, :boxes, :diagnostics
25
+
26
+ def reuse(new_node)
27
+ @node = new_node
28
+ @boxes.each_value do |box|
29
+ box.reuse(new_node)
30
+ end
31
+ @diagnostics.each do |diag|
32
+ diag.reuse(new_node)
33
+ end
34
+ end
35
+
36
+ def copy_from(other)
37
+ @covariant_types = other.covariant_types.dup
38
+ @edges = other.edges.dup
39
+ @boxes = other.boxes.dup
40
+ @diagnostics = other.diagnostics.dup
41
+
42
+ other.covariant_types.clear
43
+ other.edges.clear
44
+ other.boxes.clear
45
+ other.diagnostics.clear
46
+ end
47
+
48
+ def new_covariant_vertex(genv, sig_type_node)
49
+ # This is used to avoid duplicated vertex generation for the same sig node
50
+ @covariant_types[sig_type_node] ||= Vertex.new(sig_type_node)
51
+ end
52
+
53
+ def new_contravariant_vertex(genv, sig_type_node)
54
+ # This is used to avoid duplicated vertex generation for the same sig node
55
+ @contravariant_types[sig_type_node] ||= Vertex.new(sig_type_node)
56
+ end
57
+
58
+ def add_edge(genv, src, dst)
59
+ raise src.class.to_s unless src.is_a?(BasicVertex)
60
+ src.add_edge(genv, dst) if !@edges.include?([src, dst]) && !@new_edges.include?([src, dst])
61
+ @new_edges << [src, dst]
62
+ end
63
+
64
+ # TODO: if an edge is removed during one analysis, we may need to remove sub-boxes?
65
+
66
+ def add_method_call_box(genv, recv, mid, a_args, subclasses)
67
+ key = [:mcall, recv, mid, a_args, subclasses]
68
+ return if @new_boxes[key]
69
+ @new_boxes[key] = MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses)
70
+ end
71
+
72
+ def add_escape_box(genv, a_ret, f_ret)
73
+ key = [:return, a_ret]
74
+ return if @new_boxes[key]
75
+ @new_boxes[key] = EscapeBox.new(@node, genv, a_ret, f_ret)
76
+ end
77
+
78
+ def add_splat_box(genv, arg)
79
+ key = [:splat, arg]
80
+ return if @new_boxes[key]
81
+ @new_boxes[key] = SplatBox.new(@node, genv, arg)
82
+ end
83
+
84
+ def add_hash_splat_box(genv, arg, unified_key, unified_val)
85
+ key = [:hash_splat, arg, unified_key, unified_val]
86
+ return if @new_boxes[key]
87
+ @new_boxes[key] = HashSplatBox.new(@node, genv, arg, unified_key, unified_val)
88
+ end
89
+
90
+ def add_masgn_box(genv, value, lefts, rest_elem, rights)
91
+ key = [:masgn, value, lefts, rest_elem, rights]
92
+ return if @new_boxes[key]
93
+ @new_boxes[key] = MAsgnBox.new(@node, genv, value, lefts, rest_elem, rights)
94
+ end
95
+
96
+ def add_method_def_box(genv, cpath, singleton, mid, f_args, ret_boxes)
97
+ key = [:mdef, cpath, singleton, mid, f_args, ret_boxes]
98
+ return if @new_boxes[key]
99
+ @new_boxes[key] = MethodDefBox.new(@node, genv, cpath, singleton, mid, f_args, ret_boxes)
100
+ end
101
+
102
+ def add_method_decl_box(genv, cpath, singleton, mid, method_types, overloading)
103
+ key = [:mdecl, cpath, singleton, mid, method_types, overloading]
104
+ return if @new_boxes[key]
105
+ @new_boxes[key] = MethodDeclBox.new(@node, genv, cpath, singleton, mid, method_types, overloading)
106
+ end
107
+
108
+ def add_method_alias_box(genv, cpath, singleton, new_mid, old_mid)
109
+ key = [:mdecl, cpath, singleton, new_mid, old_mid]
110
+ return if @new_boxes[key]
111
+ @new_boxes[key] = MethodAliasBox.new(@node, genv, cpath, singleton, new_mid, old_mid)
112
+ end
113
+
114
+ def add_const_read_box(genv, static_ret)
115
+ key = [:cread, static_ret]
116
+ return if @new_boxes[key]
117
+ @new_boxes[key] = ConstReadBox.new(@node, genv, static_ret)
118
+ end
119
+
120
+ def add_gvar_read_box(genv, var)
121
+ key = [:gvar_read, var]
122
+ return if @new_boxes[key]
123
+ @new_boxes[key] = GVarReadBox.new(@node, genv, var)
124
+ end
125
+
126
+ def add_ivar_read_box(genv, cpath, singleton, name)
127
+ key = [:ivar_read, cpath, singleton, name]
128
+ return if @new_boxes[key]
129
+ @new_boxes[key] = IVarReadBox.new(@node, genv, cpath, singleton, name)
130
+ end
131
+
132
+ def add_cvar_read_box(genv, cpath, name)
133
+ key = [:cvar_read, cpath, name]
134
+ return if @new_boxes[key]
135
+ @new_boxes[key] = CVarReadBox.new(@node, genv, cpath, name)
136
+ end
137
+
138
+ def add_type_read_box(genv, type)
139
+ key = [:type_read, type]
140
+ return if @new_boxes[key]
141
+ @new_boxes[key] = TypeReadBox.new(@node, genv, type)
142
+ end
143
+
144
+ def add_diagnostic(meth, msg)
145
+ @new_diagnostics << TypeProf::Diagnostic.new(@node, meth, msg)
146
+ end
147
+
148
+ def add_depended_value_entity(ve)
149
+ @new_depended_value_entities << ve
150
+ end
151
+
152
+ def add_depended_method_entity(me)
153
+ @new_depended_method_entities << me
154
+ end
155
+
156
+ def add_depended_static_read(static_read)
157
+ @new_depended_static_reads << static_read
158
+ end
159
+
160
+ def add_depended_superclass(mod)
161
+ @new_depended_superclasses << mod
162
+ end
163
+
164
+ def reinstall(genv)
165
+ @new_edges.uniq!
166
+ @edges.each do |src, dst|
167
+ src.remove_edge(genv, dst) unless @new_edges.include?([src, dst])
168
+ end
169
+ @edges, @new_edges = @new_edges, @edges
170
+ @new_edges.clear
171
+
172
+ @boxes.each do |key, box|
173
+ box.destroy(genv)
174
+ end
175
+ @boxes, @new_boxes = @new_boxes, @boxes
176
+ @new_boxes.clear
177
+
178
+ @diagnostics, @new_diagnostics = @new_diagnostics, @diagnostics
179
+ @new_diagnostics.clear
180
+
181
+ @depended_value_entities.each do |ve|
182
+ ve.read_boxes.delete(@target) || raise
183
+ end
184
+ @new_depended_value_entities.uniq!
185
+ @new_depended_value_entities.each do |ve|
186
+ ve.read_boxes << @target
187
+ end
188
+ @depended_value_entities, @new_depended_value_entities = @new_depended_value_entities, @depended_value_entities
189
+ @new_depended_value_entities.clear
190
+
191
+ @depended_method_entities.each do |me|
192
+ me.method_call_boxes.delete(@target) || raise
193
+ end
194
+ @new_depended_method_entities.uniq!
195
+ @new_depended_method_entities.each do |me|
196
+ me.method_call_boxes << @target
197
+ end
198
+ @depended_method_entities, @new_depended_method_entities = @new_depended_method_entities, @depended_method_entities
199
+ @new_depended_method_entities.clear
200
+
201
+ @depended_static_reads.each do |static_read|
202
+ static_read.followers.delete(@target)
203
+ end
204
+ @new_depended_static_reads.uniq!
205
+ @new_depended_static_reads.each do |static_read|
206
+ static_read.followers << @target
207
+ end
208
+
209
+ @depended_static_reads, @new_depended_static_reads = @new_depended_static_reads, @depended_static_reads
210
+ @new_depended_static_reads.clear
211
+
212
+ @depended_superclasses.each do |mod|
213
+ mod.subclass_checks.delete(@target)
214
+ end
215
+ @new_depended_superclasses.uniq!
216
+ @new_depended_superclasses.each do |mod|
217
+ mod.subclass_checks << @target
218
+ end
219
+
220
+ @depended_superclasses, @new_depended_superclasses = @new_depended_superclasses, @depended_superclasses
221
+ @new_depended_superclasses.clear
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,155 @@
1
+ module TypeProf::Core
2
+ class Filter
3
+ def destroyed
4
+ false
5
+ end
6
+ end
7
+
8
+ class NilFilter < Filter
9
+ def initialize(genv, node, prev_vtx, allow_nil)
10
+ @node = node
11
+ @next_vtx = Vertex.new(node)
12
+ @allow_nil = allow_nil
13
+ prev_vtx.add_edge(genv, self)
14
+ end
15
+
16
+ attr_reader :next_vtx, :allow_nil
17
+
18
+ def filter(types, nil_type)
19
+ types.select {|ty| (ty == nil_type) == @allow_nil }
20
+ end
21
+
22
+ def on_type_added(genv, src_var, added_types)
23
+ types = filter(added_types, genv.nil_type)
24
+ @next_vtx.on_type_added(genv, self, types) unless types.empty?
25
+ end
26
+
27
+ def on_type_removed(genv, src_var, removed_types)
28
+ types = filter(removed_types, genv.nil_type)
29
+ @next_vtx.on_type_removed(genv, self, types) unless types.empty?
30
+ end
31
+
32
+ #@@new_id = 0
33
+
34
+ def to_s
35
+ "NF#{ @id ||= $new_id += 1 } -> #{ @next_vtx }"
36
+ end
37
+ end
38
+
39
+ class IsAFilter < Filter
40
+ def initialize(genv, node, prev_vtx, neg, const_read)
41
+ @node = node
42
+ @types = Set[]
43
+ @const_read = const_read
44
+ @const_read.followers << self
45
+ @next_vtx = Vertex.new(node)
46
+ prev_vtx.add_edge(genv, self)
47
+ @neg = neg
48
+ end
49
+
50
+ attr_reader :next_vtx
51
+
52
+ def on_type_added(genv, src_var, added_types)
53
+ added_types.each do |ty|
54
+ @types << ty
55
+ end
56
+ run(genv)
57
+ end
58
+
59
+ def on_type_removed(genv, src_var, removed_types)
60
+ removed_types.each do |ty|
61
+ @types.delete(ty) || raise
62
+ end
63
+ run(genv)
64
+ end
65
+
66
+ def run(genv)
67
+ if @const_read && @const_read.cpath
68
+ passed_types = []
69
+ @types.each do |ty|
70
+ base_ty = ty.base_type(genv)
71
+ subclass = false
72
+ genv.each_superclass(base_ty.mod, base_ty.is_a?(Type::Singleton)) do |mod, singleton|
73
+ if mod.cpath == @const_read.cpath
74
+ subclass = true
75
+ break
76
+ end
77
+ end
78
+ passed_types << ty if subclass != @neg
79
+ end
80
+ else
81
+ passed_types = @types.to_a
82
+ end
83
+ added_types = passed_types - @next_vtx.types.keys
84
+ removed_types = @next_vtx.types.keys - passed_types
85
+ @next_vtx.on_type_added(genv, self, added_types)
86
+ @next_vtx.on_type_removed(genv, self, removed_types)
87
+ end
88
+
89
+ #@@new_id = 0
90
+
91
+ def to_s
92
+ "NF#{ @id ||= $new_id += 1 } -> #{ @next_vtx }"
93
+ end
94
+ end
95
+
96
+ class BotFilter < Filter
97
+ def initialize(genv, node, prev_vtx, base_vtx)
98
+ @node = node
99
+ @types = {}
100
+ @prev_vtx = prev_vtx
101
+ @next_vtx = Vertex.new(node)
102
+ @base_vtx = base_vtx
103
+ base_vtx.add_edge(genv, self)
104
+ prev_vtx.add_edge(genv, self) if prev_vtx != base_vtx
105
+ end
106
+
107
+ attr_reader :types, :prev_vtx, :next_vtx, :base_vtx
108
+
109
+ def filter(types)
110
+ types.select {|ty| (ty == genv.nil_type) == @allow_nil }
111
+ end
112
+
113
+ def on_type_added(genv, src_var, added_types)
114
+ if src_var == @base_vtx
115
+ if @base_vtx.types.size == 1 && @base_vtx.types.include?(Type::Bot.new(genv))
116
+ @next_vtx.on_type_removed(genv, self, @types.keys & @next_vtx.types.keys) # XXX: smoke/control/bot2.rb
117
+ end
118
+ else
119
+ added_types.each do |ty|
120
+ @types[ty] = true
121
+ end
122
+ if @base_vtx.types.size == 1 && @base_vtx.types.include?(Type::Bot.new(genv))
123
+ # ignore
124
+ else
125
+ @next_vtx.on_type_added(genv, self, added_types - @next_vtx.types.keys) # XXX: smoke/control/bot4.rb
126
+ end
127
+ end
128
+ end
129
+
130
+ def on_type_removed(genv, src_var, removed_types)
131
+ if src_var == @base_vtx
132
+ if @base_vtx.types.size == 1 && @base_vtx.types.include?(Type::Bot.new(genv))
133
+ # ignore
134
+ else
135
+ @next_vtx.on_type_added(genv, self, @types.keys - @next_vtx.types.keys) # XXX: smoke/control/bot4.rb
136
+ end
137
+ else
138
+ removed_types.each do |ty|
139
+ @types.delete(ty) || raise
140
+ end
141
+ if @base_vtx.types.size == 1 && @base_vtx.types.include?(Type::Bot.new(genv))
142
+ # ignore
143
+ else
144
+ @next_vtx.on_type_removed(genv, self, removed_types & @next_vtx.types.keys) # XXX: smoke/control/bot2.rb
145
+ end
146
+ end
147
+ end
148
+
149
+ #@@new_id = 0
150
+
151
+ def to_s
152
+ "BF#{ @id ||= $new_id += 1 } -> #{ @next_vtx }"
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,225 @@
1
+ module TypeProf::Core
2
+ class BasicVertex
3
+ def initialize(types)
4
+ @types = types
5
+ @types_to_be_added = {}
6
+ end
7
+
8
+ attr_reader :types
9
+
10
+ def each_type(&blk)
11
+ @types.each_key(&blk)
12
+
13
+ until @types_to_be_added.empty?
14
+ h = @types_to_be_added.dup
15
+ h.each do |ty, source|
16
+ @types[ty] = source
17
+ end
18
+ @types_to_be_added.clear
19
+ h.each_key(&blk)
20
+ end
21
+ end
22
+
23
+ def check_match(genv, changes, vtx)
24
+ vtx.each_type do |ty|
25
+ if ty.is_a?(Type::Var)
26
+ changes.add_edge(genv, self, ty.vtx) if self != ty.vtx
27
+ return true
28
+ end
29
+ end
30
+
31
+ return true if @types.empty?
32
+ return true if vtx.types.empty?
33
+
34
+ each_type do |ty|
35
+ return true if vtx.types.include?(ty) # fast path
36
+ if ty.check_match(genv, changes, vtx)
37
+ return true
38
+ end
39
+ end
40
+
41
+ return false
42
+ end
43
+
44
+ def show
45
+ Fiber[:show_rec] ||= Set[]
46
+ if Fiber[:show_rec].include?(self)
47
+ "untyped"
48
+ else
49
+ begin
50
+ Fiber[:show_rec] << self
51
+ types = []
52
+ bot = @types.keys.any? {|ty| ty.is_a?(Type::Bot) }
53
+ optional = true_exist = false_exist = false
54
+ each_type do |ty|
55
+ if ty.is_a?(Type::Instance)
56
+ case ty.mod.cpath
57
+ when [:NilClass] then optional = true
58
+ when [:TrueClass] then true_exist = true
59
+ when [:FalseClass] then false_exist = true
60
+ end
61
+ end
62
+ end
63
+ bool = true_exist && false_exist
64
+ types << "bool" if bool
65
+ each_type do |ty, _source|
66
+ if ty.is_a?(Type::Instance)
67
+ next if ty.mod.cpath == [:NilClass]
68
+ next if bool && (ty.mod.cpath == [:TrueClass] || ty.mod.cpath == [:FalseClass])
69
+ end
70
+ next if ty.is_a?(Type::Bot)
71
+ types << ty.show
72
+ end
73
+ types = types.uniq.sort
74
+ case types.size
75
+ when 0
76
+ optional ? "nil" : bot ? "bot" : "untyped"
77
+ when 1
78
+ types.first + (optional ? "?" : "")
79
+ else
80
+ "(#{ types.join(" | ") })" + (optional ? "?" : "")
81
+ end
82
+ ensure
83
+ Fiber[:show_rec].delete(self) || raise
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ class Source < BasicVertex
90
+ def initialize(*tys)
91
+ types = {}
92
+ tys.each do |ty|
93
+ raise ty.inspect unless ty.is_a?(Type)
94
+ types[ty] = true
95
+ end
96
+ super(types)
97
+ end
98
+
99
+ def on_type_added(genv, src_var, added_types)
100
+ # TODO: need to report error?
101
+ end
102
+
103
+ def on_type_removed(genv, src_var, removed_types)
104
+ end
105
+
106
+ def new_vertex(genv, origin)
107
+ nvtx = Vertex.new(origin)
108
+ add_edge(genv, nvtx)
109
+ nvtx
110
+ end
111
+
112
+ def add_edge(genv, nvtx)
113
+ nvtx.on_type_added(genv, self, @types.keys)
114
+ end
115
+
116
+ def remove_edge(genv, nvtx)
117
+ nvtx.on_type_removed(genv, self, @types.keys)
118
+ end
119
+
120
+ def show
121
+ Fiber[:show_rec] ||= Set[]
122
+ if Fiber[:show_rec].include?(self)
123
+ "...(recursive)..."
124
+ else
125
+ begin
126
+ Fiber[:show_rec] << self
127
+ @types.empty? ? "untyped" : @types.keys.map {|ty| ty.show }.sort.join(" | ")
128
+ ensure
129
+ Fiber[:show_rec].delete(self) || raise
130
+ end
131
+ end
132
+ end
133
+
134
+ def to_s
135
+ "<src:#{ show }>"
136
+ end
137
+
138
+ alias inspect to_s
139
+ end
140
+
141
+ class Vertex < BasicVertex
142
+ def initialize(origin)
143
+ # Note that origin is just for debug.
144
+ # When an AST node is reused, the value of the origin will be invalid.
145
+ case origin
146
+ when AST::Node
147
+ when RBS::AST::Declarations::Base
148
+ when ValueEntity
149
+ when ActualArguments
150
+ when Array
151
+ when Symbol
152
+ else
153
+ raise "unknown class: #{ origin.class }"
154
+ end
155
+ @next_vtxs = Set[]
156
+ super({})
157
+ end
158
+
159
+ attr_reader :next_vtxs, :types
160
+
161
+ def on_type_added(genv, src_var, added_types)
162
+ new_added_types = []
163
+ added_types.each do |ty|
164
+ if @types[ty]
165
+ @types[ty] << src_var
166
+ else
167
+ set = Set[]
168
+ begin
169
+ @types[ty] = set
170
+ rescue
171
+ @types_to_be_added[ty] = set
172
+ end
173
+ set << src_var
174
+ new_added_types << ty
175
+ end
176
+ end
177
+ unless new_added_types.empty?
178
+ @next_vtxs.each do |nvtx|
179
+ nvtx.on_type_added(genv, self, new_added_types)
180
+ end
181
+ end
182
+ end
183
+
184
+ def on_type_removed(genv, src_var, removed_types)
185
+ new_removed_types = []
186
+ removed_types.each do |ty|
187
+ raise "!!! not implemented" if @types_to_be_added[ty]
188
+ @types[ty].delete(src_var) || raise
189
+ if @types[ty].empty?
190
+ @types.delete(ty) || raise
191
+ new_removed_types << ty
192
+ end
193
+ end
194
+ unless new_removed_types.empty?
195
+ @next_vtxs.each do |nvtx|
196
+ nvtx.on_type_removed(genv, self, new_removed_types)
197
+ end
198
+ end
199
+ end
200
+
201
+ def new_vertex(genv, origin)
202
+ nvtx = Vertex.new(origin)
203
+ add_edge(genv, nvtx)
204
+ nvtx
205
+ end
206
+
207
+ def add_edge(genv, nvtx)
208
+ @next_vtxs << nvtx
209
+ nvtx.on_type_added(genv, self, @types.keys) unless @types.empty?
210
+ end
211
+
212
+ def remove_edge(genv, nvtx)
213
+ @next_vtxs.delete(nvtx) || raise
214
+ nvtx.on_type_removed(genv, self, @types.keys) unless @types.empty?
215
+ end
216
+
217
+ $new_id = 0 # TODO: Use class variable
218
+
219
+ def to_s
220
+ "v#{ @id ||= $new_id += 1 }"
221
+ end
222
+
223
+ alias inspect to_s
224
+ end
225
+ end