-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[ffi] Use inline classes for pointer arithmetic? #52320
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
Comments
It should certainly be possible to add some static type checks on the use of There is no way to emulate inline classes in the case where it is used to get additional static checks. However, you could migrate to an approach using inline classes by introducing That's the only notion of a migration path that I can think of here. There is one interesting twist: The description of the operator // Using primary constructors for conciseness. That feature
// has not yet been accepted by the language team.
inline class AddressDistance(int value) {}
inline class Offset(int value) implements AddressDistance {}
inline class Size(int value) implements AddressDistance {}
inline class Address(int value) {
Address operator +(AddressDistance distance) => Address(value + distance.value);
} |
We have no clue how much code is out there, and no way to signal it using typedefs. Should we consider using language versioning on this like with class modifiers for the standard libraries? |
How is this currently done? |
Just |
Just brainstorming - If we were to provide the ability to make inline classes assignable to/from their representation types, you could potentially change things over to be open inline classes, which would be less (but still at least somewhat) breaking (anywhere anyone is currently assigning to an Address to an int would continue to work). You could then have a long-term deprecation period after which you close the inline classes and make them abstract (possibly you could add some custom warnings in the interim to nudge people over). In general, migration of core libraries is hard. |
That sounds like a plausible migration path. I'm assuming people using If |
I don't know, I think that's more of a custom compiler thing, right?
There's no built in support for this, I think you'd have to look into a custom analyzer warning or hint. Note that we have not committed to (or even really come up with a design for) the ability to have both open and closed inline classes yet, but it's a thing we've discussed. cc @sigmundch |
If we do support the subtyping relation inline class Address(int value) implements int {
...
}
void main() {
Address a = Address(7658765);
int i = a; // OK, `Address <: int`.
} If we support assignability for the opposite conversion using implicit constructors then it could be something like this: // Library 'ffi_something.dart'.
static extension AddressExtension on Address {
// Make the `Address` constructor available implicitly.
implicit factory Address.fromInt(int value) = Address.new;
}
// Library 'main.dart'.
import 'ffi_something.dart';
void main() {
Address a = 7658765; // OK, because the implicit constructor is imported.
} This means that it's easy to exit the abstraction ( It could be interesting to hear if you consider this profile ("out is easy, in is a bit harder") to be a good fit. Of course, if "out is easy" is only used during the migration period then |
This is an interesting use case. We came across something similar. Recently @srujzs, @joshualitt, and I were discussing what to do with a set of marker interface types in |
Yeah, there is some overlap here. We've talked about assignability before, but I don't think we've talked about this case with Leaf and Erik before. For context, we've been using We're using this prototype to implement JS types (like IIUC, the difference is that we're trying to change the implementation, but keep the name the same (e.g. @eernstg Naturally, the Cool to see we have some similar goals as dart:ffi! |
@srujzs wrote:
I think I should mention that those properties are design parameters rather than unavoidable consequences of the overall approach. So we could just remove or change those restrictions, if desired. For example, we could allow an Similarly, we could specify that an But the current proposals (the accepted one for the core of inline classes plus some not-yet-landed proposals, including the one about implicit construction) don't do that. Here's a bit of rationale for the two properties you mention: As proposed, implicit constructors can only be declared in a
Wrt the subtype relationships, an inline type is unrelated to its representation type by default. So with an inline class In general, these rules and defaults tend to make inline classes a bit less convenient, but, arguably, a bit safer, yielding code which is a bit easier to read and understand and maintain. |
Couple of comments:
All in all, I think having to explicitly import the extensions is a small price to pay. |
Any kind of class. In fact, it can be allowed on any declaration that introduces an interface type. For instance, we could use this feature to add a factory constructor to a mixin: As long as the constructor returns an object whose type implements that mixin, we're good. Being
... then you might do this: static extension E1 on JSString {
implicit JSString.fromString(String s) => ...;
}
static extension E2 on String {
implicit String.fromJSString(JSString jsString) => ...;
}
Cf. dart-lang/language#723, with 597 thumbs up and 3 down. 😄 |
We could consider using inline classes for pointer arithmetic in
dart:ffi
.sizeOf<IntPtr>()
could return aSize
.Pointer.address
could return anAddress
.Size
can be multiplied with integers and returns aSize
.Address
andSize
can be added which results in anAddress
.Offset
Offset
can be added to anAddress
resulting in aAddress
.Pointer.fromAddress
takes anAddress
.However, this would be a breaking change.
@leafpetersen do we have a migration path to using inline classes?
The text was updated successfully, but these errors were encountered: