Bindgen fails to detect cyclic types
kornilova203 opened this issue · comments
struct b;
struct c;
struct a {
struct b *bb;
};
struct b {
struct c *cc;
};
struct c {
struct a *aa;
};
Produces following Scala code without any warnings:
type struct_b = native.CStruct1[native.Ptr[struct_c]]
type struct_a = native.CStruct1[native.Ptr[struct_b]]
type struct_c = native.CStruct1[native.Ptr[struct_a]]
Problem is in CycleDetection::isCyclic
it checks only if a type references itself.
Field that causes recursion is changed to pointer to CArray
only in the case when struct references itself struct node { struct node *next };
It happens in TypeTranslator
, but TypeTranslator
cannot handle more difficult cases as the one above because while translating struct a
it cannot know that struct a -> struct b -> struct c -> struct a
cycle exist (it may only detect cycle in struct c
when all types are known).
Cycles should be fixed after all types are generated. Before generating Scala code Structs and Unions should check whether their fields cause cycle, if so a field should be replace with a pointer to Byte
.
But on the other hand it is okay to break cycle in only one place, so there is no need to detect cycle in all 3 structures.
Original type of replaced type should be remembered to generate proper helper methods.
Following issues should be considered:
CycleDetection
adds dependency only for value types and pointer types.
Field may be of any type (for example function pointer), soCycleDetection
should "unroll" all types. I suppose it will be reasonable to modifyType::usesType
method such that it stops if cycle is found. Then the method may be used inStruct
andUnion
to detect cyclic fields.CycleDetection
then may be removed.- We should "brake" cycle on a pointer type filed because only pointer type field may be replaced with pointer to
Byte
.
So in this case:field ofstruct b; struct a { struct b *sb; }; struct b { struct a sa; };
struct a
should be replace bynative.Ptr[Byte]
.
Note: pointer type field always exists because otherwise structs will have never-ending recursion.
Case with function pointers is pretty complicated because there might not be a pointer where cycle should be broken:
struct a;
struct b {
int (*func)(struct a);
};
struct a {
struct b sb;
};
There are two possible solutions:
- Change type of function pointer to
native.CFunctionPtr1[native.CArray[...], native.CInt]
, so it usesCArray
instead of struct type. - Change type of field to
CArray
Seems like Scala Native supports casting function pointers and it should not break anything:
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer.