scala-native / scala-native-bindgen

Scala Native Binding Generator

Home Page:https://scala-native.github.io/scala-native-bindgen/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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), so CycleDetection should "unroll" all types. I suppose it will be reasonable to modify Type::usesType method such that it stops if cycle is found. Then the method may be used in Struct and Union 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:
    struct b;
    
    struct a {
         struct b *sb;
    };
    struct b {
         struct a sa;
    };
    field of struct a should be replace by native.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 uses CArray 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.