[foreign] RFR 8218052: Don't throw an exception when encountering a type with a flexible array member

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Sat Feb 16 00:29:53 UTC 2019


On 16/02/2019 00:00, Henry Jen wrote:
> Now to the real discussion about incomplete array support, instead of undefined layout, I prefer to have limited support, we can either strip that field or generate a 0-length array for now. For jextract, C only allow such field at end of struct, and sizeof() operator simply ignore that trailing array field. This should give us a good match as first step.
Agree
>
> Move on to more general support, where incomplete array can be in-between layouts. Before that, we probably need to validate some assumption,
>
> Any incomplete array must have length specified in the same struct before the incomplete array. I believe this will pretty much cover most cases if not all.

To reinforce this point, I believe most compilers even give error if the 
incomplete array is the only member of the struct, or if it's followed 
by other stuff:

$ cat testInc.h

struct A {
    int arr[];
};

struct B {
    int l;
    int arr[];
};

struct C {
    int l;
    int arr[];
    int l2;
};

$ gcc -c testInc.h

testInc.h:2:8: error: flexible array member in a struct with no named 
members
     int arr[];
         ^~~
testInc.h:12:8: error: flexible array member not at end of struct
     int arr[];
         ^~~

$ clang -c testInc.h
testInc.h:2:8: error: flexible array member 'arr' not allowed in 
otherwise empty
       struct
    int arr[];
        ^
testInc.h:12:8: error: flexible array member 'arr' with type 'int []' is 
not at
       the end of struct
    int arr[];
        ^
testInc.h:13:8: note: next field declaration is here
    int l2;
        ^
2 errors generated.

>
> With that, I think following should work well enough,

What you describe is what I've dubbed 'dependent layout' approach - e.g. 
have one or more values in a struct provide more info for certain layout 
elements in same struct. This is fairly frequent business with message 
protocols - almost all representation for variable-sized data is 
expressed as length + data array (sometimes compressed, as in protobuf's 
VarInt).

I agree that's where we need to land, longer term. Short term it feels 
like the best move would be to just strip the array. Creating a 0-length 
array might be a  move with subtle consequences: the array occurs within 
a region with some boundaries (e.g. a struct region). The boundaries of 
the enclosing region are usually computed using sizeof(enclosing type). 
Meaning that the enclosing region won't be 'big enough' to host anything 
but a zero-length array. If we cast the array to something else, what we 
get back is an array whose boundaries would exceed those of the 
enclosing struct - so if you try e.g. to write to the array, the 
operation would fail.

To do this stuff properly in Panama you would need to allocate a bigger 
chunk of memory, of the desired size (pretty much as you would in C), 
and then cast the memory to the struct type - now you have a memory 
region that is big enough to do the struct + the array.

The alternative, which does look simpler, is to just allocate a struct 
(with array stripped) followed by an array of desired size in the same 
scope - e.g. compare this:

try (Scope s : Scope.globalScope().fork()) {

     Pointer<Byte> slab = s.allocateArray(NativeTypes.UINT8, 
Struct.sizeOf(StructWithIncompleteArray.class) + 40);
     Pointer<StructWithIncompleteArray> pstruct = 
slab.cast(LayoutType.ofStruct(StructWithIncompleteArray.class));
     Array<Integer> data = 
pstruct.data$get().cast(NativeTypes.INT32.array(10));

}

With this:

try (Scope s : Scope.globalScope().fork()) {

     StructWithoutIncompleteArray struct = 
s.allocateStruct(StructWithoutIncompleteArray.class);
     Array<Integer> data = s.allocateArray(NativeTypes.INT32, 10);

}


The code looks similar - and a client can, after the allocation, use the 
array and the struct at will. But there's a subtle difference between 
the two: in the first snippet, the array is allocated immediately after 
the struct bits - that's how allocation happened. In the second snippet 
there's no guarantee that the array will be after the struct; in fact, 
the native scope might have run out of space with the struct and needed 
to allocate a new slab of native memory via unsafe before the array 
allocation happens.


Which seems to suggest that the right way of approaching the problem, 
even if more verbose, is the first one.

Maurizio



More information about the panama-dev mailing list