Skip to content

Commit

Permalink
Merge pull request #2004 from ruby/optional-generics-param
Browse files Browse the repository at this point in the history
Use generic class definitions with default types in `Enumerator` and `Queue`
  • Loading branch information
soutaro committed Sep 13, 2024
2 parents 684b730 + 19072d9 commit 4192ea1
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 79 deletions.
20 changes: 10 additions & 10 deletions core/enumerable.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@ module Enumerable[unchecked out Elem] : _Each[Elem]
# With no block given, returns an Enumerator.
#
def reverse_each: () { (Elem arg0) -> untyped } -> void
| () -> ::Enumerator[Elem, void]
| () -> ::Enumerator[Elem]

# <!--
# rdoc-file=enum.c
Expand Down Expand Up @@ -1760,7 +1760,7 @@ module Enumerable[unchecked out Elem] : _Each[Elem]
# # show pythagorean triples less than 100
# p pythagorean_triples.take_while { |*, z| z < 100 }.force
#
def lazy: () -> Enumerator::Lazy[Elem, void]
def lazy: () -> Enumerator::Lazy[Elem]

# <!--
# rdoc-file=enum.c
Expand Down Expand Up @@ -2092,8 +2092,8 @@ module Enumerable[unchecked out Elem] : _Each[Elem]
# pp lines
# }
#
def chunk: [U] () { (Elem elt) -> U } -> ::Enumerator[[ U, ::Array[Elem] ], void]
| () -> ::Enumerator[Elem, ::Enumerator[[ untyped, ::Array[Elem] ], void]]
def chunk: [U] () { (Elem elt) -> U } -> ::Enumerator[[ U, ::Array[Elem] ]]
| () -> ::Enumerator[Elem, ::Enumerator[[ untyped, ::Array[Elem] ]]]

# <!--
# rdoc-file=enum.c
Expand Down Expand Up @@ -2142,7 +2142,7 @@ module Enumerable[unchecked out Elem] : _Each[Elem]
# Enumerable#slice_when does the same, except splitting when the block returns
# `true` instead of `false`.
#
def chunk_while: () { (Elem elt_before, Elem elt_after) -> boolish } -> ::Enumerator[::Array[Elem], void]
def chunk_while: () { (Elem elt_before, Elem elt_after) -> boolish } -> ::Enumerator[::Array[Elem]]

# <!--
# rdoc-file=enum.c
Expand Down Expand Up @@ -2204,7 +2204,7 @@ module Enumerable[unchecked out Elem] : _Each[Elem]
# Enumerable#chunk_while does the same, except splitting when the block returns
# `false` instead of `true`.
#
def slice_when: () { (Elem elt_before, Elem elt_after) -> boolish } -> ::Enumerator[::Array[Elem], void]
def slice_when: () { (Elem elt_before, Elem elt_after) -> boolish } -> ::Enumerator[::Array[Elem]]

# <!--
# rdoc-file=enum.c
Expand Down Expand Up @@ -2239,8 +2239,8 @@ module Enumerable[unchecked out Elem] : _Each[Elem]
# p e.map {|ll| ll[0...-1].map {|l| l.sub(/\\\n\z/, "") }.join + ll.last }
# #=>["foo\n", "barbaz\n", "\n", "qux\n"]
#
def slice_after: (untyped pattern) -> ::Enumerator[::Array[Elem], void]
| () { (Elem elt) -> boolish } -> ::Enumerator[::Array[Elem], void]
def slice_after: (untyped pattern) -> ::Enumerator[::Array[Elem]]
| () { (Elem elt) -> boolish } -> ::Enumerator[::Array[Elem]]

# <!--
# rdoc-file=enum.c
Expand Down Expand Up @@ -2396,6 +2396,6 @@ module Enumerable[unchecked out Elem] : _Each[Elem]
# }
# }
#
def slice_before: (untyped pattern) -> ::Enumerator[::Array[Elem], void]
| () { (Elem elt) -> boolish } -> ::Enumerator[::Array[Elem], void]
def slice_before: (untyped pattern) -> ::Enumerator[::Array[Elem]]
| () { (Elem elt) -> boolish } -> ::Enumerator[::Array[Elem]]
end
4 changes: 2 additions & 2 deletions core/enumerator.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
# puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] }
# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
#
class Enumerator[unchecked out Elem, out Return] < Object
class Enumerator[unchecked out Elem, out Return = void] < Object
include Enumerable[Elem]

# A convenience interface for `each` with optional block
Expand Down Expand Up @@ -567,7 +567,7 @@ end
# # This returns an array of items like a normal enumerator does.
# all_checked = active_items.select(&:checked)
#
class Enumerator::Lazy[out Elem, out Return] < Enumerator[Elem, Return]
class Enumerator::Lazy[out Elem, out Return = void] < Enumerator[Elem, Return]
# <!-- rdoc-file=enumerator.c -->
# Expands `lazy` enumerator to an array. See Enumerable#to_a.
#
Expand Down
2 changes: 1 addition & 1 deletion core/enumerator/product.rbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
%a{annotate:rdoc:skip}
class Enumerator[unchecked out Elem, out Return]
class Enumerator[unchecked out Elem, out Return = void]
# <!-- rdoc-file=enumerator.c -->
# Enumerator::Product generates a Cartesian product of any number of enumerable
# objects. Iterating over the product of enumerable objects is roughly
Expand Down
8 changes: 4 additions & 4 deletions core/integer.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -1168,10 +1168,10 @@ class Integer < Numeric
| (to: Numeric, ?by: Integer) { (Integer) -> void } -> void
| (by: Numeric, ?to: Numeric) { (Numeric) -> void } -> void
| () -> Enumerator[Integer, bot]
| (Numeric limit, ?Integer step) -> Enumerator[Integer, void]
| (Numeric limit, ?Numeric step) -> Enumerator[Numeric, void]
| (to: Numeric, ?by: Integer) -> Enumerator[Integer, void]
| (by: Numeric, ?to: Numeric) -> Enumerator[Numeric, void]
| (Numeric limit, ?Integer step) -> Enumerator[Integer]
| (Numeric limit, ?Numeric step) -> Enumerator[Numeric]
| (to: Numeric, ?by: Integer) -> Enumerator[Integer]
| (by: Numeric, ?to: Numeric) -> Enumerator[Numeric]

