Open sjindel-google opened 5 years ago
For reference, all the error messages we throw in our VM transformer:
FfiTypeMismatch:
# Used by dart:ffi
template: "Expected type '#type' to be '#type2', which is the Dart type corresponding to '#type3'."
external: test/ffi_test.dart
FfiTypeInvalid:
# Used by dart:ffi
template: "Expected type '#type' to be a valid and instantiated subtype of 'NativeType'."
external: test/ffi_test.dart
FfiTypeUnsized:
# Used by dart:ffi
template: "Method '#name' cannot be called on something of type '#type' as this type is unsized."
external: test/ffi_test.dart
FfiFieldAnnotation:
# Used by dart:ffi
template: "Field '#name' requires exactly one annotation to declare its C++ type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields."
external: test/ffi_test.dart
FfiNotStatic:
# Used by dart:ffi
template: "#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from c."
external: test/ffi_test.dart
FfiFieldInitializer:
# Used by dart:ffi
template: "Field '#name' is a dart:ffi Pointer to a struct field and therefore cannot be initialized before constructor execution."
external: test/ffi_test.dart
FfiExtendsSealedClass:
# Used by dart:ffi
template: "Class '#name' cannot be extended."
external: test/ffi_test.dart
FfiStructGeneric:
# Used by dart:ffi
template: "Struct '#name' should not be generic."
external: test/ffi_test.dart
FfiWrongStructInheritance:
# Used by dart:ffi
template: "Struct '#name' must inherit from 'Struct<#name>'."
external: test/ffi_test.dart
For every one of these being able to express them in the type system would remove a ffi-specific check. Edit: Or rather a feature that would prevent us having to check this manually.
We now have sealed and final classes.
We don't have virtual statics, and have no plan to add such.
Most of the other features mentioned here are ones we wouldn't consider adding for any other reason than to support dart:ffi
, which seems to be in the perfect position to give those errors itself (after all, nobody else can compile code importing dart:ffi
).
I don't see a general use for requiring type arguments to be constant (outside of constant invocations), like I also don't see a general use for requiring actual arguments to be constant expressions. It's generally something that is suggested in order to achieve something else, but rarely actually achieves that in practice.
The one thing you don't get is warnings from the analyzer, unless the analyzer cooperates and recognized the VM requirements (maybe by using annotations like @pragma('vm:must_be_constant')
).
Also, a part of the problems comes from using Dart function types to represent native function types. Maybe extension types will be a way to go in the future:
extension type Int32(int _) implements int {}
This creates a subtype of int
, which means that Int32 Function(int32, int32)
is a subtype of int Function(Int32, Int32)
, so you can see that it returns int
. Still won't allow you to abstract over parameter lists. We'd have to allow something like int Function(...)
to be a supertype of all (non-generic) functions returning an int
. (That one might actually be occasionally useful, but nobody will look for such a request in this issue.)
This is a meta-issue for the language features which would simplify the FFI API.
/cc @dcharkes @mkustermann @mraleph @leafpetersen @lrhn
Type-level correspondences
The FFI has as set of types which correspond to C types:
Int8
,Int64
,Double
,Float
,Pointer
, etc. Each of these types has a related Dart type, e.g.int
,double
orPointer
. These types cannot be instantiated in Dart (exception as annotations), but are used as type variables and in function signatures to specify additional behavior. For example, thePointer
class has two methodsload
andstore
:Unfortunately, this API can't enforce the relationship between the
NativeType
s and correspondingDart
types (this is done via a custom pass in the front-end). So the following illegal code will type-check:Access to function return type
Enforcing the correspondence between these two sets of types is further complicated by the use of function types to represent native function signatures. The following API method creates a native function address which calls a Dart method:
Here, we want to enforce that the type of
exceptionalReturn
corresponds to the return type off
, and that all the types mentioned in the signature off
correspond piece-wise to the native types mentioned in the function typeT
.The same holds for
asFunction
, although it's slightly simpler.Constant type arguments
We require that arguments to certain API functions are compile-time constants to ensure that we can compile all the required trampolines in AOT. For example:
In this case we need to ensure that at any call-site:
p.asFunction<R>
, bothR
and the receiver typeT
need to be known at compile-time. The same applies tofromFunction
.Sealed classes and virtual statics
We need to seal some classes including
NativeType
and all its children (Int8
,Double
, etc.) exceptStruct
. In addition, we need to allow classes to extendStruct
but not implementStruct
(we only generate code for classes which extendStruct
and the VM relies on having the generated code for any subtype ofStruct
).Moreover, we need to prohibit extending a class which extends
Struct
(because we cannot generate meaningful field offsets), even thoughStruct
itself can be extended.Part of the reason we need to seal the hierarchy this way is that we cannot express the all the operations the VM needs to perform on one of these types in its signature. For example, the VM needs to manufacture Dart objects corresponding to a native-type in the return value of a native method or the argument to a callback:
If we had virtual statics, we could express the needed functionality in the interface of
NativeFunction
:then:
The same goes for structs. Currently we disallow implementing
Struct
because we generate code like this:Gets elaborated to:
We prohibit implementing
Struct
because the VM needs to lookup the#sizeOf
and#fromPointer
members for any class which is a subtype ofStruct
, and we only generate them in classes which extendStruct
. If we had virtual statics, we could express these in the interface:@dcharkes Are there others?