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
22 changes: 21 additions & 1 deletion lib/typeprof/core/graph/box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,27 @@ def resolve_overloads(changes, genv, node, param_map, a_args, ret, &blk)
# "failed to resolve overloads" diagnostics for untyped arguments.
# We still set up dependency edges so the box re-runs when the
# empty arguments later receive types.
if a_args.positionals.any? {|vtx| vtx.types.empty? }
#
# For splat arguments, the positional vertex itself holds Array
# types (non-empty), but the array *element* vertex may be empty.
# The same oscillation occurs when match_arguments? extracts
# elements via get_rest_args and the universal typecheck on the
# flattened element list fails due to conflicting array sources.
# We detect this by checking element vertices of splatted arrays.
has_uninformative_args = a_args.positionals.any? {|vtx| vtx.types.empty? }
unless has_uninformative_args
a_args.positionals.each_with_index do |vtx, i|
next unless a_args.splat_flags[i]
vtx.each_type do |ty|
base = ty.base_type(genv)
if base.is_a?(Type::Instance) && base.mod == genv.mod_ary && base.args[0]
has_uninformative_args = true if base.args[0].types.empty?
end
end
break if has_uninformative_args
end
end
if has_uninformative_args
a_args.positionals.each do |vtx|
changes.add_edge(genv, vtx, changes.target)
end
Expand Down
19 changes: 19 additions & 0 deletions scenario/regressions/splat-overload-oscillation-unseeded.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## update: test.rbs
class Foo
def self.f: (*Integer) -> String | (*String) -> Symbol
end

## update: test.rb
# Minimal reproduction: unseeded splat overload oscillation.
# The splat array's element vertex is empty, triggering the
# skip in overload resolution.
def check
@x = Foo.f(*[@x])
end

## assert
class Object
def check: -> untyped
end

## diagnostics
21 changes: 21 additions & 0 deletions scenario/regressions/splat-overload-oscillation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## update: test.rbs
class Foo
def self.f: (*Integer) -> String | (*String) -> Symbol
end

## update: test.rb
# Splat arguments with rest-positional overloads used to cause
# oscillation. The overload fix skips resolution when any splat
# element vertex has no type information, preventing the cycle.
def check
@args = [42]
@x = Foo.f(*@args)
@args = [@x]
end

## assert
class Object
def check: -> [untyped]
end

## diagnostics