@@ -819,6 +819,111 @@ fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNa
819
819
}
820
820
}
821
821
822
+ /// Build a getter function for a flexible array member at the end of a C struct
823
+ /// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer
824
+ /// to the flexible array with the correct const and volatile qualifiers
825
+ fn buildFlexibleArrayFn (
826
+ c : * Context ,
827
+ scope : * Scope ,
828
+ layout : * const clang.ASTRecordLayout ,
829
+ field_name : []const u8 ,
830
+ field_decl : * const clang.FieldDecl ,
831
+ ) TypeError ! Node {
832
+ const field_qt = field_decl .getType ();
833
+
834
+ const u8_type = try Tag .type .create (c .arena , "u8" );
835
+ const self_param_name = "self" ;
836
+ const self_param = try Tag .identifier .create (c .arena , self_param_name );
837
+ const self_type = try Tag .typeof .create (c .arena , self_param );
838
+
839
+ const fn_params = try c .arena .alloc (ast .Payload .Param , 1 );
840
+
841
+ fn_params [0 ] = .{
842
+ .name = self_param_name ,
843
+ .type = Tag .@"anytype" .init (),
844
+ .is_noalias = false ,
845
+ };
846
+
847
+ const array_type = @ptrCast (* const clang .ArrayType , field_qt .getTypePtr ());
848
+ const element_qt = array_type .getElementType ();
849
+ const element_type = try transQualType (c , scope , element_qt , field_decl .getLocation ());
850
+
851
+ var block_scope = try Scope .Block .init (c , scope , false );
852
+ defer block_scope .deinit ();
853
+
854
+ const intermediate_type_name = try block_scope .makeMangledName (c , "Intermediate" );
855
+ const intermediate_type = try Tag .std_meta_flexible_array_type .create (c .arena , .{ .lhs = self_type , .rhs = u8_type });
856
+ const intermediate_type_decl = try Tag .var_simple .create (c .arena , .{
857
+ .name = intermediate_type_name ,
858
+ .init = intermediate_type ,
859
+ });
860
+ try block_scope .statements .append (intermediate_type_decl );
861
+ const intermediate_type_ident = try Tag .identifier .create (c .arena , intermediate_type_name );
862
+
863
+ const return_type_name = try block_scope .makeMangledName (c , "ReturnType" );
864
+ const return_type = try Tag .std_meta_flexible_array_type .create (c .arena , .{ .lhs = self_type , .rhs = element_type });
865
+ const return_type_decl = try Tag .var_simple .create (c .arena , .{
866
+ .name = return_type_name ,
867
+ .init = return_type ,
868
+ });
869
+ try block_scope .statements .append (return_type_decl );
870
+ const return_type_ident = try Tag .identifier .create (c .arena , return_type_name );
871
+
872
+ const field_index = field_decl .getFieldIndex ();
873
+ const bit_offset = layout .getFieldOffset (field_index ); // this is a target-specific constant based on the struct layout
874
+ const byte_offset = bit_offset / 8 ;
875
+
876
+ const casted_self = try Tag .ptr_cast .create (c .arena , .{
877
+ .lhs = intermediate_type_ident ,
878
+ .rhs = self_param ,
879
+ });
880
+ const field_offset = try transCreateNodeNumber (c , byte_offset , .int );
881
+ const field_ptr = try Tag .add .create (c .arena , .{ .lhs = casted_self , .rhs = field_offset });
882
+
883
+ const alignment = try Tag .alignof .create (c .arena , element_type );
884
+
885
+ const ptr_val = try Tag .align_cast .create (c .arena , .{ .lhs = alignment , .rhs = field_ptr });
886
+ const ptr_cast = try Tag .ptr_cast .create (c .arena , .{ .lhs = return_type_ident , .rhs = ptr_val });
887
+ const return_stmt = try Tag .@"return" .create (c .arena , ptr_cast );
888
+ try block_scope .statements .append (return_stmt );
889
+
890
+ const payload = try c .arena .create (ast .Payload .Func );
891
+ payload .* = .{
892
+ .base = .{ .tag = .func },
893
+ .data = .{
894
+ .is_pub = true ,
895
+ .is_extern = false ,
896
+ .is_export = false ,
897
+ .is_var_args = false ,
898
+ .name = field_name ,
899
+ .linksection_string = null ,
900
+ .explicit_callconv = null ,
901
+ .params = fn_params ,
902
+ .return_type = return_type ,
903
+ .body = try block_scope .complete (c ),
904
+ .alignment = null ,
905
+ },
906
+ };
907
+ return Node .initPayload (& payload .base );
908
+ }
909
+
910
+ fn isFlexibleArrayFieldDecl (c : * Context , field_decl : * const clang.FieldDecl ) bool {
911
+ return qualTypeCanon (field_decl .getType ()).isIncompleteOrZeroLengthArrayType (c .clang_context );
912
+ }
913
+
914
+ /// clang's RecordDecl::hasFlexibleArrayMember is not suitable for determining
915
+ /// this because it returns false for a record that ends with a zero-length
916
+ /// array, but we consider those to be flexible arrays
917
+ fn hasFlexibleArrayField (c : * Context , record_def : * const clang.RecordDecl ) bool {
918
+ var it = record_def .field_begin ();
919
+ const end_it = record_def .field_end ();
920
+ while (it .neq (end_it )) : (it = it .next ()) {
921
+ const field_decl = it .deref ();
922
+ if (isFlexibleArrayFieldDecl (c , field_decl )) return true ;
923
+ }
924
+ return false ;
925
+ }
926
+
822
927
fn transRecordDecl (c : * Context , scope : * Scope , record_decl : * const clang.RecordDecl ) Error ! void {
823
928
if (c .decl_table .get (@ptrToInt (record_decl .getCanonicalDecl ()))) | name |
824
929
return ; // Avoid processing this decl twice
@@ -868,9 +973,16 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
868
973
var fields = std .ArrayList (ast .Payload .Record .Field ).init (c .gpa );
869
974
defer fields .deinit ();
870
975
976
+ var functions = std .ArrayList (Node ).init (c .gpa );
977
+ defer functions .deinit ();
978
+
979
+ const has_flexible_array = hasFlexibleArrayField (c , record_def );
871
980
var unnamed_field_count : u32 = 0 ;
872
981
var it = record_def .field_begin ();
873
982
const end_it = record_def .field_end ();
983
+ const layout = record_def .getASTRecordLayout (c .clang_context );
984
+ const record_alignment = layout .getAlignment ();
985
+
874
986
while (it .neq (end_it )) : (it = it .next ()) {
875
987
const field_decl = it .deref ();
876
988
const field_loc = field_decl .getLocation ();
@@ -882,12 +994,6 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
882
994
break :blk Tag .opaque_literal .init ();
883
995
}
884
996
885
- if (qualTypeCanon (field_qt ).isIncompleteOrZeroLengthArrayType (c .clang_context )) {
886
- try c .opaque_demotes .put (c .gpa , @ptrToInt (record_decl .getCanonicalDecl ()), {});
887
- try warn (c , scope , field_loc , "{s} demoted to opaque type - has variable length array" , .{container_kind_name });
888
- break :blk Tag .opaque_literal .init ();
889
- }
890
-
891
997
var is_anon = false ;
892
998
var field_name = try c .str (@ptrCast (* const clang .NamedDecl , field_decl ).getName_bytes_begin ());
893
999
if (field_decl .isAnonymousStructOrUnion () or field_name .len == 0 ) {
@@ -896,6 +1002,18 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
896
1002
unnamed_field_count += 1 ;
897
1003
is_anon = true ;
898
1004
}
1005
+ if (isFlexibleArrayFieldDecl (c , field_decl )) {
1006
+ const flexible_array_fn = buildFlexibleArrayFn (c , scope , layout , field_name , field_decl ) catch | err | switch (err ) {
1007
+ error .UnsupportedType = > {
1008
+ try c .opaque_demotes .put (c .gpa , @ptrToInt (record_decl .getCanonicalDecl ()), {});
1009
+ try warn (c , scope , record_loc , "{s} demoted to opaque type - unable to translate type of flexible array field {s}" , .{ container_kind_name , field_name });
1010
+ break :blk Tag .opaque_literal .init ();
1011
+ },
1012
+ else = > | e | return e ,
1013
+ };
1014
+ try functions .append (flexible_array_fn );
1015
+ continue ;
1016
+ }
899
1017
const field_type = transQualType (c , scope , field_qt , field_loc ) catch | err | switch (err ) {
900
1018
error .UnsupportedType = > {
901
1019
try c .opaque_demotes .put (c .gpa , @ptrToInt (record_decl .getCanonicalDecl ()), {});
@@ -905,7 +1023,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
905
1023
else = > | e | return e ,
906
1024
};
907
1025
908
- const alignment = zigAlignment (field_decl .getAlignedAttribute (c .clang_context ));
1026
+ const alignment = if (has_flexible_array and field_decl .getFieldIndex () == 0 )
1027
+ @intCast (c_uint , record_alignment )
1028
+ else
1029
+ zigAlignment (field_decl .getAlignedAttribute (c .clang_context ));
909
1030
910
1031
if (is_anon ) {
911
1032
try c .decl_table .putNoClobber (c .gpa , @ptrToInt (field_decl .getCanonicalDecl ()), field_name );
@@ -924,6 +1045,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
924
1045
.data = .{
925
1046
.is_packed = is_packed ,
926
1047
.fields = try c .arena .dupe (ast .Payload .Record .Field , fields .items ),
1048
+ .functions = try c .arena .dupe (Node , functions .items ),
927
1049
},
928
1050
};
929
1051
break :blk Node .initPayload (& record_payload .base );
@@ -1737,12 +1859,12 @@ fn transImplicitCastExpr(
1737
1859
return maybeSuppressResult (c , scope , result_used , sub_expr_node );
1738
1860
},
1739
1861
.ArrayToPointerDecay = > {
1740
- if ( exprIsNarrowStringLiteral ( sub_expr )) {
1741
- const sub_expr_node = try transExpr (c , scope , sub_expr , .used );
1862
+ const sub_expr_node = try transExpr ( c , scope , sub_expr , .used );
1863
+ if ( exprIsNarrowStringLiteral ( sub_expr ) or exprIsFlexibleArrayRef (c , sub_expr )) {
1742
1864
return maybeSuppressResult (c , scope , result_used , sub_expr_node );
1743
1865
}
1744
1866
1745
- const addr = try Tag .address_of .create (c .arena , try transExpr ( c , scope , sub_expr , .used ) );
1867
+ const addr = try Tag .address_of .create (c .arena , sub_expr_node );
1746
1868
const casted = try transCPtrCast (c , scope , expr .getBeginLoc (), dest_type , src_type , addr );
1747
1869
return maybeSuppressResult (c , scope , result_used , casted );
1748
1870
},
@@ -1852,6 +1974,19 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
1852
1974
}
1853
1975
}
1854
1976
1977
+ fn exprIsFlexibleArrayRef (c : * Context , expr : * const clang.Expr ) bool {
1978
+ if (expr .getStmtClass () == .MemberExprClass ) {
1979
+ const member_expr = @ptrCast (* const clang .MemberExpr , expr );
1980
+ const member_decl = member_expr .getMemberDecl ();
1981
+ const decl_kind = @ptrCast (* const clang .Decl , member_decl ).getKind ();
1982
+ if (decl_kind == .Field ) {
1983
+ const field_decl = @ptrCast (* const clang .FieldDecl , member_decl );
1984
+ return isFlexibleArrayFieldDecl (c , field_decl );
1985
+ }
1986
+ }
1987
+ return false ;
1988
+ }
1989
+
1855
1990
fn isBoolRes (res : Node ) bool {
1856
1991
switch (res .tag ()) {
1857
1992
.@"or" ,
@@ -3056,7 +3191,6 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used:
3056
3191
3057
3192
fn transMemberExpr (c : * Context , scope : * Scope , stmt : * const clang.MemberExpr , result_used : ResultUsed ) TransError ! Node {
3058
3193
var container_node = try transExpr (c , scope , stmt .getBase (), .used );
3059
-
3060
3194
if (stmt .isArrow ()) {
3061
3195
container_node = try Tag .deref .create (c .arena , container_node );
3062
3196
}
@@ -3076,7 +3210,11 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re
3076
3210
const decl = @ptrCast (* const clang .NamedDecl , member_decl );
3077
3211
break :blk try c .str (decl .getName_bytes_begin ());
3078
3212
};
3079
- const node = try Tag .field_access .create (c .arena , .{ .lhs = container_node , .field_name = name });
3213
+
3214
+ var node = try Tag .field_access .create (c .arena , .{ .lhs = container_node , .field_name = name });
3215
+ if (exprIsFlexibleArrayRef (c , @ptrCast (* const clang .Expr , stmt ))) {
3216
+ node = try Tag .call .create (c .arena , .{ .lhs = node , .args = &.{} });
3217
+ }
3080
3218
return maybeSuppressResult (c , scope , result_used , node );
3081
3219
}
3082
3220
0 commit comments