Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/typeprof/core/ast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ def self.create_target_node(raw_node, lenv)
IndexWriteNode.new(raw_node, dummy_node, lenv)
when :call_target_node
CallWriteNode.new(raw_node, dummy_node, lenv)
when :multi_target_node
MultiTargetNode.new(raw_node, dummy_node, lenv)
else
pp raw_node
raise "not supported yet: #{ raw_node.type }"
Expand Down
57 changes: 57 additions & 0 deletions lib/typeprof/core/ast/misc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,63 @@ def retrieve_at(pos, &blk)
end
end

class MultiTargetNode < Node
def initialize(raw_node, rhs, lenv)
super(raw_node, lenv)
@rhs = rhs
@lefts = raw_node.lefts.map do |raw_lhs|
AST.create_target_node(raw_lhs, lenv)
end
if raw_node.rest
case raw_node.rest.type
when :splat_node
if raw_node.rest.expression
@rest = AST.create_target_node(raw_node.rest.expression, lenv)
end
when :implicit_rest_node
# no assignment target
else
raise "unexpected rest node in multi_target: #{raw_node.rest.type}"
end
end
@rights = raw_node.rights.map do |raw_rhs|
AST.create_target_node(raw_rhs, lenv)
end
end

attr_reader :rhs, :lefts, :rest, :rights

def subnodes = { rhs:, lefts:, rest:, rights: }

def install0(genv)
# The rhs should be installed by the parent MultiWriteNode
# Here we set up the multi-assignment box for nested destructuring
value = @rhs.install(genv)

@lefts.each {|lhs| lhs.install(genv) }
@lefts.each {|lhs| lhs.rhs.ret || raise(lhs.rhs.inspect) }
lefts = @lefts.map {|lhs| lhs.rhs.ret }

rest_elem = nil
if @rest
rest_elem = Vertex.new(self)
@rest.install(genv)
@rest.rhs.ret || raise(@rest.rhs.inspect)
@changes.add_edge(genv, Source.new(Type::Instance.new(genv, genv.mod_ary, [rest_elem])), @rest.rhs.ret)
end

rights = nil
if @rights && !@rights.empty?
@rights.each {|rhs| rhs.install(genv) }
@rights.each {|rhs| rhs.rhs.ret || raise(rhs.rhs.inspect) }
rights = @rights.map {|rhs| rhs.rhs.ret }
end

box = @changes.add_masgn_box(genv, value, lefts, rest_elem, rights)
box.ret
end
end

class MatchWriteNode < Node
def initialize(raw_node, lenv)
super(raw_node, lenv)
Expand Down
2 changes: 1 addition & 1 deletion lib/typeprof/core/graph/box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ def run0(genv, changes)
else
if @lefts.size >= 1
edges << [Source.new(ty), @lefts[0]]
elsif @rights.size >= 1
elsif @rights && @rights.size >= 1
edges << [Source.new(ty), @rights[0]]
else
edges << [Source.new(ty), @rest_elem]
Expand Down
3 changes: 2 additions & 1 deletion lib/typeprof/core/type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def splat_assign(genv, lefts, rest_elem, rights)
edges = []
state = :left
j = nil
rights_size = rights ? rights.size : 0
@elems.each_with_index do |elem, i|
case state
when :left
Expand All @@ -123,7 +124,7 @@ def splat_assign(genv, lefts, rest_elem, rights)
redo
end
when :rest
if @elems.size - i > rights.size
if @elems.size - i > rights_size
edges << [elem, rest_elem]
else
state = :right
Expand Down
34 changes: 34 additions & 0 deletions scenario/variable/masgn_nested.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## update
def test_nested_destructuring
a, (b, c) = [1, [2, 3]]
[a, b, c]
end

def test_nested_with_strings
x, (y, z) = ["foo", ["bar", "baz"]]
[x, y, z]
end

def test_deeper_nesting
a, (b, (c, d)) = [1, [2, [3, 4]]]
[a, b, c, d]
end

def test_nested_with_rest
a, (b, *rest) = [1, [2, 3, 4]]
[a, b, rest]
end

def test_nested_with_rest_and_rights
a, (b, *rest, c) = [1, [2, 3, 4, 5]]
[a, b, rest, c]
end

## assert
class Object
def test_nested_destructuring: -> [Integer, Integer, Integer]
def test_nested_with_strings: -> [String, String, String]
def test_deeper_nesting: -> [Integer, Integer, Integer, Integer]
def test_nested_with_rest: -> [Integer, Integer, Array[Integer]]
def test_nested_with_rest_and_rights: -> [Integer, Integer, Array[Integer], Integer]
end