# <!--
# rdoc-file=numeric.c
Expand Down
11 changes: 6 additions & 5 deletions core/thread.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -1545,7 +1545,7 @@ end
#
# consumer.join
#
class Thread::Queue < Object
class Thread::Queue[Elem = untyped] < Object
# <!-- rdoc-file=thread_sync.c -->
# Pushes the given `object` to the queue.
#
Expand Down Expand Up @@ -1661,7 +1661,7 @@ class Thread::Queue < Object
# If `timeout` seconds have passed and no data is available `nil` is returned.
# If `timeout` is `0` it returns immediately.
#
def pop: (?boolish non_block, ?timeout: _ToF?) -> untyped
def pop: (?boolish non_block, ?timeout: _ToF?) -> Elem?

# <!--
# rdoc-file=thread_sync.c
Expand All @@ -1671,7 +1671,7 @@ class Thread::Queue < Object
# -->
# Pushes the given `object` to the queue.
#
def push: (untyped obj) -> void
def push: (Elem obj) -> void

# <!--
# rdoc-file=thread_sync.rb
Expand All @@ -1692,7 +1692,7 @@ end
#
# See Thread::Queue for an example of how a Thread::SizedQueue works.
#
class Thread::SizedQueue < Thread::Queue
class Thread::SizedQueue[Elem = untyped] < Thread::Queue[Elem]
# <!--
# rdoc-file=thread_sync.rb
# - <<(object, non_block = false, timeout: nil)
Expand Down Expand Up @@ -1755,7 +1755,8 @@ class Thread::SizedQueue < Thread::Queue
# If `timeout` seconds have passed and no space is available `nil` is returned.
# If `timeout` is `0` it returns immediately. Otherwise it returns `self`.
#
def push: (untyped obj, ?boolish non_block, timeout: _ToF?) -> void
def push: (Elem obj, ?boolish non_block) -> void
| (Elem obj, timeout: _ToF?) -> self?
end

class ConditionVariable = Thread::ConditionVariable
Expand Down
42 changes: 41 additions & 1 deletion lib/rbs/cli/validate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def validate_class_module_definition
end

if dt = param.default_type
void_type_context_validator(dt)
void_type_context_validator(dt, true)
no_self_type_validator(dt)
no_classish_type_validator(dt)
@validator.validate_type(dt, context: nil)
Expand Down Expand Up @@ -232,6 +232,22 @@ def validate_interface
location: decl.decl.location&.aref(:type_params)
)

decl.decl.type_params.each do |param|
if ub = param.upper_bound_type
void_type_context_validator(ub)
no_self_type_validator(ub)
no_classish_type_validator(ub)
@validator.validate_type(ub, context: nil)
end

if dt = param.default_type
void_type_context_validator(dt, true)
no_self_type_validator(dt)
no_classish_type_validator(dt)
@validator.validate_type(dt, context: nil)
end
end

decl.decl.members.each do |member|
case member
when AST::Members::MethodDefinition
Expand Down Expand Up @@ -278,7 +294,31 @@ def validate_type_alias
@builder.expand_alias1(name).tap do |type|
@validator.validate_type type, context: nil
end

@validator.validate_type_alias(entry: decl)

@validator.validate_type_params(
decl.decl.type_params,
type_name: name,
location: decl.decl.location&.aref(:type_params)
)

decl.decl.type_params.each do |param|
if ub = param.upper_bound_type
void_type_context_validator(ub)
no_self_type_validator(ub)
no_classish_type_validator(ub)
@validator.validate_type(ub, context: nil)
end

if dt = param.default_type
void_type_context_validator(dt, true)
no_self_type_validator(dt)
no_classish_type_validator(dt)
@validator.validate_type(dt, context: nil)
end
end

no_self_type_validator(decl.decl.type)
no_classish_type_validator(decl.decl.type)
void_type_context_validator(decl.decl.type)
Expand Down
7 changes: 7 additions & 0 deletions lib/rbs/test/type_check.rb
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,13 @@ def value(val, type)
Test.call(val, IS_AP, instance_class)
when Types::ClassInstance
klass = get_class(type.name) or return false
if params = builder.env.normalized_module_class_entry(type.name.absolute!)&.type_params
args = AST::TypeParam.normalize_args(params, type.args)
unless args == type.args
type = Types::ClassInstance.new(name: type.name, args: args, location: type.location)
end
end

case
when klass == ::Array
Test.call(val, IS_AP, klass) && each_sample(val).all? {|v| value(v, type.args[0]) }
Expand Down
Loading

0 comments on commit 4192ea1

Please sign in to comment.