Skip to content
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

RBS additions #1

Merged
merged 17 commits into from
Jan 8, 2024
Merged

Conversation

larskanis
Copy link

Happy new year as well @ParadoxV5 !

I did some minor adjustments to the CI to make it green. Then I checked the RBS files against the specs and was surprised about the many failures. So I went further to fix them in the subsequent commits.

I used the following command line:

RBS_TEST_TARGET='FFI::*' RUBYOPT='-rrbs/test/setup'  bundle exec rake spec

I was able to fix most RBS issues, but a few specs fail with mismatch in Struct#layout . I wasn't able to fix it properly. And Function#attach fails with endless recursion. Probably an incompatibility with super call and the prepend method

Could you please check my additions?

rb_class_new_instance() automatically passes the block forward to the initialize method.
That way the block of a callback is forwarded to the Struct initializer, like here in the specs:

      s = LibTest::S8F32S32.new
      ret = LibTest.testCallbackVrT { s }

Struct#initialize ignores the block, so that the code works, but it doesn't make sense.

This stood up when enabling rbs type checks, which complained about an unspecified block to Struct#initialize.
They stood up when running with rbs type checks enabled.
@ParadoxV5 ParadoxV5 self-assigned this Jan 2, 2024
Copy link
Owner

@ParadoxV5 ParadoxV5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By Struct#layout you meant Struct.layout (AKA Struct::layout).

Test failures show that a [Symbol, Integer] 2-tuple is also valid to come after a member name. This wasn’t documented.

layout :a, :int, :c, :test_enum,
  :d, [ TestEnum, TestEnum.symbols.length ]
layout( :a, [:char, 10],
        :i, :int,
        :f, :float,
        :d, :double,
        :s, :short,
        :l, :long,
        :j, :long_long,
        :c, :char )

It’d be more consistent (to type check with, as well) if the array form has each entry be a 2–3 tuple in the form [Symbol name, layout_type type, ?Integer offset].

[Update]
I thought the integer in the 2-tuple is the offset, but the tuple actually stands for an array, with the first element as the field type and the interger the array size.
This is documented in “Char arrays” in the wiki, though the array can be of any type.

sig/ffi/enum.rbs Outdated Show resolved Hide resolved
Comment on lines 18 to +19
def []: (Symbol query) -> Integer
| (Integer query) -> Symbol
| (Integer query) -> Symbol?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so, may as well?

Suggested change
def []: (Symbol query) -> Integer
| (Integer query) -> Symbol
| (Integer query) -> Symbol?
def []: (Symbol query) -> Integer?
| (Integer query) -> Symbol?

RBS for Ruby Core classes like ::Array does it like this:

Suggested change
def []: (Symbol query) -> Integer
| (Integer query) -> Symbol
| (Integer query) -> Symbol?
def []: %a{implicitly-returns-nil} (Symbol query) -> Integer
| %a{implicitly-returns-nil} (Integer query) -> Symbol

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please decide on your own? I only read a crash course about RBS and static typing...

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%a{implicitly-returns-nil} doesn’t work with RBS Tests.

) -> self

# ?blocking: boolish?, ?convention: Library::convention?, ?enums: Enums?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See ruby/rbs#1639

For now, it’s more intuitive if there isn’t a blank line gapping #initialize and this comment.

@@ -28,7 +27,6 @@ module FFI
def read_string_length: (Integer len) -> String
def read_string_to_null: () -> String
def slice: (Integer offset, Integer length) -> Pointer
def to_ptr: () -> self
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to indicate that Pointer#to_ptr returns self (which is a Pointer).

sig/ffi/pointer.rbs Show resolved Hide resolved
sig/ffi/struct_by_reference.rbs Outdated Show resolved Hide resolved
@@ -159,6 +159,6 @@ module FFI
def write_array_of_long_long: (Array[Integer] ary) -> self
def write_array_of_float: (Array[Numeric] ary) -> self
def write_array_of_double: (Array[Numeric] ary) -> self
def write_array_of_pointer: (Array[Pointer::_ToPtr] ary) -> self
def write_array_of_pointer: (Array[Pointer::_ToPtr?] ary) -> self
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that nil is also accepted whereëver _ToPtr is, would type pointer = _ToPtr? in Pointer RBS be a valuable addition?

