From d6796acf27003667af6f2463447df8193a7514c8 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 9 Aug 2024 19:19:30 +0900 Subject: [PATCH] Prevent the analysis from getting stuck Creating a new vertex in argument matching may cause infinite loop. See scenario/misc/ivar-stuck-case.rb. --- lib/typeprof/core/ast/sig_type.rb | 40 +++++++++++++-------------- lib/typeprof/core/graph/box.rb | 9 ++++-- lib/typeprof/core/graph/change_set.rb | 8 +++++- lib/typeprof/core/graph/vertex.rb | 1 + scenario/misc/ivar-stuck-case.rb | 12 ++++++++ 5 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 scenario/misc/ivar-stuck-case.rb diff --git a/lib/typeprof/core/ast/sig_type.rb b/lib/typeprof/core/ast/sig_type.rb index 72bcde86..6033ebce 100644 --- a/lib/typeprof/core/ast/sig_type.rb +++ b/lib/typeprof/core/ast/sig_type.rb @@ -80,13 +80,13 @@ def attrs = { type_params:, block_required: } class SigTyNode < Node def covariant_vertex(genv, changes, subst) - vtx = changes.new_vertex(genv, self) + vtx = changes.new_covariant_vertex(genv, self) covariant_vertex0(genv, changes, vtx, subst) vtx end def contravariant_vertex(genv, changes, subst) - vtx = Vertex.new(self) + vtx = changes.new_contravariant_vertex(genv, self) contravariant_vertex0(genv, changes, vtx, subst) vtx end @@ -98,7 +98,7 @@ def covariant_vertex0(genv, changes, vtx, subst) end def contravariant_vertex0(genv, changes, vtx, subst) - Source.new(genv.true_type, genv.false_type).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(genv.true_type, genv.false_type), vtx) end def show @@ -112,7 +112,7 @@ def covariant_vertex0(genv, changes, vtx, subst) end def contravariant_vertex0(genv, changes, vtx, subst) - Source.new(genv.nil_type).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(genv.nil_type), vtx) end def show @@ -126,7 +126,7 @@ def covariant_vertex0(genv, changes, vtx, subst) end def contravariant_vertex0(genv, changes, vtx, subst) - subst[:"*self"].add_edge(genv, vtx) + changes.add_edge(genv, subst[:"*self"], vtx) end def show @@ -140,7 +140,7 @@ def covariant_vertex0(genv, changes, vtx, subst) end def contravariant_vertex0(genv, changes, vtx, subst) - Source.new(genv.obj_type).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(genv.obj_type), vtx) end def show @@ -181,7 +181,7 @@ def covariant_vertex0(genv, changes, vtx, subst) end def contravariant_vertex0(genv, changes, vtx, subst) - Source.new(Type::Bot.new(genv)).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(Type::Bot.new(genv)), vtx) end def show @@ -195,7 +195,7 @@ def covariant_vertex0(genv, changes, vtx, subst) end def contravariant_vertex0(genv, changes, vtx, subst) - subst[:"*instance"].add_edge(genv, vtx) + changes.add_edge(genv, subst[:"*instance"], vtx) end def show @@ -209,7 +209,7 @@ def covariant_vertex0(genv, changes, vtx, subst) end def contravariant_vertex0(genv, changes, vtx, subst) - subst[:"*class"].add_edge(genv, vtx) + changes.add_edge(genv, subst[:"*class"], vtx) end def show @@ -375,7 +375,7 @@ def contravariant_vertex0(genv, changes, vtx, subst) cpath = @static_ret.last.cpath return unless cpath mod = genv.resolve_cpath(cpath) - Source.new(Type::Singleton.new(genv, mod)).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(Type::Singleton.new(genv, mod)), vtx) end def show @@ -436,7 +436,7 @@ def contravariant_vertex0(genv, changes, vtx, subst) return unless cpath mod = genv.resolve_cpath(cpath) args = @args.map {|arg| arg.contravariant_vertex(genv, changes, subst) } - Source.new(Type::Instance.new(genv, mod, args)).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx) end def show @@ -458,23 +458,23 @@ def initialize(raw_decl, lenv) def subnodes = { types: } def covariant_vertex0(genv, changes, vtx, subst) - unified_elem = Vertex.new(self) # TODO + unified_elem = changes.new_covariant_vertex(genv, [self, :Elem]) # TODO elems = @types.map do |type| nvtx = type.covariant_vertex(genv, changes, subst) - nvtx.add_edge(genv, unified_elem) + changes.add_edge(genv, nvtx, unified_elem) nvtx end changes.add_edge(genv, Source.new(Type::Array.new(genv, elems, genv.gen_ary_type(unified_elem))), vtx) end def contravariant_vertex0(genv, changes, vtx, subst) - unified_elem = Vertex.new(self) + unified_elem = changes.new_contravariant_vertex(genv, [self, :Elem]) # TODO elems = @types.map do |type| nvtx = type.contravariant_vertex(genv, changes, subst) - nvtx.add_edge(genv, unified_elem) + changes.add_edge(genv, nvtx, unified_elem) nvtx end - Source.new(Type::Array.new(genv, elems, genv.gen_ary_type(unified_elem))).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(Type::Array.new(genv, elems, genv.gen_ary_type(unified_elem))), vtx) end def show @@ -513,7 +513,7 @@ def covariant_vertex0(genv, changes, vtx, subst) def contravariant_vertex0(genv, changes, vtx, subst) raise "unknown type variable: #{ @var }" unless subst[@var] - Source.new(Type::Var.new(genv, @var, subst[@var])).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(Type::Var.new(genv, @var, subst[@var])), vtx) end def show @@ -537,7 +537,7 @@ def covariant_vertex0(genv, changes, vtx, subst) def contravariant_vertex0(genv, changes, vtx, subst) @type.contravariant_vertex0(genv, changes, vtx, subst) - Source.new(genv.nil_type).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(genv.nil_type), vtx) end def show @@ -576,7 +576,7 @@ def covariant_vertex0(genv, changes, vtx, subst) end def contravariant_vertex0(genv, changes, vtx, subst) - Source.new(get_type(genv)).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(get_type(genv)), vtx) end def show @@ -648,7 +648,7 @@ def contravariant_vertex0(genv, changes, vtx, subst) return unless cpath mod = genv.resolve_cpath(cpath) args = @args.map {|arg| arg.contravariant_vertex(genv, changes, subst) } - Source.new(Type::Instance.new(genv, mod, args)).add_edge(genv, vtx) + changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx) end def show diff --git a/lib/typeprof/core/graph/box.rb b/lib/typeprof/core/graph/box.rb index 496324c7..8f4d5eec 100644 --- a/lib/typeprof/core/graph/box.rb +++ b/lib/typeprof/core/graph/box.rb @@ -184,8 +184,7 @@ def resolve_overloads(changes, genv, node, param_map, a_args, ret) @method_types.each do |method_type| param_map0 = param_map.dup if method_type.type_params - method_type.type_params.map do |var| - vtx = Vertex.new(node) + method_type.type_params.zip(yield(method_type)) do |var, vtx| param_map0[var] = vtx end end @@ -210,6 +209,7 @@ def resolve_overloads(changes, genv, node, param_map, a_args, ret) end end ret_vtx = method_type.return_type.covariant_vertex(genv, changes, param_map0) + changes.add_edge(genv, ret_vtx, ret) match_any_overload = true end @@ -644,6 +644,7 @@ def initialize(node, genv, recv, mid, a_args, subclasses) @a_args.block.add_edge(genv, self) if @a_args.block @ret = Vertex.new(node) @subclasses = subclasses + @generics = {} end attr_reader :recv, :mid, :ret @@ -673,7 +674,9 @@ def run0(genv, changes) ty_env[param] = arg end end - mdecl.resolve_overloads(changes, genv, @node, ty_env, @a_args, @ret) + mdecl.resolve_overloads(changes, genv, @node, ty_env, @a_args, @ret) do |method_type| + @generics[method_type] ||= method_type.type_params.map {|var| Vertex.new(@node) } + end end elsif !me.defs.empty? me.defs.each do |mdef| diff --git a/lib/typeprof/core/graph/change_set.rb b/lib/typeprof/core/graph/change_set.rb index a98b1a40..4a59c072 100644 --- a/lib/typeprof/core/graph/change_set.rb +++ b/lib/typeprof/core/graph/change_set.rb @@ -4,6 +4,7 @@ def initialize(node, target) @node = node @target = target @covariant_types = {} + @contravariant_types = {} @edges = [] @new_edges = [] @boxes = {} @@ -44,11 +45,16 @@ def copy_from(other) other.diagnostics.clear end - def new_vertex(genv, sig_type_node) + def new_covariant_vertex(genv, sig_type_node) # This is used to avoid duplicated vertex generation for the same sig node @covariant_types[sig_type_node] ||= Vertex.new(sig_type_node) end + def new_contravariant_vertex(genv, sig_type_node) + # This is used to avoid duplicated vertex generation for the same sig node + @contravariant_types[sig_type_node] ||= Vertex.new(sig_type_node) + end + def add_edge(genv, src, dst) raise src.class.to_s unless src.is_a?(BasicVertex) src.add_edge(genv, dst) if !@edges.include?([src, dst]) && !@new_edges.include?([src, dst]) diff --git a/lib/typeprof/core/graph/vertex.rb b/lib/typeprof/core/graph/vertex.rb index b841fbb2..b39d8f8f 100644 --- a/lib/typeprof/core/graph/vertex.rb +++ b/lib/typeprof/core/graph/vertex.rb @@ -142,6 +142,7 @@ def initialize(origin) when RBS::AST::Declarations::Base when ValueEntity when ActualArguments + when Array else raise "unknown class: #{ origin.class }" end diff --git a/scenario/misc/ivar-stuck-case.rb b/scenario/misc/ivar-stuck-case.rb new file mode 100644 index 00000000..ed352532 --- /dev/null +++ b/scenario/misc/ivar-stuck-case.rb @@ -0,0 +1,12 @@ +## update: test.rbs +class Foo + def check: [T] (T) -> [T] +end + +## update: test.rb +class Foo + def foo + @foo = [] + @foo = check(@foo) + end +end