Open dcharkes opened 2 years ago
Wish list. Don't write dart types
I have been thinking about this and discussing this with @leafpetersen couple of times. My original thinking was that we could use views for this, though I don't think the current version of views has all necessary features.
If CFE retained typedef reference in the AST rather than performed full substitution then we could simply write:
// dart:ffi
typedef Void = void;
// ...
typedef Int32 = int;
typedef Int64 = int;
typedef Float = double;
typedef Double = double;
// ...
@Native()
external Int64 sum(Int64 a, Int64 b);
@Native()
external Void foo(Pointer<Int64> a, Int64 b);
Though unfortunately right now CFE is going the opposite way (/cc @johnniwinther @chloestefantsova) and is considering to delete TypedefType
from the public AST. This node is not used for non-function type aliases anyway.
@mraleph Java Native Access works with reflections to get the return and parameter types. Considering static metaprogramming is a replacement for dart:mirrors, and the equivalent of reflections in Dart is dart:mirrors, I believe static metaprogramming would be the most correct feature to help solve this problem.
@Wdestroier I don't think static metaprogramming is going to help us much here. It can't change signatures of the existing methods it can only generate new methods. The problem we are facing here does not really exist in Java - as Java has the full zoo of numeric types. In Dart however there is just int
and double
.
@mraleph is the primary shortcoming you see with views the inability to use literals (or in general values) of the representation type as initializers? Or are there other places it falls short?
@leafpetersen I think if we had a way to declare conversions both ways as implicit (both to and from representation type) & CFE preserved view type, rather than fully erasing it, then I think views would do the job.
Essentially we want the following to be a valid code:
@Native()
external Int64 sum(Int64 a, Int64 b);
int useSum(List<int> v) {
return v.reduce((a, b) => sum(a, b));
// Maybe even the following, but probably okay if this does not type check.
// return v.reduce(sum);
}
In other words, we don't really need much of advanced view functionality - what we want is a typedef
that survives in the AST representation until backend.
Similar to allowing the overload of implicit and explicit is
and as
operators...
@leafpetersen I think if we had a way to declare conversions both ways as implicit (both to and from representation type) & CFE preserved view type, rather than fully erasing it, then I think views would do the job.
Essentially we want the following to be a valid code:
@Native() external Int64 sum(Int64 a, Int64 b); int useSum(List<int> v) { return v.reduce((a, b) => sum(a, b)); // Maybe even the following, but probably okay if this does not type check. // return v.reduce(sum); }
In other words, we don't really need much of advanced view functionality - what we want is a
typedef
that survives in the AST representation until backend.
This is not really the full picture. We also use the native types (currently subtypes of NativeType
) for other types of type checks. For example it is now allowed to assign a Pointer<Int8>
to a Pointer<Int16>
. If we were using typedef
s, these assignments would not be flagged anymore by the type system.
Moreover, we'd have to change Pointer
's type argument bound to be Object
. Unless views can implement
another type. Then we could let those views implement NativeType
.
(Side note: we want to stop reifying the type argument of Pointer at runtime (https://github.com/dart-lang/sdk/issues/49935). However, we want to keep all static checks that we currently get from using native types as type arguments.)
@mraleph
declare conversions both ways as implicit (both to and from representation type)
My general take is that if we want to support this, it should probably be a separate first class feature that can be used for any type.
& CFE preserved view type, rather than fully erasing it
If I understand correctly, this is more of an implementation detail around how the CFE represents these?
@dcharkes
- Do view disable assignment to other views on the same underlying type?
Yes.
- Can views implement another type?
Views can be made subtypes of other view types. I'm not sure if that covers your use case or not. We have considered allowing views to implement class types in the case that the underlying representation type also implements that class type. That is currently not in the proposal though.
- Rename FfiNative to Native
We need to add @Native
and deprecate @FfiNative
so that we don't break dart:ui
in Flutter on the roll. Then after a roll, we can migrate dart:ui
and then remove @FfiNative
.
If I'm going to modify the API, I might as well take the asset
s into account as well. I've created https://github.com/dart-lang/sdk/issues/49803#issuecomment-1283716761 with an API design (1) and (2).
View types will be part of the AST since we need them to do static reasoning on precompiled code. The implementation will be similar to the current implementation of the experimental ExtensionType
:
class ViewType extends DartType {
final View view;
final List<DartType> typeArguments;
final DartType representationType;
...
}
where representationType
is the type used at runtime. For instance for
view class View<T>(T it) {}
View<int> method() => ...
the representation type of View<int>
will by int
.
Our current syntax for "ffi-natives" is still a bit verbose.
1. Rename
FfiNative
toNative
We could consider
@Ffi<...>
and@Native<...>
.sum
is an external function.sum
is a native function.sum
is not an "ffi" function, the mechanism for callingsum
is "ffi".This makes it most natural to change the syntax to:
Courtesy of @mit-mit and @mkustermann.
2. Make the symbol optional
If the symbol in C is identical to the Dart symbol, we don't need to repeat it.
Wish list. Don't write dart types
The native types uniquely define the Dart types, so it would be nice if we could omit the Dart types
However, we would need some kind of transformation before any Dart static analysis runs to replace the native types with the corresponding Dart code. Otherwise, calling a function with Dart integers will not pass type checks.
We would likely need macros for this, if our approach for macros could already support this.
Misc
The old natives are deprecated, so we don't have to worry about naming collision (having to designate the new natives as "new native" or "ffi native"). FWIW the syntax of the deprecated old natives:
See d8d7af15ce9e4eb78c8aac0dfa46413036747bb7.