| author | |
| committer | |
| log | e563af13296cdb3e64f0f396fdc58112d4484968 |
| tree | 46d5066948ebcce3825932839d25577014cb8ab6 |
| parent | f374ea2cd16cf5d0f6f8df6b50f55a9e315ae565 |
| parent | a560af96568a668bf0c01b3c58ad4dda647ea1b4 |
| signature |
translate-c: Translate clang packed struct C into Zig extern struct with align(1)6 files changed, 174 insertions(+), 50 deletions(-)
src/clang.zig+6| ... | @@ -470,6 +470,9 @@ pub const FieldDecl = opaque { | ... | @@ -470,6 +470,9 @@ pub const FieldDecl = opaque { |
| 470 | pub const getAlignedAttribute = ZigClangFieldDecl_getAlignedAttribute; | 470 | pub const getAlignedAttribute = ZigClangFieldDecl_getAlignedAttribute; |
| 471 | extern fn ZigClangFieldDecl_getAlignedAttribute(*const FieldDecl, *const ASTContext) c_uint; | 471 | extern fn ZigClangFieldDecl_getAlignedAttribute(*const FieldDecl, *const ASTContext) c_uint; |
| 472 | 472 | ||
| 473 | pub const getPackedAttribute = ZigClangFieldDecl_getPackedAttribute; | ||
| 474 | extern fn ZigClangFieldDecl_getPackedAttribute(*const FieldDecl) bool; | ||
| 475 | |||
| 473 | pub const isAnonymousStructOrUnion = ZigClangFieldDecl_isAnonymousStructOrUnion; | 476 | pub const isAnonymousStructOrUnion = ZigClangFieldDecl_isAnonymousStructOrUnion; |
| 474 | extern fn ZigClangFieldDecl_isAnonymousStructOrUnion(*const FieldDecl) bool; | 477 | extern fn ZigClangFieldDecl_isAnonymousStructOrUnion(*const FieldDecl) bool; |
| 475 | 478 | ||
| ... | @@ -1015,6 +1018,9 @@ pub const VarDecl = opaque { | ... | @@ -1015,6 +1018,9 @@ pub const VarDecl = opaque { |
| 1015 | pub const getAlignedAttribute = ZigClangVarDecl_getAlignedAttribute; | 1018 | pub const getAlignedAttribute = ZigClangVarDecl_getAlignedAttribute; |
| 1016 | extern fn ZigClangVarDecl_getAlignedAttribute(*const VarDecl, *const ASTContext) c_uint; | 1019 | extern fn ZigClangVarDecl_getAlignedAttribute(*const VarDecl, *const ASTContext) c_uint; |
| 1017 | 1020 | ||
| 1021 | pub const getPackedAttribute = ZigClangVarDecl_getPackedAttribute; | ||
| 1022 | extern fn ZigClangVarDecl_getPackedAttribute(*const VarDecl) bool; | ||
| 1023 | |||
| 1018 | pub const getCleanupAttribute = ZigClangVarDecl_getCleanupAttribute; | 1024 | pub const getCleanupAttribute = ZigClangVarDecl_getCleanupAttribute; |
| 1019 | extern fn ZigClangVarDecl_getCleanupAttribute(*const VarDecl) ?*const FunctionDecl; | 1025 | extern fn ZigClangVarDecl_getCleanupAttribute(*const VarDecl) ?*const FunctionDecl; |
| 1020 | 1026 |
src/translate_c.zig+61-16| ... | @@ -878,7 +878,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co | ... | @@ -878,7 +878,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 878 | .is_export = is_export, | 878 | .is_export = is_export, |
| 879 | .is_threadlocal = is_threadlocal, | 879 | .is_threadlocal = is_threadlocal, |
| 880 | .linksection_string = linksection_string, | 880 | .linksection_string = linksection_string, |
| 881 | .alignment = zigAlignment(var_decl.getAlignedAttribute(c.clang_context)), | 881 | .alignment = ClangAlignment.forVar(c, var_decl).zigAlignment(), |
| 882 | .name = var_name, | 882 | .name = var_name, |
| 883 | .type = type_node, | 883 | .type = type_node, |
| 884 | .init = init_node, | 884 | .init = init_node, |
| ... | @@ -1096,7 +1096,6 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD | ... | @@ -1096,7 +1096,6 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 1096 | break :blk Tag.opaque_literal.init(); | 1096 | break :blk Tag.opaque_literal.init(); |
| 1097 | }; | 1097 | }; |
| 1098 | 1098 | ||
| 1099 | const is_packed = record_decl.getPackedAttribute(); | ||
| 1100 | var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa); | 1099 | var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa); |
| 1101 | defer fields.deinit(); | 1100 | defer fields.deinit(); |
| 1102 | 1101 | ||
| ... | @@ -1153,7 +1152,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD | ... | @@ -1153,7 +1152,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 1153 | const alignment = if (has_flexible_array and field_decl.getFieldIndex() == 0) | 1152 | const alignment = if (has_flexible_array and field_decl.getFieldIndex() == 0) |
| 1154 | @intCast(c_uint, record_alignment) | 1153 | @intCast(c_uint, record_alignment) |
| 1155 | else | 1154 | else |
| 1156 | zigAlignment(field_decl.getAlignedAttribute(c.clang_context)); | 1155 | ClangAlignment.forField(c, field_decl, record_def).zigAlignment(); |
| 1157 | 1156 | ||
| 1158 | if (is_anon) { | 1157 | if (is_anon) { |
| 1159 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name); | 1158 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name); |
| ... | @@ -1166,15 +1165,11 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD | ... | @@ -1166,15 +1165,11 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 1166 | }); | 1165 | }); |
| 1167 | } | 1166 | } |
| 1168 | 1167 | ||
| 1169 | if (!c.zig_is_stage1 and is_packed) { | ||
| 1170 | return failDecl(c, record_loc, name, "cannot translate packed record union", .{}); | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | const record_payload = try c.arena.create(ast.Payload.Record); | 1168 | const record_payload = try c.arena.create(ast.Payload.Record); |
| 1174 | record_payload.* = .{ | 1169 | record_payload.* = .{ |
| 1175 | .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] }, | 1170 | .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] }, |
| 1176 | .data = .{ | 1171 | .data = .{ |
| 1177 | .layout = if (is_packed) .@"packed" else .@"extern", | 1172 | .layout = .@"extern", |
| 1178 | .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), | 1173 | .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), |
| 1179 | .functions = try c.arena.dupe(Node, functions.items), | 1174 | .functions = try c.arena.dupe(Node, functions.items), |
| 1180 | .variables = &.{}, | 1175 | .variables = &.{}, |
| ... | @@ -1851,12 +1846,62 @@ fn transCStyleCastExprClass( | ... | @@ -1851,12 +1846,62 @@ fn transCStyleCastExprClass( |
| 1851 | return maybeSuppressResult(c, scope, result_used, cast_node); | 1846 | return maybeSuppressResult(c, scope, result_used, cast_node); |
| 1852 | } | 1847 | } |
| 1853 | 1848 | ||
| 1854 | /// Clang reports the alignment in bits, we use bytes | 1849 | /// The alignment of a variable or field |
| 1855 | /// Clang uses 0 for "no alignment specified", we use null | 1850 | const ClangAlignment = struct { |
| 1856 | fn zigAlignment(bit_alignment: c_uint) ?c_uint { | 1851 | /// Clang reports the alignment in bits, we use bytes |
| 1857 | if (bit_alignment == 0) return null; | 1852 | /// Clang uses 0 for "no alignment specified", we use null |
| 1858 | return bit_alignment / 8; | 1853 | bit_alignment: c_uint, |
| 1859 | } | 1854 | /// If the field or variable is marked as 'packed' |
| 1855 | /// | ||
| 1856 | /// According to the GCC variable attribute docs, this impacts alignment | ||
| 1857 | /// https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html | ||
| 1858 | /// | ||
| 1859 | /// > The packed attribute specifies that a structure member | ||
| 1860 | /// > should have the smallest possible alignment | ||
| 1861 | /// | ||
| 1862 | /// Note also that specifying the 'packed' attribute on a structure | ||
| 1863 | /// implicitly packs all its fields (making their alignment 1). | ||
| 1864 | /// | ||
| 1865 | /// This will be null if the AST node doesn't support packing (functions) | ||
| 1866 | is_packed: ?bool, | ||
| 1867 | |||
| 1868 | /// Get the alignment for a field, optionally taking into account the parent record | ||
| 1869 | pub fn forField(c: *const Context, field: *const clang.FieldDecl, parent: ?*const clang.RecordDecl) ClangAlignment { | ||
| 1870 | const parent_packed = if (parent) |record| record.getPackedAttribute() else false; | ||
| 1871 | // NOTE: According to GCC docs, parent attribute packed implies child attribute packed | ||
| 1872 | return ClangAlignment{ | ||
| 1873 | .bit_alignment = field.getAlignedAttribute(c.clang_context), | ||
| 1874 | .is_packed = field.getPackedAttribute() or parent_packed, | ||
| 1875 | }; | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | pub fn forVar(c: *const Context, var_decl: *const clang.VarDecl) ClangAlignment { | ||
| 1879 | return ClangAlignment{ | ||
| 1880 | .bit_alignment = var_decl.getAlignedAttribute(c.clang_context), | ||
| 1881 | .is_packed = var_decl.getPackedAttribute(), | ||
| 1882 | }; | ||
| 1883 | } | ||
| 1884 | |||
| 1885 | pub fn forFunc(c: *const Context, fun: *const clang.FunctionDecl) ClangAlignment { | ||
| 1886 | return ClangAlignment{ | ||
| 1887 | .bit_alignment = fun.getAlignedAttribute(c.clang_context), | ||
| 1888 | .is_packed = null, // not supported by GCC/clang (or meaningful), | ||
| 1889 | }; | ||
| 1890 | } | ||
| 1891 | |||
| 1892 | /// Translate the clang alignment info into a zig alignment | ||
| 1893 | /// | ||
| 1894 | /// Returns null if there is no special alignment info | ||
| 1895 | pub fn zigAlignment(self: ClangAlignment) ?c_uint { | ||
| 1896 | if (self.bit_alignment != 0) { | ||
| 1897 | return self.bit_alignment / 8; | ||
| 1898 | } else if (self.is_packed orelse false) { | ||
| 1899 | return 1; | ||
| 1900 | } else { | ||
| 1901 | return null; | ||
| 1902 | } | ||
| 1903 | } | ||
| 1904 | }; | ||
| 1860 | 1905 | ||
| 1861 | fn transDeclStmtOne( | 1906 | fn transDeclStmtOne( |
| 1862 | c: *Context, | 1907 | c: *Context, |
| ... | @@ -1910,7 +1955,7 @@ fn transDeclStmtOne( | ... | @@ -1910,7 +1955,7 @@ fn transDeclStmtOne( |
| 1910 | .is_export = false, | 1955 | .is_export = false, |
| 1911 | .is_threadlocal = var_decl.getTLSKind() != .None, | 1956 | .is_threadlocal = var_decl.getTLSKind() != .None, |
| 1912 | .linksection_string = null, | 1957 | .linksection_string = null, |
| 1913 | .alignment = zigAlignment(var_decl.getAlignedAttribute(c.clang_context)), | 1958 | .alignment = ClangAlignment.forVar(c, var_decl).zigAlignment(), |
| 1914 | .name = var_name, | 1959 | .name = var_name, |
| 1915 | .type = type_node, | 1960 | .type = type_node, |
| 1916 | .init = init_node, | 1961 | .init = init_node, |
| ... | @@ -5054,7 +5099,7 @@ fn finishTransFnProto( | ... | @@ -5054,7 +5099,7 @@ fn finishTransFnProto( |
| 5054 | break :blk null; | 5099 | break :blk null; |
| 5055 | }; | 5100 | }; |
| 5056 | 5101 | ||
| 5057 | const alignment = if (fn_decl) |decl| zigAlignment(decl.getAlignedAttribute(c.clang_context)) else null; | 5102 | const alignment = if (fn_decl) |decl| ClangAlignment.forFunc(c, decl).zigAlignment() else null; |
| 5058 | 5103 | ||
| 5059 | const explicit_callconv = if ((is_inline or is_export or is_extern) and cc == .C) null else cc; | 5104 | const explicit_callconv = if ((is_inline or is_export or is_extern) and cc == .C) null else cc; |
| 5060 | 5105 |
src/zig_clang.cpp+11-4| ... | @@ -1941,10 +1941,7 @@ const char* ZigClangVarDecl_getSectionAttribute(const struct ZigClangVarDecl *se | ... | @@ -1941,10 +1941,7 @@ const char* ZigClangVarDecl_getSectionAttribute(const struct ZigClangVarDecl *se |
| 1941 | 1941 | ||
| 1942 | bool ZigClangRecordDecl_getPackedAttribute(const ZigClangRecordDecl *zig_record_decl) { | 1942 | bool ZigClangRecordDecl_getPackedAttribute(const ZigClangRecordDecl *zig_record_decl) { |
| 1943 | const clang::RecordDecl *record_decl = reinterpret_cast<const clang::RecordDecl *>(zig_record_decl); | 1943 | const clang::RecordDecl *record_decl = reinterpret_cast<const clang::RecordDecl *>(zig_record_decl); |
| 1944 | if (record_decl->getAttr<clang::PackedAttr>()) { | 1944 | return record_decl->hasAttr<clang::PackedAttr>(); |
| 1945 | return true; | ||
| 1946 | } | ||
| 1947 | return false; | ||
| 1948 | } | 1945 | } |
| 1949 | 1946 | ||
| 1950 | unsigned ZigClangVarDecl_getAlignedAttribute(const struct ZigClangVarDecl *self, const ZigClangASTContext* ctx) { | 1947 | unsigned ZigClangVarDecl_getAlignedAttribute(const struct ZigClangVarDecl *self, const ZigClangASTContext* ctx) { |
| ... | @@ -1985,6 +1982,16 @@ unsigned ZigClangFunctionDecl_getAlignedAttribute(const struct ZigClangFunctionD | ... | @@ -1985,6 +1982,16 @@ unsigned ZigClangFunctionDecl_getAlignedAttribute(const struct ZigClangFunctionD |
| 1985 | return 0; | 1982 | return 0; |
| 1986 | } | 1983 | } |
| 1987 | 1984 | ||
| 1985 | bool ZigClangVarDecl_getPackedAttribute(const struct ZigClangVarDecl *self) { | ||
| 1986 | auto casted_self = reinterpret_cast<const clang::VarDecl *>(self); | ||
| 1987 | return casted_self->hasAttr<clang::PackedAttr>(); | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | bool ZigClangFieldDecl_getPackedAttribute(const struct ZigClangFieldDecl *self) { | ||
| 1991 | auto casted_self = reinterpret_cast<const clang::FieldDecl *>(self); | ||
| 1992 | return casted_self->hasAttr<clang::PackedAttr>(); | ||
| 1993 | } | ||
| 1994 | |||
| 1988 | ZigClangQualType ZigClangParmVarDecl_getOriginalType(const struct ZigClangParmVarDecl *self) { | 1995 | ZigClangQualType ZigClangParmVarDecl_getOriginalType(const struct ZigClangParmVarDecl *self) { |
| 1989 | return bitcast(reinterpret_cast<const clang::ParmVarDecl *>(self)->getOriginalType()); | 1996 | return bitcast(reinterpret_cast<const clang::ParmVarDecl *>(self)->getOriginalType()); |
| 1990 | } | 1997 | } |
src/zig_clang.h+2| ... | @@ -1101,6 +1101,8 @@ ZIG_EXTERN_C const struct ZigClangFunctionDecl *ZigClangVarDecl_getCleanupAttrib | ... | @@ -1101,6 +1101,8 @@ ZIG_EXTERN_C const struct ZigClangFunctionDecl *ZigClangVarDecl_getCleanupAttrib |
| 1101 | ZIG_EXTERN_C unsigned ZigClangVarDecl_getAlignedAttribute(const struct ZigClangVarDecl *self, const ZigClangASTContext* ctx); | 1101 | ZIG_EXTERN_C unsigned ZigClangVarDecl_getAlignedAttribute(const struct ZigClangVarDecl *self, const ZigClangASTContext* ctx); |
| 1102 | ZIG_EXTERN_C unsigned ZigClangFunctionDecl_getAlignedAttribute(const struct ZigClangFunctionDecl *self, const ZigClangASTContext* ctx); | 1102 | ZIG_EXTERN_C unsigned ZigClangFunctionDecl_getAlignedAttribute(const struct ZigClangFunctionDecl *self, const ZigClangASTContext* ctx); |
| 1103 | ZIG_EXTERN_C unsigned ZigClangFieldDecl_getAlignedAttribute(const struct ZigClangFieldDecl *self, const ZigClangASTContext* ctx); | 1103 | ZIG_EXTERN_C unsigned ZigClangFieldDecl_getAlignedAttribute(const struct ZigClangFieldDecl *self, const ZigClangASTContext* ctx); |
| 1104 | ZIG_EXTERN_C bool ZigClangVarDecl_getPackedAttribute(const struct ZigClangVarDecl *self); | ||
| 1105 | ZIG_EXTERN_C bool ZigClangFieldDecl_getPackedAttribute(const struct ZigClangFieldDecl *self); | ||
| 1104 | 1106 | ||
| 1105 | ZIG_EXTERN_C const struct ZigClangStringLiteral *ZigClangFileScopeAsmDecl_getAsmString(const struct ZigClangFileScopeAsmDecl *self); | 1107 | ZIG_EXTERN_C const struct ZigClangStringLiteral *ZigClangFileScopeAsmDecl_getAsmString(const struct ZigClangFileScopeAsmDecl *self); |
| 1106 | 1108 |
test/run_translated_c.zig+12-14| ... | @@ -250,20 +250,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { | ... | @@ -250,20 +250,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 250 | \\} | 250 | \\} |
| 251 | , ""); | 251 | , ""); |
| 252 | 252 | ||
| 253 | if (@import("builtin").zig_backend == .stage1) { | 253 | cases.add("struct initializer - packed", |
| 254 | cases.add("struct initializer - packed", | 254 | \\#define _NO_CRT_STDIO_INLINE 1 |
| 255 | \\#define _NO_CRT_STDIO_INLINE 1 | 255 | \\#include <stdint.h> |
| 256 | \\#include <stdint.h> | 256 | \\#include <stdlib.h> |
| 257 | \\#include <stdlib.h> | 257 | \\struct s {uint8_t x,y; |
| 258 | \\struct s {uint8_t x,y; | 258 | \\ uint32_t z;} __attribute__((packed)) s0 = {1, 2}; |
| 259 | \\ uint32_t z;} __attribute__((packed)) s0 = {1, 2}; | 259 | \\int main() { |
| 260 | \\int main() { | 260 | \\ /* sizeof nor offsetof currently supported */ |
| 261 | \\ /* sizeof nor offsetof currently supported */ | 261 | \\ if (((intptr_t)&s0.z - (intptr_t)&s0.x) != 2) abort(); |
| 262 | \\ if (((intptr_t)&s0.z - (intptr_t)&s0.x) != 2) abort(); | 262 | \\ return 0; |
| 263 | \\ return 0; | 263 | \\} |
| 264 | \\} | 264 | , ""); |
| 265 | , ""); | ||
| 266 | } | ||
| 267 | 265 | ||
| 268 | cases.add("cast signed array index to unsigned", | 266 | cases.add("cast signed array index to unsigned", |
| 269 | \\#include <stdlib.h> | 267 | \\#include <stdlib.h> |
test/translate_c.zig+82-16| ... | @@ -728,22 +728,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -728,22 +728,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 728 | \\} | 728 | \\} |
| 729 | }); | 729 | }); |
| 730 | 730 | ||
| 731 | if (builtin.zig_backend == .stage1) { | 731 | cases.add("struct initializer - packed", |
| 732 | cases.add("struct initializer - packed", | 732 | \\struct {int x,y,z;} __attribute__((packed)) s0 = {1, 2}; |
| 733 | \\struct {int x,y,z;} __attribute__((packed)) s0 = {1, 2}; | 733 | , &[_][]const u8{ |
| 734 | , &[_][]const u8{ | 734 | \\const struct_unnamed_1 = extern struct { |
| 735 | \\const struct_unnamed_1 = packed struct { | 735 | \\ x: c_int align(1), |
| 736 | \\ x: c_int, | 736 | \\ y: c_int align(1), |
| 737 | \\ y: c_int, | 737 | \\ z: c_int align(1), |
| 738 | \\ z: c_int, | 738 | \\}; |
| 739 | \\}; | 739 | \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{ |
| 740 | \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{ | 740 | \\ .x = @as(c_int, 1), |
| 741 | \\ .x = @as(c_int, 1), | 741 | \\ .y = @as(c_int, 2), |
| 742 | \\ .y = @as(c_int, 2), | 742 | \\ .z = 0, |
| 743 | \\ .z = 0, | 743 | \\}; |
| 744 | \\}; | 744 | }); |
| 745 | }); | ||
| 746 | } | ||
| 747 | 745 | ||
| 748 | // Test case temporarily disabled: | 746 | // Test case temporarily disabled: |
| 749 | // https://github.com/ziglang/zig/issues/12055 | 747 | // https://github.com/ziglang/zig/issues/12055 |
| ... | @@ -1393,6 +1391,74 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1393,6 +1391,74 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1393 | \\pub const Foo = union_Foo; | 1391 | \\pub const Foo = union_Foo; |
| 1394 | }); | 1392 | }); |
| 1395 | 1393 | ||
| 1394 | cases.add("packed union - simple", | ||
| 1395 | \\union Foo { | ||
| 1396 | \\ char x; | ||
| 1397 | \\ double y; | ||
| 1398 | \\} __attribute__((packed)); | ||
| 1399 | , &[_][]const u8{ | ||
| 1400 | \\pub const union_Foo = extern union { | ||
| 1401 | \\ x: u8 align(1), | ||
| 1402 | \\ y: f64 align(1), | ||
| 1403 | \\}; | ||
| 1404 | , | ||
| 1405 | \\pub const Foo = union_Foo; | ||
| 1406 | }); | ||
| 1407 | |||
| 1408 | cases.add("packed union - nested unpacked", | ||
| 1409 | \\union Foo{ | ||
| 1410 | \\ char x; | ||
| 1411 | \\ double y; | ||
| 1412 | \\ struct { | ||
| 1413 | \\ char a; | ||
| 1414 | \\ int b; | ||
| 1415 | \\ } z; | ||
| 1416 | \\} __attribute__((packed)); | ||
| 1417 | , &[_][]const u8{ | ||
| 1418 | // NOTE: The nested struct is *not* packed/aligned, | ||
| 1419 | // even though the parent struct is | ||
| 1420 | // this is consistent with GCC docs | ||
| 1421 | \\const struct_unnamed_1 = extern struct { | ||
| 1422 | \\ a: u8, | ||
| 1423 | \\ b: c_int, | ||
| 1424 | \\}; | ||
| 1425 | , | ||
| 1426 | \\pub const union_Foo = extern union { | ||
| 1427 | \\ x: u8 align(1), | ||
| 1428 | \\ y: f64 align(1), | ||
| 1429 | \\ z: struct_unnamed_1 align(1), | ||
| 1430 | \\}; | ||
| 1431 | , | ||
| 1432 | \\pub const Foo = union_Foo; | ||
| 1433 | }); | ||
| 1434 | |||
| 1435 | cases.add("packed union - nested packed", | ||
| 1436 | \\union Foo{ | ||
| 1437 | \\ char x; | ||
| 1438 | \\ double y; | ||
| 1439 | \\ struct { | ||
| 1440 | \\ char a; | ||
| 1441 | \\ int b; | ||
| 1442 | \\ } __attribute__((packed)) z; | ||
| 1443 | \\} __attribute__((packed)); | ||
| 1444 | , &[_][]const u8{ | ||
| 1445 | // in order for the nested struct to be packed, it must | ||
| 1446 | // have an independent packed declaration on | ||
| 1447 | // the nested type (see GCC docs for details) | ||
| 1448 | \\const struct_unnamed_1 = extern struct { | ||
| 1449 | \\ a: u8 align(1), | ||
| 1450 | \\ b: c_int align(1), | ||
| 1451 | \\}; | ||
| 1452 | , | ||
| 1453 | \\pub const union_Foo = extern union { | ||
| 1454 | \\ x: u8 align(1), | ||
| 1455 | \\ y: f64 align(1), | ||
| 1456 | \\ z: struct_unnamed_1 align(1), | ||
| 1457 | \\}; | ||
| 1458 | , | ||
| 1459 | \\pub const Foo = union_Foo; | ||
| 1460 | }); | ||
| 1461 | |||
| 1396 | cases.add("string literal", | 1462 | cases.add("string literal", |
| 1397 | \\const char *foo(void) { | 1463 | \\const char *foo(void) { |
| 1398 | \\ return "bar"; | 1464 | \\ return "bar"; |