authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-05-21 16:32:53-07:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-06-11 21:31:39+03:00
log45212e3b33151b016ae4a597a898db0cc13d4e6b
treea172cfd2cd706a53d9ccb89c3df8ceecdc92bd1f
parentd8b133d7339605d3cda75459bf986472a72f5bd3

translate-c: Implement flexible arrays

Fixes #8759

8 files changed, 283 insertions(+), 21 deletions(-)

lib/std/meta.zig+31
...@@ -1355,3 +1355,34 @@ test "isError" {...@@ -1355,3 +1355,34 @@ test "isError" {
1355 try std.testing.expect(isError(math.absInt(@as(i8, -128))));1355 try std.testing.expect(isError(math.absInt(@as(i8, -128))));
1356 try std.testing.expect(!isError(math.absInt(@as(i8, -127))));1356 try std.testing.expect(!isError(math.absInt(@as(i8, -127))));
1357}1357}
1358
1359/// This function is for translate-c and is not intended for general use.
1360/// Constructs a [*c] pointer with the const and volatile annotations
1361/// from SelfType for pointing to a C flexible array of ElementType.
1362pub fn FlexibleArrayType(comptime SelfType: type, ElementType: type) type {
1363 switch (@typeInfo(SelfType)) {
1364 .Pointer => |ptr| {
1365 return @Type(TypeInfo{ .Pointer = .{
1366 .size = .C,
1367 .is_const = ptr.is_const,
1368 .is_volatile = ptr.is_volatile,
1369 .alignment = @alignOf(ElementType),
1370 .child = ElementType,
1371 .is_allowzero = true,
1372 .sentinel = null,
1373 } });
1374 },
1375 else => |info| @compileError("Invalid self type \"" ++ @tagName(info) ++ "\" for flexible array getter: " ++ @typeName(SelfType)),
1376 }
1377}
1378
1379test "Flexible Array Type" {
1380 const Container = extern struct {
1381 size: usize,
1382 };
1383
1384 try testing.expectEqual(FlexibleArrayType(*Container, c_int), [*c]c_int);
1385 try testing.expectEqual(FlexibleArrayType(*const Container, c_int), [*c]const c_int);
1386 try testing.expectEqual(FlexibleArrayType(*volatile Container, c_int), [*c]volatile c_int);
1387 try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);
1388}
src/clang.zig+14
...@@ -183,6 +183,14 @@ pub const ArrayType = opaque {...@@ -183,6 +183,14 @@ pub const ArrayType = opaque {
183 extern fn ZigClangArrayType_getElementType(*const ArrayType) QualType;183 extern fn ZigClangArrayType_getElementType(*const ArrayType) QualType;
184};184};
185185
186pub const ASTRecordLayout = opaque {
187 pub const getFieldOffset = ZigClangASTRecordLayout_getFieldOffset;
188 extern fn ZigClangASTRecordLayout_getFieldOffset(*const ASTRecordLayout, c_uint) u64;
189
190 pub const getAlignment = ZigClangASTRecordLayout_getAlignment;
191 extern fn ZigClangASTRecordLayout_getAlignment(*const ASTRecordLayout) i64;
192};
193
186pub const AttributedType = opaque {194pub const AttributedType = opaque {
187 pub const getEquivalentType = ZigClangAttributedType_getEquivalentType;195 pub const getEquivalentType = ZigClangAttributedType_getEquivalentType;
188 extern fn ZigClangAttributedType_getEquivalentType(*const AttributedType) QualType;196 extern fn ZigClangAttributedType_getEquivalentType(*const AttributedType) QualType;
...@@ -461,6 +469,9 @@ pub const FieldDecl = opaque {...@@ -461,6 +469,9 @@ pub const FieldDecl = opaque {
461469
462 pub const getParent = ZigClangFieldDecl_getParent;470 pub const getParent = ZigClangFieldDecl_getParent;
463 extern fn ZigClangFieldDecl_getParent(*const FieldDecl) ?*const RecordDecl;471 extern fn ZigClangFieldDecl_getParent(*const FieldDecl) ?*const RecordDecl;
472
473 pub const getFieldIndex = ZigClangFieldDecl_getFieldIndex;
474 extern fn ZigClangFieldDecl_getFieldIndex(*const FieldDecl) c_uint;
464};475};
465476
466pub const FileID = opaque {};477pub const FileID = opaque {};
...@@ -752,6 +763,9 @@ pub const RecordDecl = opaque {...@@ -752,6 +763,9 @@ pub const RecordDecl = opaque {
752 pub const getLocation = ZigClangRecordDecl_getLocation;763 pub const getLocation = ZigClangRecordDecl_getLocation;
753 extern fn ZigClangRecordDecl_getLocation(*const RecordDecl) SourceLocation;764 extern fn ZigClangRecordDecl_getLocation(*const RecordDecl) SourceLocation;
754765
766 pub const getASTRecordLayout = ZigClangRecordDecl_getASTRecordLayout;
767 extern fn ZigClangRecordDecl_getASTRecordLayout(*const RecordDecl, *const ASTContext) *const ASTRecordLayout;
768
755 pub const field_begin = ZigClangRecordDecl_field_begin;769 pub const field_begin = ZigClangRecordDecl_field_begin;
756 extern fn ZigClangRecordDecl_field_begin(*const RecordDecl) field_iterator;770 extern fn ZigClangRecordDecl_field_begin(*const RecordDecl) field_iterator;
757771
src/translate_c.zig+150-12
...@@ -819,6 +819,111 @@ fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNa...@@ -819,6 +819,111 @@ fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNa
819 }819 }
820}820}
821821
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
825fn 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
910fn 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
917fn 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
822fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void {927fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void {
823 if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name|928 if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name|
824 return; // Avoid processing this decl twice929 return; // Avoid processing this decl twice
...@@ -868,9 +973,16 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD...@@ -868,9 +973,16 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
868 var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa);973 var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa);
869 defer fields.deinit();974 defer fields.deinit();
870975
976 var functions = std.ArrayList(Node).init(c.gpa);
977 defer functions.deinit();
978
979 const has_flexible_array = hasFlexibleArrayField(c, record_def);
871 var unnamed_field_count: u32 = 0;980 var unnamed_field_count: u32 = 0;
872 var it = record_def.field_begin();981 var it = record_def.field_begin();
873 const end_it = record_def.field_end();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 while (it.neq(end_it)) : (it = it.next()) {986 while (it.neq(end_it)) : (it = it.next()) {
875 const field_decl = it.deref();987 const field_decl = it.deref();
876 const field_loc = field_decl.getLocation();988 const field_loc = field_decl.getLocation();
...@@ -882,12 +994,6 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD...@@ -882,12 +994,6 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
882 break :blk Tag.opaque_literal.init();994 break :blk Tag.opaque_literal.init();
883 }995 }
884996
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 var is_anon = false;997 var is_anon = false;
892 var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());998 var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
893 if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) {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,6 +1002,18 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
896 unnamed_field_count += 1;1002 unnamed_field_count += 1;
897 is_anon = true;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 const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) {1017 const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) {
900 error.UnsupportedType => {1018 error.UnsupportedType => {
901 try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});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,7 +1023,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
905 else => |e| return e,1023 else => |e| return e,
906 };1024 };
9071025
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));
9091030
910 if (is_anon) {1031 if (is_anon) {
911 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name);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,6 +1045,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
924 .data = .{1045 .data = .{
925 .is_packed = is_packed,1046 .is_packed = is_packed,
926 .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items),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 break :blk Node.initPayload(&record_payload.base);1051 break :blk Node.initPayload(&record_payload.base);
...@@ -1737,12 +1859,12 @@ fn transImplicitCastExpr(...@@ -1737,12 +1859,12 @@ fn transImplicitCastExpr(
1737 return maybeSuppressResult(c, scope, result_used, sub_expr_node);1859 return maybeSuppressResult(c, scope, result_used, sub_expr_node);
1738 },1860 },
1739 .ArrayToPointerDecay => {1861 .ArrayToPointerDecay => {
1740 if (exprIsNarrowStringLiteral(sub_expr)) {1862 const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
1741 const sub_expr_node = try transExpr(c, scope, sub_expr, .used);1863 if (exprIsNarrowStringLiteral(sub_expr) or exprIsFlexibleArrayRef(c, sub_expr)) {
1742 return maybeSuppressResult(c, scope, result_used, sub_expr_node);1864 return maybeSuppressResult(c, scope, result_used, sub_expr_node);
1743 }1865 }
17441866
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 const casted = try transCPtrCast(c, scope, expr.getBeginLoc(), dest_type, src_type, addr);1868 const casted = try transCPtrCast(c, scope, expr.getBeginLoc(), dest_type, src_type, addr);
1747 return maybeSuppressResult(c, scope, result_used, casted);1869 return maybeSuppressResult(c, scope, result_used, casted);
1748 },1870 },
...@@ -1852,6 +1974,19 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {...@@ -1852,6 +1974,19 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
1852 }1974 }
1853}1975}
18541976
1977fn 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
1855fn isBoolRes(res: Node) bool {1990fn isBoolRes(res: Node) bool {
1856 switch (res.tag()) {1991 switch (res.tag()) {
1857 .@"or",1992 .@"or",
...@@ -3056,7 +3191,6 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used:...@@ -3056,7 +3191,6 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used:
30563191
3057fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node {3192fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node {
3058 var container_node = try transExpr(c, scope, stmt.getBase(), .used);3193 var container_node = try transExpr(c, scope, stmt.getBase(), .used);
3059
3060 if (stmt.isArrow()) {3194 if (stmt.isArrow()) {
3061 container_node = try Tag.deref.create(c.arena, container_node);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,7 +3210,11 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re
3076 const decl = @ptrCast(*const clang.NamedDecl, member_decl);3210 const decl = @ptrCast(*const clang.NamedDecl, member_decl);
3077 break :blk try c.str(decl.getName_bytes_begin());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 return maybeSuppressResult(c, scope, result_used, node);3218 return maybeSuppressResult(c, scope, result_used, node);
3081}3219}
30823220
src/translate_c/ast.zig+21-5
...@@ -193,6 +193,8 @@ pub const Node = extern union {...@@ -193,6 +193,8 @@ pub const Node = extern union {
193193
194 /// @import("std").meta.sizeof(operand)194 /// @import("std").meta.sizeof(operand)
195 std_meta_sizeof,195 std_meta_sizeof,
196 /// @import("std").meta.FlexibleArrayType(lhs, rhs)
197 std_meta_flexible_array_type,
196 /// @import("std").meta.shuffleVectorIndex(lhs, rhs)198 /// @import("std").meta.shuffleVectorIndex(lhs, rhs)
197 std_meta_shuffle_vector_index,199 std_meta_shuffle_vector_index,
198 /// @import("std").meta.Vector(lhs, rhs)200 /// @import("std").meta.Vector(lhs, rhs)
...@@ -328,6 +330,7 @@ pub const Node = extern union {...@@ -328,6 +330,7 @@ pub const Node = extern union {
328 .align_cast,330 .align_cast,
329 .array_access,331 .array_access,
330 .std_mem_zeroinit,332 .std_mem_zeroinit,
333 .std_meta_flexible_array_type,
331 .std_meta_shuffle_vector_index,334 .std_meta_shuffle_vector_index,
332 .std_meta_vector,335 .std_meta_vector,
333 .ptr_cast,336 .ptr_cast,
...@@ -567,6 +570,7 @@ pub const Payload = struct {...@@ -567,6 +570,7 @@ pub const Payload = struct {
567 data: struct {570 data: struct {
568 is_packed: bool,571 is_packed: bool,
569 fields: []Field,572 fields: []Field,
573 functions: []Node,
570 },574 },
571575
572 pub const Field = struct {576 pub const Field = struct {
...@@ -909,6 +913,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -909,6 +913,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
909 const import_node = try renderStdImport(c, "mem", "zeroInit");913 const import_node = try renderStdImport(c, "mem", "zeroInit");
910 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });914 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
911 },915 },
916 .std_meta_flexible_array_type => {
917 const payload = node.castTag(.std_meta_flexible_array_type).?.data;
918 const import_node = try renderStdImport(c, "meta", "FlexibleArrayType");
919 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
920 },
912 .std_meta_shuffle_vector_index => {921 .std_meta_shuffle_vector_index => {
913 const payload = node.castTag(.std_meta_shuffle_vector_index).?.data;922 const payload = node.castTag(.std_meta_shuffle_vector_index).?.data;
914 const import_node = try renderStdImport(c, "meta", "shuffleVectorIndex");923 const import_node = try renderStdImport(c, "meta", "shuffleVectorIndex");
...@@ -1992,7 +2001,10 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {...@@ -1992,7 +2001,10 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {
1992 try c.addToken(.keyword_union, "union");2001 try c.addToken(.keyword_union, "union");
19932002
1994 _ = try c.addToken(.l_brace, "{");2003 _ = try c.addToken(.l_brace, "{");
1995 const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.fields.len, 2));2004
2005 const num_funcs = payload.functions.len;
2006 const total_members = payload.fields.len + num_funcs;
2007 const members = try c.gpa.alloc(NodeIndex, std.math.max(total_members, 2));
1996 defer c.gpa.free(members);2008 defer c.gpa.free(members);
1997 members[0] = 0;2009 members[0] = 0;
1998 members[1] = 0;2010 members[1] = 0;
...@@ -2033,9 +2045,12 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {...@@ -2033,9 +2045,12 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {
2033 });2045 });
2034 _ = try c.addToken(.comma, ",");2046 _ = try c.addToken(.comma, ",");
2035 }2047 }
2048 for (payload.functions) |function, i| {
2049 members[payload.fields.len + i] = try renderNode(c, function);
2050 }
2036 _ = try c.addToken(.r_brace, "}");2051 _ = try c.addToken(.r_brace, "}");
20372052
2038 if (payload.fields.len == 0) {2053 if (total_members == 0) {
2039 return c.addNode(.{2054 return c.addNode(.{
2040 .tag = .container_decl_two,2055 .tag = .container_decl_two,
2041 .main_token = kind_tok,2056 .main_token = kind_tok,
...@@ -2044,9 +2059,9 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {...@@ -2044,9 +2059,9 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {
2044 .rhs = 0,2059 .rhs = 0,
2045 },2060 },
2046 });2061 });
2047 } else if (payload.fields.len <= 2) {2062 } else if (total_members <= 2) {
2048 return c.addNode(.{2063 return c.addNode(.{
2049 .tag = .container_decl_two_trailing,2064 .tag = if (num_funcs == 0) .container_decl_two_trailing else .container_decl_two,
2050 .main_token = kind_tok,2065 .main_token = kind_tok,
2051 .data = .{2066 .data = .{
2052 .lhs = members[0],2067 .lhs = members[0],
...@@ -2056,7 +2071,7 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {...@@ -2056,7 +2071,7 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {
2056 } else {2071 } else {
2057 const span = try c.listToSpan(members);2072 const span = try c.listToSpan(members);
2058 return c.addNode(.{2073 return c.addNode(.{
2059 .tag = .container_decl_trailing,2074 .tag = if (num_funcs == 0) .container_decl_trailing else .container_decl,
2060 .main_token = kind_tok,2075 .main_token = kind_tok,
2061 .data = .{2076 .data = .{
2062 .lhs = span.start,2077 .lhs = span.start,
...@@ -2229,6 +2244,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2229,6 +2244,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2229 .std_meta_promoteIntLiteral,2244 .std_meta_promoteIntLiteral,
2230 .std_meta_vector,2245 .std_meta_vector,
2231 .std_meta_shuffle_vector_index,2246 .std_meta_shuffle_vector_index,
2247 .std_meta_flexible_array_type,
2232 .std_mem_zeroinit,2248 .std_mem_zeroinit,
2233 .integer_literal,2249 .integer_literal,
2234 .float_literal,2250 .float_literal,
src/zig_clang.cpp+22
...@@ -24,6 +24,7 @@...@@ -24,6 +24,7 @@
24#include <clang/AST/APValue.h>24#include <clang/AST/APValue.h>
25#include <clang/AST/Attr.h>25#include <clang/AST/Attr.h>
26#include <clang/AST/Expr.h>26#include <clang/AST/Expr.h>
27#include <clang/AST/RecordLayout.h>
2728
28#if __GNUC__ >= 829#if __GNUC__ >= 8
29#pragma GCC diagnostic pop30#pragma GCC diagnostic pop
...@@ -2716,6 +2717,22 @@ struct ZigClangQualType ZigClangCStyleCastExpr_getType(const struct ZigClangCSty...@@ -2716,6 +2717,22 @@ struct ZigClangQualType ZigClangCStyleCastExpr_getType(const struct ZigClangCSty
2716 return bitcast(casted->getType());2717 return bitcast(casted->getType());
2717}2718}
27182719
2720const struct ZigClangASTRecordLayout *ZigClangRecordDecl_getASTRecordLayout(const struct ZigClangRecordDecl *self, const struct ZigClangASTContext *ctx) {
2721 auto casted_self = reinterpret_cast<const clang::RecordDecl *>(self);
2722 auto casted_ctx = reinterpret_cast<const clang::ASTContext *>(ctx);
2723 const clang::ASTRecordLayout &layout = casted_ctx->getASTRecordLayout(casted_self);
2724 return reinterpret_cast<const struct ZigClangASTRecordLayout *>(&layout);
2725}
2726
2727uint64_t ZigClangASTRecordLayout_getFieldOffset(const struct ZigClangASTRecordLayout *self, unsigned field_no) {
2728 return reinterpret_cast<const clang::ASTRecordLayout *>(self)->getFieldOffset(field_no);
2729}
2730
2731int64_t ZigClangASTRecordLayout_getAlignment(const struct ZigClangASTRecordLayout *self) {
2732 auto casted_self = reinterpret_cast<const clang::ASTRecordLayout *>(self);
2733 return casted_self->getAlignment().getQuantity();
2734}
2735
2719bool ZigClangIntegerLiteral_EvaluateAsInt(const struct ZigClangIntegerLiteral *self, struct ZigClangExprEvalResult *result, const struct ZigClangASTContext *ctx) {2736bool ZigClangIntegerLiteral_EvaluateAsInt(const struct ZigClangIntegerLiteral *self, struct ZigClangExprEvalResult *result, const struct ZigClangASTContext *ctx) {
2720 auto casted_self = reinterpret_cast<const clang::IntegerLiteral *>(self);2737 auto casted_self = reinterpret_cast<const clang::IntegerLiteral *>(self);
2721 auto casted_ctx = reinterpret_cast<const clang::ASTContext *>(ctx);2738 auto casted_ctx = reinterpret_cast<const clang::ASTContext *>(ctx);
...@@ -3136,6 +3153,11 @@ const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const struct ZigCla...@@ -3136,6 +3153,11 @@ const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const struct ZigCla
3136 return reinterpret_cast<const ZigClangRecordDecl *>(casted->getParent());3153 return reinterpret_cast<const ZigClangRecordDecl *>(casted->getParent());
3137}3154}
31383155
3156unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *self) {
3157 auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
3158 return casted->getFieldIndex();
3159}
3160
3139ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *self) {3161ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *self) {
3140 auto casted = reinterpret_cast<const clang::FieldDecl *>(self);3162 auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
3141 return bitcast(casted->getType());3163 return bitcast(casted->getType());
src/zig_clang.h+7
...@@ -91,6 +91,7 @@ struct ZigClangAPFloat;...@@ -91,6 +91,7 @@ struct ZigClangAPFloat;
91struct ZigClangAPInt;91struct ZigClangAPInt;
92struct ZigClangAPSInt;92struct ZigClangAPSInt;
93struct ZigClangASTContext;93struct ZigClangASTContext;
94struct ZigClangASTRecordLayout;
94struct ZigClangASTUnit;95struct ZigClangASTUnit;
95struct ZigClangArraySubscriptExpr;96struct ZigClangArraySubscriptExpr;
96struct ZigClangArrayType;97struct ZigClangArrayType;
...@@ -1017,6 +1018,11 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangEnumDecl_getLocation(const st...@@ -1017,6 +1018,11 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangEnumDecl_getLocation(const st
1017ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangTypedefNameDecl_getLocation(const struct ZigClangTypedefNameDecl *);1018ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangTypedefNameDecl_getLocation(const struct ZigClangTypedefNameDecl *);
1018ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDecl_getLocation(const struct ZigClangDecl *);1019ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDecl_getLocation(const struct ZigClangDecl *);
10191020
1021ZIG_EXTERN_C const struct ZigClangASTRecordLayout *ZigClangRecordDecl_getASTRecordLayout(const struct ZigClangRecordDecl *, const struct ZigClangASTContext *);
1022
1023ZIG_EXTERN_C uint64_t ZigClangASTRecordLayout_getFieldOffset(const struct ZigClangASTRecordLayout *, unsigned);
1024ZIG_EXTERN_C int64_t ZigClangASTRecordLayout_getAlignment(const struct ZigClangASTRecordLayout *);
1025
1020ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionDecl_getType(const struct ZigClangFunctionDecl *);1026ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionDecl_getType(const struct ZigClangFunctionDecl *);
1021ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFunctionDecl_getLocation(const struct ZigClangFunctionDecl *);1027ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFunctionDecl_getLocation(const struct ZigClangFunctionDecl *);
1022ZIG_EXTERN_C bool ZigClangFunctionDecl_hasBody(const struct ZigClangFunctionDecl *);1028ZIG_EXTERN_C bool ZigClangFunctionDecl_hasBody(const struct ZigClangFunctionDecl *);
...@@ -1317,6 +1323,7 @@ ZIG_EXTERN_C bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangField...@@ -1317,6 +1323,7 @@ ZIG_EXTERN_C bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangField
1317ZIG_EXTERN_C struct ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *);1323ZIG_EXTERN_C struct ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *);
1318ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *);1324ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *);
1319ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const struct ZigClangFieldDecl *);1325ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const struct ZigClangFieldDecl *);
1326ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *);
13201327
1321ZIG_EXTERN_C const struct ZigClangExpr *ZigClangEnumConstantDecl_getInitExpr(const struct ZigClangEnumConstantDecl *);1328ZIG_EXTERN_C const struct ZigClangExpr *ZigClangEnumConstantDecl_getInitExpr(const struct ZigClangEnumConstantDecl *);
1322ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *);1329ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *);
test/run_translated_c.zig+21
...@@ -1519,4 +1519,25 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1519,4 +1519,25 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1519 \\ return 0;1519 \\ return 0;
1520 \\}1520 \\}
1521 , "");1521 , "");
1522
1523 cases.add("Flexible arrays",
1524 \\#include <stdlib.h>
1525 \\#include <stdint.h>
1526 \\typedef struct { char foo; int bar; } ITEM;
1527 \\typedef struct { size_t count; ITEM items[]; } ITEM_LIST;
1528 \\typedef struct { unsigned char count; int items[]; } INT_LIST;
1529 \\#define SIZE 10
1530 \\int main(void) {
1531 \\ ITEM_LIST *list = malloc(sizeof(ITEM_LIST) + SIZE * sizeof(ITEM));
1532 \\ for (int i = 0; i < SIZE; i++) list->items[i] = (ITEM) {.foo = i, .bar = i + 1};
1533 \\ const ITEM_LIST *const c_list = list;
1534 \\ for (int i = 0; i < SIZE; i++) if (c_list->items[i].foo != i || c_list->items[i].bar != i + 1) abort();
1535 \\ INT_LIST *int_list = malloc(sizeof(INT_LIST) + SIZE * sizeof(int));
1536 \\ for (int i = 0; i < SIZE; i++) int_list->items[i] = i;
1537 \\ const INT_LIST *const c_int_list = int_list;
1538 \\ const int *const ints = int_list->items;
1539 \\ for (int i = 0; i < SIZE; i++) if (ints[i] != i) abort();
1540 \\ return 0;
1541 \\}
1542 , "");
1522}1543}
test/translate_c.zig+17-4
...@@ -421,13 +421,26 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -421,13 +421,26 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
421 \\};421 \\};
422 });422 });
423423
424 cases.add("structs with VLAs are rejected",424 cases.add("struct with flexible array",
425 \\struct foo { int x; int y[]; };425 \\struct foo { int x; int y[]; };
426 \\struct bar { int x; int y[0]; };426 \\struct bar { int x; int y[0]; };
427 , &[_][]const u8{427 , &[_][]const u8{
428 \\pub const struct_foo = opaque {};428 \\pub const struct_foo = extern struct {
429 ,429 \\ x: c_int align(4),
430 \\pub const struct_bar = opaque {};430 \\ pub fn y(self: anytype) @import("std").meta.FlexibleArrayType(@TypeOf(self), c_int) {
431 \\ const Intermediate = @import("std").meta.FlexibleArrayType(@TypeOf(self), u8);
432 \\ const ReturnType = @import("std").meta.FlexibleArrayType(@TypeOf(self), c_int);
433 \\ return @ptrCast(ReturnType, @alignCast(@alignOf(c_int), @ptrCast(Intermediate, self) + 4));
434 \\ }
435 \\};
436 \\pub const struct_bar = extern struct {
437 \\ x: c_int align(4),
438 \\ pub fn y(self: anytype) @import("std").meta.FlexibleArrayType(@TypeOf(self), c_int) {
439 \\ const Intermediate = @import("std").meta.FlexibleArrayType(@TypeOf(self), u8);
440 \\ const ReturnType = @import("std").meta.FlexibleArrayType(@TypeOf(self), c_int);
441 \\ return @ptrCast(ReturnType, @alignCast(@alignOf(c_int), @ptrCast(Intermediate, self) + 4));
442 \\ }
443 \\};
431 });444 });
432445
433 cases.add("nested loops without blocks",446 cases.add("nested loops without blocks",