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
9 changes: 2 additions & 7 deletions lib/typeprof/core/ast/call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,8 @@ def install0(genv)
include_leading_positionals: @forwarding_arguments != :rest,
activation_required: @forwarding_arguments == :rest,
)
leading_args = @positional_args.map do |arg|
if arg.is_a?(DummyNilNode)
@lenv.get_var(:"*anonymous_rest")
else
arg.install(genv)
end
end
# An anonymous rest cannot appear here: `bar(*, ...)` is a syntax error
leading_args = @positional_args.map {|arg| arg.install(genv) }
a_args = forward_a_args.prepend_positionals(leading_args, @splat_flags)
a_args = a_args.with_keywords(@keyword_args.install(genv)) if @keyword_args
else
Expand Down
5 changes: 4 additions & 1 deletion lib/typeprof/core/env/method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ def accept_actual_arguments(genv, changes, a_args)
private

def build_keyword_args(genv, changes, node)
opt_keyword_pairs = @opt_keyword_pairs.reject {|_name, vtx| vtx.types.empty? }
opt_keyword_pairs = @opt_keyword_pairs

if @req_keyword_pairs.empty? && opt_keyword_pairs.empty?
return @rest_keywords, !!@rest_keywords
Expand All @@ -414,6 +414,9 @@ def build_keyword_args(genv, changes, node)

if literal_pairs.empty?
[Source.new(base_hash_type), false]
elsif @rest_keywords
fallback = Source.new(Type::Record.new(genv, literal_pairs, base_hash_type))
[changes.add_keyword_merge_box(genv, @rest_keywords, literal_pairs, fallback).ret, false]
else
[Source.new(Type::Record.new(genv, literal_pairs, base_hash_type)), false]
end
Expand Down
29 changes: 28 additions & 1 deletion lib/typeprof/core/graph/box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,33 @@ def run0(genv, changes)
end
end

# Merges the keywords being forwarded into the `**rest` hash while keeping the
# rest's own fields, so that the callee can still tell the two apart.
class KeywordMergeBox < Box
def initialize(node, genv, rest, literal_pairs, fallback)
super(node)
@rest = rest
@literal_pairs = literal_pairs
@fallback = fallback
@rest.add_edge(genv, self)
@ret = Vertex.new(node)
end

attr_reader :ret

def run0(genv, changes)
merged = false
@rest.each_type do |ty|
if ty.is_a?(Type::Record)
fields = ty.fields.merge(@literal_pairs)
changes.add_edge(genv, Source.new(Type::Record.new(genv, fields, ty.base_type(genv))), @ret)
merged = true
end
end
changes.add_edge(genv, @fallback, @ret) unless merged
end
end

class HashSplatBox < Box
def initialize(node, genv, hsh, unified_key, unified_val)
super(node)
Expand Down Expand Up @@ -1077,7 +1104,7 @@ def run0(genv, changes)
error_count = 0
resolve(genv, changes) do |me, ty, mid, orig_ty|
if @node.is_a?(AST::YieldNode) && mid == :call && orig_ty.is_a?(Type::Symbol)
box = add_symbol_proc_call_box(changes, genv, orig_ty.sym, @a_args.positionals, @a_args.keywords)
box = add_symbol_proc_call_box(changes, genv, orig_ty.sym, a_args.positionals, a_args.keywords)
changes.add_edge(genv, box.ret, @ret) if box
elsif !me
if @unresolved_recv
Expand Down
5 changes: 5 additions & 0 deletions lib/typeprof/core/graph/change_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ def add_splat_box(genv, arg, idx = nil, unresolved_recv = nil)
new_boxes[key] ||= SplatBox.new(@node, genv, arg, idx, unresolved_recv)
end

def add_keyword_merge_box(genv, rest, literal_pairs, fallback)
key = [:kw_merge, rest, literal_pairs, fallback]
new_boxes[key] ||= KeywordMergeBox.new(@node, genv, rest, literal_pairs, fallback)
end

def add_hash_splat_box(genv, arg, unified_key, unified_val)
key = [:hash_splat, arg, unified_key, unified_val]
new_boxes[key] ||= HashSplatBox.new(@node, genv, arg, unified_key, unified_val)
Expand Down
33 changes: 33 additions & 0 deletions scenario/misc/super_keywords.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## update
class B
def foo(a, b: 1) = [a, b]
end
class C < B
def foo(a, b: 2) = super
end
C.new.foo(1, b: "s")

## assert
class B
def foo: (Integer, ?b: Integer | String) -> [Integer, Integer | String]
end
class C < B
def foo: (Integer, ?b: Integer | String) -> [Integer, Integer | String]
end

## update
class B
def foo(b: 1, **r) = [b, r]
end
class C < B
def foo(b: 1, **r) = super
end
C.new.foo(b: "s", z: 3)

## assert
class B
def foo: (?b: Integer | String, **Integer) -> [Integer | String, { z: Integer }]
end
class C < B
def foo: (?b: Integer | String, **Integer) -> [Integer | String, { z: Integer }]
end
Loading