diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 18f761d86e..56b231eedb 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -1355,3 +1355,34 @@ test "isError" { try std.testing.expect(isError(math.absInt(@as(i8, -128)))); try std.testing.expect(!isError(math.absInt(@as(i8, -127)))); } + +/// This function is for translate-c and is not intended for general use. +/// Constructs a [*c] pointer with the const and volatile annotations +/// from SelfType for pointing to a C flexible array of ElementType. +pub fn FlexibleArrayType(comptime SelfType: type, ElementType: type) type { + switch (@typeInfo(SelfType)) { + .Pointer => |ptr| { + return @Type(TypeInfo{ .Pointer = .{ + .size = .C, + .is_const = ptr.is_const, + .is_volatile = ptr.is_volatile, + .alignment = @alignOf(ElementType), + .child = ElementType, + .is_allowzero = true, + .sentinel = null, + } }); + }, + else => |info| @compileError("Invalid self type \"" ++ @tagName(info) ++ "\" for flexible array getter: " ++ @typeName(SelfType)), + } +} + +test "Flexible Array Type" { + const Container = extern struct { + size: usize, + }; + + try testing.expectEqual(FlexibleArrayType(*Container, c_int), [*c]c_int); + try testing.expectEqual(FlexibleArrayType(*const Container, c_int), [*c]const c_int); + try testing.expectEqual(FlexibleArrayType(*volatile Container, c_int), [*c]volatile c_int); + try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int); +} diff --git a/src/clang.zig b/src/clang.zig index d91ee91e72..4c9238117b 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -183,6 +183,14 @@ pub const ArrayType = opaque { extern fn ZigClangArrayType_getElementType(*const ArrayType) QualType; }; +pub const ASTRecordLayout = opaque { + pub const getFieldOffset = ZigClangASTRecordLayout_getFieldOffset; + extern fn ZigClangASTRecordLayout_getFieldOffset(*const ASTRecordLayout, c_uint) u64; + + pub const getAlignment = ZigClangASTRecordLayout_getAlignment; + extern fn ZigClangASTRecordLayout_getAlignment(*const ASTRecordLayout) i64; +}; + pub const AttributedType = opaque { pub const getEquivalentType = ZigClangAttributedType_getEquivalentType; extern fn ZigClangAttributedType_getEquivalentType(*const AttributedType) QualType; @@ -461,6 +469,9 @@ pub const FieldDecl = opaque { pub const getParent = ZigClangFieldDecl_getParent; extern fn ZigClangFieldDecl_getParent(*const FieldDecl) ?*const RecordDecl; + + pub const getFieldIndex = ZigClangFieldDecl_getFieldIndex; + extern fn ZigClangFieldDecl_getFieldIndex(*const FieldDecl) c_uint; }; pub const FileID = opaque {}; @@ -752,6 +763,9 @@ pub const RecordDecl = opaque { pub const getLocation = ZigClangRecordDecl_getLocation; extern fn ZigClangRecordDecl_getLocation(*const RecordDecl) SourceLocation; + pub const getASTRecordLayout = ZigClangRecordDecl_getASTRecordLayout; + extern fn ZigClangRecordDecl_getASTRecordLayout(*const RecordDecl, *const ASTContext) *const ASTRecordLayout; + pub const field_begin = ZigClangRecordDecl_field_begin; extern fn ZigClangRecordDecl_field_begin(*const RecordDecl) field_iterator; diff --git a/src/translate_c.zig b/src/translate_c.zig index bd3d5ad0b4..d1fa2d7222 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -819,6 +819,111 @@ fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNa } } +/// Build a getter function for a flexible array member at the end of a C struct +/// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer +/// to the flexible array with the correct const and volatile qualifiers +fn buildFlexibleArrayFn( + c: *Context, + scope: *Scope, + layout: *const clang.ASTRecordLayout, + field_name: []const u8, + field_decl: *const clang.FieldDecl, +) TypeError!Node { + const field_qt = field_decl.getType(); + + const u8_type = try Tag.type.create(c.arena, "u8"); + const self_param_name = "self"; + const self_param = try Tag.identifier.create(c.arena, self_param_name); + const self_type = try Tag.typeof.create(c.arena, self_param); + + const fn_params = try c.arena.alloc(ast.Payload.Param, 1); + + fn_params[0] = .{ + .name = self_param_name, + .type = Tag.@"anytype".init(), + .is_noalias = false, + }; + + const array_type = @ptrCast(*const clang.ArrayType, field_qt.getTypePtr()); + const element_qt = array_type.getElementType(); + const element_type = try transQualType(c, scope, element_qt, field_decl.getLocation()); + + var block_scope = try Scope.Block.init(c, scope, false); + defer block_scope.deinit(); + + const intermediate_type_name = try block_scope.makeMangledName(c, "Intermediate"); + const intermediate_type = try Tag.std_meta_flexible_array_type.create(c.arena, .{ .lhs = self_type, .rhs = u8_type }); + const intermediate_type_decl = try Tag.var_simple.create(c.arena, .{ + .name = intermediate_type_name, + .init = intermediate_type, + }); + try block_scope.statements.append(intermediate_type_decl); + const intermediate_type_ident = try Tag.identifier.create(c.arena, intermediate_type_name); + + const return_type_name = try block_scope.makeMangledName(c, "ReturnType"); + const return_type = try Tag.std_meta_flexible_array_type.create(c.arena, .{ .lhs = self_type, .rhs = element_type }); + const return_type_decl = try Tag.var_simple.create(c.arena, .{ + .name = return_type_name, + .init = return_type, + }); + try block_scope.statements.append(return_type_decl); + const return_type_ident = try Tag.identifier.create(c.arena, return_type_name); + + const field_index = field_decl.getFieldIndex(); + const bit_offset = layout.getFieldOffset(field_index); // this is a target-specific constant based on the struct layout + const byte_offset = bit_offset / 8; + + const casted_self = try Tag.ptr_cast.create(c.arena, .{ + .lhs = intermediate_type_ident, + .rhs = self_param, + }); + const field_offset = try transCreateNodeNumber(c, byte_offset, .int); + const field_ptr = try Tag.add.create(c.arena, .{ .lhs = casted_self, .rhs = field_offset }); + + const alignment = try Tag.alignof.create(c.arena, element_type); + + const ptr_val = try Tag.align_cast.create(c.arena, .{ .lhs = alignment, .rhs = field_ptr }); + const ptr_cast = try Tag.ptr_cast.create(c.arena, .{ .lhs = return_type_ident, .rhs = ptr_val }); + const return_stmt = try Tag.@"return".create(c.arena, ptr_cast); + try block_scope.statements.append(return_stmt); + + const payload = try c.arena.create(ast.Payload.Func); + payload.* = .{ + .base = .{ .tag = .func }, + .data = .{ + .is_pub = true, + .is_extern = false, + .is_export = false, + .is_var_args = false, + .name = field_name, + .linksection_string = null, + .explicit_callconv = null, + .params = fn_params, + .return_type = return_type, + .body = try block_scope.complete(c), + .alignment = null, + }, + }; + return Node.initPayload(&payload.base); +} + +fn isFlexibleArrayFieldDecl(c: *Context, field_decl: *const clang.FieldDecl) bool { + return qualTypeCanon(field_decl.getType()).isIncompleteOrZeroLengthArrayType(c.clang_context); +} + +/// clang's RecordDecl::hasFlexibleArrayMember is not suitable for determining +/// this because it returns false for a record that ends with a zero-length +/// array, but we consider those to be flexible arrays +fn hasFlexibleArrayField(c: *Context, record_def: *const clang.RecordDecl) bool { + var it = record_def.field_begin(); + const end_it = record_def.field_end(); + while (it.neq(end_it)) : (it = it.next()) { + const field_decl = it.deref(); + if (isFlexibleArrayFieldDecl(c, field_decl)) return true; + } + return false; +} + fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void { if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name| return; // Avoid processing this decl twice @@ -868,9 +973,16 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa); defer fields.deinit(); + var functions = std.ArrayList(Node).init(c.gpa); + defer functions.deinit(); + + const has_flexible_array = hasFlexibleArrayField(c, record_def); var unnamed_field_count: u32 = 0; var it = record_def.field_begin(); const end_it = record_def.field_end(); + const layout = record_def.getASTRecordLayout(c.clang_context); + const record_alignment = layout.getAlignment(); + while (it.neq(end_it)) : (it = it.next()) { const field_decl = it.deref(); const field_loc = field_decl.getLocation(); @@ -882,12 +994,6 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD break :blk Tag.opaque_literal.init(); } - if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) { - try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - try warn(c, scope, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); - break :blk Tag.opaque_literal.init(); - } - var is_anon = false; var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) { @@ -896,6 +1002,18 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD unnamed_field_count += 1; is_anon = true; } + if (isFlexibleArrayFieldDecl(c, field_decl)) { + const flexible_array_fn = buildFlexibleArrayFn(c, scope, layout, field_name, field_decl) catch |err| switch (err) { + error.UnsupportedType => { + try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); + 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 }); + break :blk Tag.opaque_literal.init(); + }, + else => |e| return e, + }; + try functions.append(flexible_array_fn); + continue; + } const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) { error.UnsupportedType => { 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 else => |e| return e, }; - const alignment = zigAlignment(field_decl.getAlignedAttribute(c.clang_context)); + const alignment = if (has_flexible_array and field_decl.getFieldIndex() == 0) + @intCast(c_uint, record_alignment) + else + zigAlignment(field_decl.getAlignedAttribute(c.clang_context)); if (is_anon) { 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 .data = .{ .is_packed = is_packed, .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), + .functions = try c.arena.dupe(Node, functions.items), }, }; break :blk Node.initPayload(&record_payload.base); @@ -1737,12 +1859,12 @@ fn transImplicitCastExpr( return maybeSuppressResult(c, scope, result_used, sub_expr_node); }, .ArrayToPointerDecay => { - if (exprIsNarrowStringLiteral(sub_expr)) { - const sub_expr_node = try transExpr(c, scope, sub_expr, .used); + const sub_expr_node = try transExpr(c, scope, sub_expr, .used); + if (exprIsNarrowStringLiteral(sub_expr) or exprIsFlexibleArrayRef(c, sub_expr)) { return maybeSuppressResult(c, scope, result_used, sub_expr_node); } - const addr = try Tag.address_of.create(c.arena, try transExpr(c, scope, sub_expr, .used)); + const addr = try Tag.address_of.create(c.arena, sub_expr_node); const casted = try transCPtrCast(c, scope, expr.getBeginLoc(), dest_type, src_type, addr); return maybeSuppressResult(c, scope, result_used, casted); }, @@ -1852,6 +1974,19 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool { } } +fn exprIsFlexibleArrayRef(c: *Context, expr: *const clang.Expr) bool { + if (expr.getStmtClass() == .MemberExprClass) { + const member_expr = @ptrCast(*const clang.MemberExpr, expr); + const member_decl = member_expr.getMemberDecl(); + const decl_kind = @ptrCast(*const clang.Decl, member_decl).getKind(); + if (decl_kind == .Field) { + const field_decl = @ptrCast(*const clang.FieldDecl, member_decl); + return isFlexibleArrayFieldDecl(c, field_decl); + } + } + return false; +} + fn isBoolRes(res: Node) bool { switch (res.tag()) { .@"or", @@ -3056,7 +3191,6 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node { var container_node = try transExpr(c, scope, stmt.getBase(), .used); - if (stmt.isArrow()) { container_node = try Tag.deref.create(c.arena, container_node); } @@ -3076,7 +3210,11 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re const decl = @ptrCast(*const clang.NamedDecl, member_decl); break :blk try c.str(decl.getName_bytes_begin()); }; - const node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .field_name = name }); + + var node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .field_name = name }); + if (exprIsFlexibleArrayRef(c, @ptrCast(*const clang.Expr, stmt))) { + node = try Tag.call.create(c.arena, .{ .lhs = node, .args = &.{} }); + } return maybeSuppressResult(c, scope, result_used, node); } diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 50af075652..ba881f2daa 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -193,6 +193,8 @@ pub const Node = extern union { /// @import("std").meta.sizeof(operand) std_meta_sizeof, + /// @import("std").meta.FlexibleArrayType(lhs, rhs) + std_meta_flexible_array_type, /// @import("std").meta.shuffleVectorIndex(lhs, rhs) std_meta_shuffle_vector_index, /// @import("std").meta.Vector(lhs, rhs) @@ -328,6 +330,7 @@ pub const Node = extern union { .align_cast, .array_access, .std_mem_zeroinit, + .std_meta_flexible_array_type, .std_meta_shuffle_vector_index, .std_meta_vector, .ptr_cast, @@ -567,6 +570,7 @@ pub const Payload = struct { data: struct { is_packed: bool, fields: []Field, + functions: []Node, }, pub const Field = struct { @@ -909,6 +913,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const import_node = try renderStdImport(c, "mem", "zeroInit"); return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); }, + .std_meta_flexible_array_type => { + const payload = node.castTag(.std_meta_flexible_array_type).?.data; + const import_node = try renderStdImport(c, "meta", "FlexibleArrayType"); + return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); + }, .std_meta_shuffle_vector_index => { const payload = node.castTag(.std_meta_shuffle_vector_index).?.data; const import_node = try renderStdImport(c, "meta", "shuffleVectorIndex"); @@ -1992,7 +2001,10 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex { try c.addToken(.keyword_union, "union"); _ = try c.addToken(.l_brace, "{"); - const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.fields.len, 2)); + + const num_funcs = payload.functions.len; + const total_members = payload.fields.len + num_funcs; + const members = try c.gpa.alloc(NodeIndex, std.math.max(total_members, 2)); defer c.gpa.free(members); members[0] = 0; members[1] = 0; @@ -2033,9 +2045,12 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex { }); _ = try c.addToken(.comma, ","); } + for (payload.functions) |function, i| { + members[payload.fields.len + i] = try renderNode(c, function); + } _ = try c.addToken(.r_brace, "}"); - if (payload.fields.len == 0) { + if (total_members == 0) { return c.addNode(.{ .tag = .container_decl_two, .main_token = kind_tok, @@ -2044,9 +2059,9 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex { .rhs = 0, }, }); - } else if (payload.fields.len <= 2) { + } else if (total_members <= 2) { return c.addNode(.{ - .tag = .container_decl_two_trailing, + .tag = if (num_funcs == 0) .container_decl_two_trailing else .container_decl_two, .main_token = kind_tok, .data = .{ .lhs = members[0], @@ -2056,7 +2071,7 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex { } else { const span = try c.listToSpan(members); return c.addNode(.{ - .tag = .container_decl_trailing, + .tag = if (num_funcs == 0) .container_decl_trailing else .container_decl, .main_token = kind_tok, .data = .{ .lhs = span.start, @@ -2229,6 +2244,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .std_meta_promoteIntLiteral, .std_meta_vector, .std_meta_shuffle_vector_index, + .std_meta_flexible_array_type, .std_mem_zeroinit, .integer_literal, .float_literal, diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 60cc4ec14d..8430ac0d21 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #if __GNUC__ >= 8 #pragma GCC diagnostic pop @@ -2716,6 +2717,22 @@ struct ZigClangQualType ZigClangCStyleCastExpr_getType(const struct ZigClangCSty return bitcast(casted->getType()); } +const struct ZigClangASTRecordLayout *ZigClangRecordDecl_getASTRecordLayout(const struct ZigClangRecordDecl *self, const struct ZigClangASTContext *ctx) { + auto casted_self = reinterpret_cast(self); + auto casted_ctx = reinterpret_cast(ctx); + const clang::ASTRecordLayout &layout = casted_ctx->getASTRecordLayout(casted_self); + return reinterpret_cast(&layout); +} + +uint64_t ZigClangASTRecordLayout_getFieldOffset(const struct ZigClangASTRecordLayout *self, unsigned field_no) { + return reinterpret_cast(self)->getFieldOffset(field_no); +} + +int64_t ZigClangASTRecordLayout_getAlignment(const struct ZigClangASTRecordLayout *self) { + auto casted_self = reinterpret_cast(self); + return casted_self->getAlignment().getQuantity(); +} + bool ZigClangIntegerLiteral_EvaluateAsInt(const struct ZigClangIntegerLiteral *self, struct ZigClangExprEvalResult *result, const struct ZigClangASTContext *ctx) { auto casted_self = reinterpret_cast(self); auto casted_ctx = reinterpret_cast(ctx); @@ -3136,6 +3153,11 @@ const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const struct ZigCla return reinterpret_cast(casted->getParent()); } +unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *self) { + auto casted = reinterpret_cast(self); + return casted->getFieldIndex(); +} + ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *self) { auto casted = reinterpret_cast(self); return bitcast(casted->getType()); diff --git a/src/zig_clang.h b/src/zig_clang.h index 7de5e5466d..e2a5444f52 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -91,6 +91,7 @@ struct ZigClangAPFloat; struct ZigClangAPInt; struct ZigClangAPSInt; struct ZigClangASTContext; +struct ZigClangASTRecordLayout; struct ZigClangASTUnit; struct ZigClangArraySubscriptExpr; struct ZigClangArrayType; @@ -1017,6 +1018,11 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangEnumDecl_getLocation(const st ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangTypedefNameDecl_getLocation(const struct ZigClangTypedefNameDecl *); ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDecl_getLocation(const struct ZigClangDecl *); +ZIG_EXTERN_C const struct ZigClangASTRecordLayout *ZigClangRecordDecl_getASTRecordLayout(const struct ZigClangRecordDecl *, const struct ZigClangASTContext *); + +ZIG_EXTERN_C uint64_t ZigClangASTRecordLayout_getFieldOffset(const struct ZigClangASTRecordLayout *, unsigned); +ZIG_EXTERN_C int64_t ZigClangASTRecordLayout_getAlignment(const struct ZigClangASTRecordLayout *); + ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionDecl_getType(const struct ZigClangFunctionDecl *); ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFunctionDecl_getLocation(const struct ZigClangFunctionDecl *); ZIG_EXTERN_C bool ZigClangFunctionDecl_hasBody(const struct ZigClangFunctionDecl *); @@ -1317,6 +1323,7 @@ ZIG_EXTERN_C bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangField ZIG_EXTERN_C struct ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *); ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *); ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const struct ZigClangFieldDecl *); +ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *); ZIG_EXTERN_C const struct ZigClangExpr *ZigClangEnumConstantDecl_getInitExpr(const struct ZigClangEnumConstantDecl *); ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *); diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index b846cb00ca..78a63b2287 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1519,4 +1519,25 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Flexible arrays", + \\#include + \\#include + \\typedef struct { char foo; int bar; } ITEM; + \\typedef struct { size_t count; ITEM items[]; } ITEM_LIST; + \\typedef struct { unsigned char count; int items[]; } INT_LIST; + \\#define SIZE 10 + \\int main(void) { + \\ ITEM_LIST *list = malloc(sizeof(ITEM_LIST) + SIZE * sizeof(ITEM)); + \\ for (int i = 0; i < SIZE; i++) list->items[i] = (ITEM) {.foo = i, .bar = i + 1}; + \\ const ITEM_LIST *const c_list = list; + \\ for (int i = 0; i < SIZE; i++) if (c_list->items[i].foo != i || c_list->items[i].bar != i + 1) abort(); + \\ INT_LIST *int_list = malloc(sizeof(INT_LIST) + SIZE * sizeof(int)); + \\ for (int i = 0; i < SIZE; i++) int_list->items[i] = i; + \\ const INT_LIST *const c_int_list = int_list; + \\ const int *const ints = int_list->items; + \\ for (int i = 0; i < SIZE; i++) if (ints[i] != i) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/translate_c.zig b/test/translate_c.zig index fe0624872b..74b7202319 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -421,13 +421,26 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; }); - cases.add("structs with VLAs are rejected", + cases.add("struct with flexible array", \\struct foo { int x; int y[]; }; \\struct bar { int x; int y[0]; }; , &[_][]const u8{ - \\pub const struct_foo = opaque {}; - , - \\pub const struct_bar = opaque {}; + \\pub const struct_foo = extern struct { + \\ x: c_int align(4), + \\ pub fn y(self: anytype) @import("std").meta.FlexibleArrayType(@TypeOf(self), c_int) { + \\ const Intermediate = @import("std").meta.FlexibleArrayType(@TypeOf(self), u8); + \\ const ReturnType = @import("std").meta.FlexibleArrayType(@TypeOf(self), c_int); + \\ return @ptrCast(ReturnType, @alignCast(@alignOf(c_int), @ptrCast(Intermediate, self) + 4)); + \\ } + \\}; + \\pub const struct_bar = extern struct { + \\ x: c_int align(4), + \\ pub fn y(self: anytype) @import("std").meta.FlexibleArrayType(@TypeOf(self), c_int) { + \\ const Intermediate = @import("std").meta.FlexibleArrayType(@TypeOf(self), u8); + \\ const ReturnType = @import("std").meta.FlexibleArrayType(@TypeOf(self), c_int); + \\ return @ptrCast(ReturnType, @alignCast(@alignOf(c_int), @ptrCast(Intermediate, self) + 4)); + \\ } + \\}; }); cases.add("nested loops without blocks",