Issue #19057 has been updated by Eregon (Benoit Daloze).
I am not sure there is an easier path here, IMO it's OK for some extensions to break
while they test against ruby-head, and to adapt to it (and we are not close to December).
It would be nicer to deprecate first, but I am not sure how feasible is that.
It seems maybe GCC & Clang might support deprecating struct members:
https://stackoverflow.com/questions/22823477/c-portable-way-to-deprecate-st…
Then we could deprecate every member of `rb_io_t`.
That seems useful, even if the warning only works on those compilers, most people would
see it.
That said, even with deprecations for one release I'm pretty sure not all extensions
using rb_io_t members will have migrated, so there will be some breakage anyway, just like
for every deprecated thing.
But if extensions didn't migrate for more than a year, then it's really on them if
they did not address it in time.
Another way could be to deprecate `GetOpenFile` (or the whole struct type if that's
possible).
But that seems difficult because many `rb_io_*` functions take a rb_io_t*, e.g., `FILE
*rb_io_stdio_file(rb_io_t *fptr);`, so we would then need to add a variant taking `VALUE
io` for all of these (with another name).
There is less value in that, because if `rb_io_t` is opaque these functions are not really
problematic, e.g. TruffleRuby could just return and cast the `io` for `GetOpenFile(io)`,
and it would require larger efforts to migrate.
The last paragraph of
https://bugs.ruby-lang.org/issues/19057#note-2 was mentioning
similar ideas about deprecation.
----------------------------------------
Feature #19057: Hide implementation of `rb_io_t`.
https://bugs.ruby-lang.org/issues/19057#change-103379
* Author: ioquatix (Samuel Williams)
* Status: Assigned
* Priority: Normal
* Assignee: ioquatix (Samuel Williams)
----------------------------------------
In order to make improvements to the IO implementation like
<https://bugs.ruby-lang.org/issues/18455>, we need to add new fields to `struct
rb_io_t`.
By the way, ending types in `_t` is not recommended by POSIX, so I'm also trying to
rename the internal implementation to drop `_t` where possible during this conversion.
Anyway, we should try to hide the implementation of `struct rb_io`. Ideally, we don't
expose any of it, but the problem is backwards compatibility.
So, in order to remain backwards compatibility, we should expose some fields of `struct
rb_io`, the most commonly used one is `fd` and `mode`, but several others are commonly
used.
There are many fields which should not be exposed because they are implementation
details.
## Current proposal
The current proposed change <https://github.com/ruby/ruby/pull/6511> creates two
structs:
```c
// include/ruby/io.h
#ifndef RB_IO_T
struct rb_io {
int fd;
// ... public fields ...
};
#else
struct rb_io;
#endif
// internal/io.h
#define RB_IO_T
struct rb_io {
int fd;
// ... public fields ...
// ... private fields ...
};
```
However, we are not 100% confident this is safe according to the C specification. My
experience is not sufficiently wide to say this is safe in practice, but it does look okay
to both myself, and @Eregon + @tenderlovemaking have both given some kind of approval.
That being said, maybe it's not safe.
There are two alternatives:
## Hide all details
We can make public `struct rb_io` completely invisible.
```c
// include/ruby/io.h
#define RB_IO_HIDDEN
struct rb_io;
int rb_ioptr_descriptor(struct rb_io *ioptr); // accessor for previously visible state.
// internal/io.h
struct rb_io {
// ... all fields ...
};
```
This would only be forwards compatible, and code would need to feature detect like this:
```c
#ifdef RB_IO_HIDDEN
#define RB_IOPTR_DESCRIPTOR rb_ioptr_descriptor
#else
#define RB_IOPTR_DESCRIPTOR(ioptr) rb_ioptr_descriptor(ioptr)
#endif
```
## Nested public interface
Alternatively, we can nest the public fields into the private struct:
```c
// include/ruby/io.h
struct rb_io_public {
int fd;
// ... public fields ...
};
// internal/io.h
#define RB_IO_T
struct rb_io {
struct rb_io_public public;
// ... private fields ...
};
```
## Considerations
I personally think the "Hide all details" implementation is the best, but
it's also the lest compatible. This is also what we are ultimately aiming for, whether
we decide to take an intermediate "compatibility step" is up to us.
I think "Nested public interface" is messy and introduces more complexity, but
it might be slightly better defined than the "Current proposal" which might
create undefined behaviour. That being said, all the tests are passing.
--
https://bugs.ruby-lang.org/