-
Notifications
You must be signed in to change notification settings - Fork 229
[Kernel] Tweak signatures for conversion methods, and add unit tests in #2683
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -481,9 +481,12 @@ module Kernel : BasicObject | |
| # | ||
| def self?.Complex: (_ToC complex_like, ?exception: true) -> Complex | ||
| | (_ToC complex_like, exception: bool) -> Complex? | ||
| | (Numeric | String real, ?Numeric | String imag, ?exception: true) -> Complex | ||
| | (Numeric | String real, ?Numeric | String imag, exception: bool) -> Complex? | ||
| | (untyped, ?untyped, ?exception: bool) -> Complex? | ||
| | (Numeric numeric, ?exception: bool) -> Complex | ||
| | (String real_or_both, ?exception: true) -> Complex | ||
| | (untyped real_or_both, exception: bool) -> Complex? | ||
| | (Numeric | String real, Numeric | String imag, ?exception: true) -> Complex | ||
| | (Numeric | String real, Integer | Float | Rational | Complex imag, exception: bool) -> Complex | ||
| | (Numeric | String real, untyped imag, exception: bool) -> Complex? | ||
|
|
||
| # <!-- | ||
| # rdoc-file=kernel.rb | ||
|
|
@@ -503,7 +506,7 @@ module Kernel : BasicObject | |
| # | ||
| def self?.Float: (_ToF float_like, ?exception: true) -> Float | ||
| | (_ToF float_like, exception: bool) -> Float? | ||
| | (untyped, ?exception: bool) -> Float? | ||
| | (untyped, exception: bool) -> Float? | ||
|
|
||
| # <!-- | ||
| # rdoc-file=object.c | ||
|
|
@@ -525,7 +528,7 @@ module Kernel : BasicObject | |
| # Hash(nil) # => {} | ||
| # Hash([]) # => {} | ||
| # | ||
| def self?.Hash: [K, V] (nil | [] _empty) -> Hash[K, V] | ||
| def self?.Hash: [K, V] (nil | []) -> Hash[K, V] | ||
| | [K, V] (hash[K, V] hash_like) -> Hash[K, V] | ||
|
|
||
| # <!-- | ||
|
|
@@ -615,7 +618,7 @@ module Kernel : BasicObject | |
| | (int | _ToI int_like, exception: bool) -> Integer? | ||
| | (string str, int base, ?exception: true) -> Integer | ||
| | (string str, int base, exception: bool) -> Integer? | ||
| | (untyped, ?untyped, ?exception: bool) -> Integer? | ||
| | (untyped, ?int base, exception: bool) -> Integer? | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If provided, the second argument actually has to be an |
||
|
|
||
| # <!-- | ||
| # rdoc-file=rational.c | ||
|
|
@@ -656,14 +659,19 @@ module Kernel : BasicObject | |
| # | ||
| def self?.Rational: (int | _ToR rational_like, ?exception: true) -> Rational | ||
| | (int | _ToR rational_like, exception: bool) -> Rational? | ||
| | (int | _ToR numer, ?int | _ToR denom, ?exception: true) -> Rational | ||
| | (int | _ToR numer, ?int | _ToR denom, exception: bool) -> Rational? | ||
| | [T] (Numeric & _RationalDiv[T] numer, Numeric denom, ?exception: bool) -> T | ||
| | (int | _ToR numer, int | _ToR denom, ?exception: true) -> Rational | ||
| | (int | _ToR numer, int | _ToR denom, exception: bool) -> Rational? | ||
|
Comment on lines
+662
to
+663
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I simplified the signature of this a bit, collapsing the |
||
| | [T < Numeric] (T value, 1, ?exception: bool) -> T | ||
| | (untyped, ?untyped, ?exception: bool) -> Rational? | ||
| | [T] (Numeric & _RationalDiv[T] numer, Numeric denom, ?exception: bool) -> T | ||
| | (untyped, ?untyped, exception: bool) -> Rational? | ||
|
|
||
| # An interface used in `Kernel.Rational` when both arguments are `Numeric`s, | ||
| # but don't define `to_r` or `to_int`. | ||
| # | ||
| # The return type of the division is the return type of `Rational(numer, denom)`. | ||
| interface _RationalDiv[T] | ||
| def /: (Numeric) -> T | ||
| # Divide the numerator by `denom` | ||
| def /: (Numeric denom) -> T | ||
| end | ||
|
|
||
| # <!-- | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -356,7 +356,12 @@ def value(val, type) | |
| when Types::Variable | ||
| true | ||
| when Types::Literal | ||
| type.literal == val | ||
| begin | ||
| type.literal == val | ||
| rescue NoMethodError | ||
| raise if defined?(val.==) | ||
| false | ||
|
Comment on lines
+360
to
+363
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is needed because one of the
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add test cases for |
||
| end | ||
| when Types::Union | ||
| type.types.any? {|type| value(val, type) } | ||
| when Types::Intersection | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -180,11 +180,11 @@ def send_setup(method_type, receiver, method, args, proc) | |
| ) | ||
| errors = typecheck.method_call(method, method_type, trace, errors: []) | ||
|
|
||
| assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }, "Call trace does not match with given method type: #{trace.inspect}" | ||
| assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }, proc { "Call trace does not match with given method type: #{trace.inspect}" } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of always creating the string for the |
||
|
|
||
| method_defs = method_defs(method) | ||
| all_errors = method_defs.map {|t| typecheck.method_call(method, t.type, trace, errors: [], annotations: t.each_annotation.to_a) } | ||
| assert all_errors.any? {|es| es.empty? }, "Call trace does not match one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}" | ||
| assert all_errors.any? {|es| es.empty? }, proc { "Call trace does not match one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}" } | ||
|
|
||
| raise exception if exception | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,73 +25,208 @@ def test_Array | |
| assert_send_type "(nil) -> []", | ||
| Kernel, :Array, nil | ||
|
|
||
| with_array(1r, 2r).chain([ToA.new(1r,2r)]).each do |ary| | ||
| assert_send_type "(::array[Rational] | ::_ToA[Rational]) -> Array[Rational]", | ||
| Kernel, :Array, ary | ||
| with_untyped do |ele| | ||
| with_array(ele, ele).and ToA.new(ele, ele) do |ary| | ||
| assert_send_type "[T] (array[T] | _ToA[T]) -> Array[T]", | ||
| Kernel, :Array, ary | ||
| end | ||
|
|
||
| next if defined?(ele.to_a) || defined?(ele.to_ary) | ||
| assert_send_type "[T] (T) -> [T]", | ||
| Kernel, :Array, ele | ||
| end | ||
| end | ||
|
|
||
| def test_Complex | ||
| # (_ToC complex_like, ?exception: true) -> Complex | ||
| assert_send_type "(_ToC) -> Complex", | ||
| Kernel, :Complex, ToC.new | ||
| assert_send_type "(_ToC, exception: true) -> Complex", | ||
| Kernel, :Complex, ToC.new, exception: true | ||
|
|
||
| # (_ToC complex_like, exception: bool) -> Complex? | ||
| assert_send_type "(_ToC, exception: bool) -> Complex", | ||
| Kernel, :Complex, ToC.new, exception: false | ||
| assert_send_type "(_ToC, exception: bool) -> nil", | ||
| Kernel, :Complex, Class.new(BlankSlate){ def to_c = fail }.new, exception: false | ||
|
|
||
| assert_send_type "(Rational) -> [Rational]", | ||
| Kernel, :Array, 1r | ||
| numeric = Class.new(Numeric).new | ||
|
|
||
| # (Numeric numeric, ?exception: bool) -> Complex | ||
| with 1, 1r, 1.0, (1+0i), numeric do |real| | ||
| assert_send_type "(Numeric) -> Complex", | ||
| Kernel, :Complex, real | ||
|
|
||
| # Single `Numeric`s can never fail | ||
| with_bool do |exception| | ||
| assert_send_type "(Numeric, exception: bool) -> Complex", | ||
| Kernel, :Complex, real, exception: exception | ||
| end | ||
| end | ||
|
|
||
| # (String real_or_both, ?exception: true) -> Complex | ||
| assert_send_type "(String) -> Complex", | ||
| Kernel, :Complex, '1' | ||
| assert_send_type "(String, exception: true) -> Complex", | ||
| Kernel, :Complex, '1', exception: true | ||
|
|
||
| # (untyped real_or_both, exception: bool) -> Complex? | ||
| with_untyped.and 'oops' do |real_untype| | ||
| assert_send_type '(untyped, exception: bool) -> Complex?', | ||
| Kernel, :Complex, real_untype, exception: false | ||
| end | ||
|
|
||
| with '1', 1, 1r, 1.0, (1+0i), numeric do |real| | ||
| with '2', 2, 2r, 2.0, (2+0i), numeric do |imag| | ||
| # (Numeric | String real, Numeric | String imag, ?exception: true) -> Complex | ||
| assert_send_type "(Numeric | String, Numeric | String) -> Complex", | ||
| Kernel, :Complex, real, imag | ||
| assert_send_type "(Numeric | String, Numeric | String, exception: true) -> Complex", | ||
| Kernel, :Complex, real, imag, exception: true | ||
|
|
||
| # Complex has an awkward edgecase where `exception: false` will unconditionally return `nil` | ||
| # if the imaginary argument is not one of the builtin `Numeric`s. Oddly enough, it's not for | ||
| # the `real` one... | ||
| case imag | ||
| when Integer, Float, Rational, Complex | ||
| # (Numeric | String real, Integer | Float | Rational | Complex imag, exception: bool) -> Complex | ||
| assert_send_type "(Numeric | String, Integer | Float | Rational | Complex, exception: bool) -> Complex", | ||
| Kernel, :Complex, real, imag, exception: false | ||
| end | ||
| end | ||
|
|
||
| # (Numeric | String real, untyped, exception: bool) -> Complex? | ||
| with_untyped.and 'oops', numeric do |imag| | ||
| next if [Integer, Float, Rational, Complex].any? { _1 === imag } | ||
| assert_send_type "(Numeric | String, untyped, exception: bool) -> nil", | ||
| Kernel, :Complex, real, imag, exception: false | ||
| end | ||
| end | ||
| end | ||
|
|
||
|
|
||
| def test_Float | ||
| with_float 1.0 do |float| | ||
| assert_send_type "(::float) -> Float", | ||
| Kernel, :Float, float | ||
| assert_send_type "(::float, exception: true) -> Float", | ||
| Kernel, :Float, float, exception: true | ||
| assert_send_type "(::float, exception: bool) -> Float?", | ||
| Kernel, :Float, float, exception: false | ||
| with 1, 1.0, ToF.new(1.0), '1e3' do |float_like| | ||
| assert_send_type "(_ToF) -> Float", | ||
| Kernel, :Float, float_like | ||
| assert_send_type "(_ToF, exception: true) -> Float", | ||
| Kernel, :Float, float_like, exception: true | ||
| assert_send_type "(_ToF, exception: bool) -> Float", | ||
| Kernel, :Float, float_like, exception: false | ||
| end | ||
|
|
||
| assert_send_type "(untyped, ?exception: bool) -> Float?", | ||
| Kernel, :Float, :hello, exception: false | ||
| with_untyped do |untyped| | ||
| next if defined? untyped.to_f | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't determine if the subsequent tests are actually being executed. To determine this, it would be necessary to rely on the implementation of A BasicObject, or some other object that is guaranteed not to have a |
||
| assert_send_type "(untyped, exception: bool) -> nil", | ||
| Kernel, :Float, untyped, exception: false | ||
| end | ||
| end | ||
|
|
||
| def test_Hash | ||
| assert_send_type "(nil) -> Hash[untyped, untyped]", | ||
| assert_send_type "[K, V] (nil) -> Hash[K, V]", | ||
| Kernel, :Hash, nil | ||
| assert_send_type "([]) -> Hash[untyped, untyped]", | ||
| assert_send_type "[K, V] ([]) -> Hash[K, V]", | ||
| Kernel, :Hash, [] | ||
|
|
||
| with_hash 'a' => 3 do |hash| | ||
| assert_send_type "(::hash[String, Integer]) -> Hash[String, Integer]", | ||
| assert_send_type "[K, V] (hash[K, V]) -> Hash[K, V]", | ||
| Kernel, :Hash, hash | ||
| end | ||
| end | ||
|
|
||
| def test_Integer | ||
| with_int(1).chain([ToI.new(1)]).each do |int| | ||
| assert_send_type "(::int | ::_ToI) -> Integer", | ||
| with_int.and ToI.new do |int| | ||
| assert_send_type "(int | _ToI) -> Integer", | ||
| Kernel, :Integer, int | ||
| assert_send_type "(::int | ::_ToI, exception: true) -> Integer", | ||
| assert_send_type "(int | _ToI, exception: true) -> Integer", | ||
| Kernel, :Integer, int, exception: true | ||
| assert_send_type "(::int | ::_ToI, exception: bool) -> Integer?", | ||
| assert_send_type "(int | _ToI, exception: bool) -> Integer?", | ||
| Kernel, :Integer, int, exception: false | ||
| end | ||
|
|
||
| with_string "123" do |string| | ||
| with_int 8 do |base| | ||
| assert_send_type "(::string, ::int) -> Integer", | ||
| assert_send_type "(string, int) -> Integer", | ||
| Kernel, :Integer, string, base | ||
| assert_send_type "(::string, ::int, exception: true) -> Integer", | ||
| assert_send_type "(string, int, exception: true) -> Integer", | ||
| Kernel, :Integer, string, base, exception: true | ||
| assert_send_type "(::string, ::int, exception: bool) -> Integer?", | ||
| assert_send_type "(string, int, exception: bool) -> Integer?", | ||
| Kernel, :Integer, string, base, exception: false | ||
| end | ||
| end | ||
|
|
||
| assert_send_type "(untyped, ?exception: bool) -> Integer?", | ||
| Kernel, :Integer, :hello, exception: false | ||
| with_untyped do |untyped| | ||
| assert_send_type "(untyped, exception: bool) -> Integer?", | ||
| Kernel, :Integer, untyped, exception: false | ||
|
|
||
| with_int 10 do |base| | ||
| assert_send_type "(untyped, int, exception: bool) -> Integer?", | ||
| Kernel, :Integer, untyped, base, exception: false | ||
| end | ||
| end | ||
| end | ||
|
|
||
| def test_Rational | ||
| with_int(1).and ToR.new(1r) do |numer| | ||
| assert_send_type "(int | _ToR) -> Rational", | ||
| Kernel, :Rational, numer | ||
| assert_send_type "(int | _ToR, exception: true) -> Rational", | ||
| Kernel, :Rational, numer, exception: true | ||
| assert_send_type "(int | _ToR, exception: bool) -> Rational", | ||
| Kernel, :Rational, numer, exception: false | ||
|
|
||
| with_int(2).and ToR.new(2r) do |denom| | ||
| assert_send_type "(int | _ToR, int | _ToR) -> Rational", | ||
| Kernel, :Rational, numer, denom | ||
| assert_send_type "(int | _ToR, int | _ToR, exception: true) -> Rational", | ||
| Kernel, :Rational, numer, denom, exception: true | ||
| assert_send_type "(int | _ToR, int | _ToR, exception: bool) -> Rational", | ||
| Kernel, :Rational, numer, denom, exception: false | ||
| end | ||
| end | ||
|
|
||
| bad_int = Class.new(BlankSlate){ def to_int = fail }.new | ||
| bad_rat = Class.new(BlankSlate){ def to_r = fail }.new | ||
| with bad_int, bad_rat do |bad_numer| | ||
| assert_send_type "(int | _ToR, exception: bool) -> nil", | ||
| Kernel, :Rational, bad_numer, exception: false | ||
| assert_send_type "(int | _ToR, int | _ToR, exception: bool) -> nil", | ||
| Kernel, :Rational, bad_numer, bad_numer, exception: false | ||
| end | ||
|
|
||
|
|
||
| numeric = Class.new(Numeric).new | ||
| assert_send_type "[T < _Numeric] (T numer, 1) -> T", | ||
| Kernel, :Rational, numeric, 1 | ||
| assert_send_type "[T < _Numeric] (T numer, 1, exception: bool) -> T", | ||
| Kernel, :Rational, numeric, 1, exception: true | ||
| assert_send_type "[T < _Numeric] (T numer, 1, exception: bool) -> T", | ||
| Kernel, :Rational, numeric, 1, exception: false | ||
|
|
||
| numeric_div = Class.new(Numeric){ def /(other) = :hello }.new | ||
|
|
||
| assert_send_type "[T] (Numeric & Kernel::_RationalDiv[T] numer, Numeric denom) -> T", | ||
| Kernel, :Rational, numeric_div, numeric | ||
| assert_send_type "[T] (Numeric & Kernel::_RationalDiv[T] numer, Numeric denom, exception: bool) -> T", | ||
| Kernel, :Rational, numeric_div, numeric, exception: true | ||
| assert_send_type "[T] (Numeric & Kernel::_RationalDiv[T] numer, Numeric denom, exception: bool) -> T", | ||
| Kernel, :Rational, numeric_div, numeric, exception: false | ||
|
|
||
| with_untyped do |numer| | ||
| with_untyped do |denom| | ||
| assert_send_type "(untyped, untyped, exception: bool) -> Rational?", | ||
| Kernel, :Rational, numer, denom, exception: false | ||
| end | ||
| end | ||
| end | ||
|
|
||
| def test_String | ||
| with_string do |string| | ||
| assert_send_type "(::string) -> String", | ||
| assert_send_type "(string) -> String", | ||
| Kernel, :String, string | ||
| end | ||
|
|
||
| assert_send_type "(::_ToS) -> String", | ||
| assert_send_type "(_ToS) -> String", | ||
| Kernel, :String, ToS.new | ||
| end | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one's been changed because, after looking into it,
Complexis quite messy:Complex(Numeric)can never returnnil, regardless ofexceptionComplex(Numeric | String, denom, exception: false)will actually returnnilwhen the denom is a non-Integer | Float | Complex | RationalNumeric. Wonky, becauseexception: true/omitted doesn't do that, and it's only for the second argument