RBS builtins has things like type int = Integer | _ToInt, albeit they’re redundant.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please decide on your own?

Copy link
Owner

@ParadoxV5 ParadoxV5 Jan 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s implicit, but #(put|write)_array_of_pointer also accepts Integer elements (but not #to_ints since they share the conversion implementation with #(put|write)_pointer.


While FFI currently only mention #to_ptr in AbstractMemory, the _ToPtr?|Integer pattern is useful in user libraries’ (i.e., bindings based on FFI) RBS as well, so I’ll surface the type alias to the FFI namespace.

sig/ffi/abstract_memory.rbs Show resolved Hide resolved
Comment on lines +3 to +4
# TODO: leads to a endless recursion when used with -rrbs/test/setup
# def attach: (Module mod, String name) -> self
Copy link
Owner

@ParadoxV5 ParadoxV5 Jan 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps porting Function#attach to Ruby (similar to its cousin, VariadicInvoker#attach) and flatten the RegisterAttach private module away would solve/workaround this problem.

Did you know that even CRuby is migrating some implementations from C to Ruby?
With all the JIT improvements, Ruby code’s performance is rivaling that of C itself!

In fact, it makes sense to me if Function and VariadicInvoker share some implementations with a common ancestor module, or even (if past design decisions aren’t in the way) have VariadicInvoker inherit from Function.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened an issue regarding the stack level too deep.

sig/ffi/library.rbs Outdated Show resolved Hide resolved
For better readability.
And add some comments.
Similar to commit 5b4b406

rb_class_new_instance() automatically passes the block forward to the initialize method.
That way the block of an array function is forwarded to the Struct initializer.
Struct#initialize ignores the block, so that the code works, but it doesn't make sense.

This stood up when enabling rbs type checks, which complained about an unspecified block to Struct#initialize.
  11) MemoryPointer allows writing as a sized int
      Failure/Error: put(type, 0, value)

      RBS::Test::Tester::TypeError:
        TypeError: [FFI::AbstractMemory#put] ArgumentTypeError: expected `bot` (value) but given `1`
  11) MemoryPointer allows writing as a sized int
      Failure/Error: m.write :uint32, 1

      RBS::Test::Tester::TypeError:
        TypeError: [FFI::Pointer#write] ArgumentTypeError: expected `bot` (value) but given `1`
      # /home/lars/.rvm/gems/ruby-3.3.0/gems/rbs-3.4.1/lib/rbs/test/tester.rb:158:in `call'
      # /home/lars/.rvm/gems/ruby-3.3.0/gems/rbs-3.4.1/lib/rbs/test/observer.rb:10:in `notify'
      # /home/lars/.rvm/gems/ruby-3.3.0/gems/rbs-3.4.1/lib/rbs/test/hook.rb:178:in `ensure in write__with__RBS_TEST_eb5149_4888829b'
      # /home/lars/.rvm/gems/ruby-3.3.0/gems/rbs-3.4.1/lib/rbs/test/hook.rb:178:in `write__with__RBS_TEST_eb5149_4888829b'
      # ./spec/ffi/rbx/memory_pointer_spec.rb:65:in `block (2 levels) in <top (required)>'
      # ------------------
      # --- Caused by: ---
      # RBS::Test::Tester::TypeError:
      #   TypeError: [FFI::AbstractMemory#put] ArgumentTypeError: expected `bot` (value) but given `1`
      #   /home/lars/.rvm/gems/ruby-3.3.0/gems/rbs-3.4.1/lib/rbs/test/tester.rb:158:in `call'
@ParadoxV5 ParadoxV5 merged commit ff9d8e9 into ParadoxV5:rbs-init Jan 8, 2024
@larskanis larskanis deleted the ParadoxV5-rbs-init branch March 29, 2024 17:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants