authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-02 20:26:33+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-03 01:04:46+03:00
logb83c037f9ffd7a4285de41c95827615fcbdbbf2f
tree25b6f3ce249c9b77603a47647fc9d658980da08a
parent6aee07c1446f3ce98d326998c887fbca3b7fd945

Sema: only ABI sized packed structs are extern compatible


9 files changed, 116 insertions(+), 125 deletions(-)

src/Sema.zig+31-16
......@@ -5056,7 +5056,7 @@ pub fn analyzeExport(
50565056 try mod.ensureDeclAnalyzed(exported_decl_index);
50575057 const exported_decl = mod.declPtr(exported_decl_index);
50585058
5059 if (!sema.validateExternType(exported_decl.ty, .other)) {
5059 if (!try sema.validateExternType(block, src, exported_decl.ty, .other)) {
50605060 const msg = msg: {
50615061 const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)});
50625062 errdefer msg.destroy(sema.gpa);
......@@ -7896,7 +7896,7 @@ fn funcCommon(
78967896 };
78977897 return sema.failWithOwnedErrorMsg(msg);
78987898 }
7899 if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !sema.validateExternType(return_type, .ret_ty)) {
7899 if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !try sema.validateExternType(block, ret_ty_src, return_type, .ret_ty)) {
79007900 const msg = msg: {
79017901 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
79027902 return_type.fmt(sema.mod), @tagName(cc_workaround),
......@@ -8115,7 +8115,7 @@ fn analyzeParameter(
81158115 };
81168116 return sema.failWithOwnedErrorMsg(msg);
81178117 }
8118 if (!Type.fnCallingConventionAllowsZigTypes(cc) and !sema.validateExternType(param.ty, .param_ty)) {
8118 if (!Type.fnCallingConventionAllowsZigTypes(cc) and !try sema.validateExternType(block, param_src, param.ty, .param_ty)) {
81198119 const msg = msg: {
81208120 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{
81218121 param.ty.fmt(sema.mod), @tagName(cc),
......@@ -15583,7 +15583,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1558315583 } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) {
1558415584 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
1558515585 } else if (inst_data.size == .C) {
15586 if (!sema.validateExternType(elem_ty, .other)) {
15586 if (!try sema.validateExternType(block, elem_ty_src, elem_ty, .other)) {
1558715587 const msg = msg: {
1558815588 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
1558915589 errdefer msg.destroy(sema.gpa);
......@@ -16681,7 +16681,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
1668116681 } else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) {
1668216682 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
1668316683 } else if (ptr_size == .C) {
16684 if (!sema.validateExternType(elem_ty, .other)) {
16684 if (!try sema.validateExternType(block, src, elem_ty, .other)) {
1668516685 const msg = msg: {
1668616686 const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
1668716687 errdefer msg.destroy(sema.gpa);
......@@ -20549,7 +20549,14 @@ const ExternPosition = enum {
2054920549
2055020550/// Returns true if `ty` is allowed in extern types.
2055120551/// Does *NOT* require `ty` to be resolved in any way.
20552fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool {
20552/// Calls `resolveTypeLayout` for packed containers.
20553fn validateExternType(
20554 sema: *Sema,
20555 block: *Block,
20556 src: LazySrcLoc,
20557 ty: Type,
20558 position: ExternPosition,
20559) !bool {
2055320560 switch (ty.zigTypeTag()) {
2055420561 .Type,
2055520562 .ComptimeFloat,
......@@ -20577,17 +20584,25 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool {
2057720584 .Fn => return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()),
2057820585 .Enum => {
2057920586 var buf: Type.Payload.Bits = undefined;
20580 return sema.validateExternType(ty.intTagType(&buf), position);
20587 return sema.validateExternType(block, src, ty.intTagType(&buf), position);
2058120588 },
2058220589 .Struct, .Union => switch (ty.containerLayout()) {
20583 .Extern, .Packed => return true,
20584 else => return false,
20590 .Extern => return true,
20591 .Packed => {
20592 const target = sema.mod.getTarget();
20593 const bit_size = try ty.bitSizeAdvanced(target, sema.kit(block, src));
20594 switch (bit_size) {
20595 8, 16, 32, 64, 128 => return true,
20596 else => return false,
20597 }
20598 },
20599 .Auto => return false,
2058520600 },
2058620601 .Array => {
2058720602 if (position == .ret_ty or position == .param_ty) return false;
20588 return sema.validateExternType(ty.elemType2(), .other);
20603 return sema.validateExternType(block, src, ty.elemType2(), .other);
2058920604 },
20590 .Vector => return sema.validateExternType(ty.elemType2(), .other),
20605 .Vector => return sema.validateExternType(block, src, ty.elemType2(), .other),
2059120606 .Optional => return ty.isPtrLikeOptional(),
2059220607 }
2059320608}
......@@ -20639,8 +20654,8 @@ fn explainWhyTypeIsNotExtern(
2063920654 try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
2064020655 try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
2064120656 },
20642 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}),
20643 .Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}),
20657 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}),
20658 .Union => try mod.errNoteNonLazy(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}),
2064420659 .Array => {
2064520660 if (position == .ret_ty) {
2064620661 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{});
......@@ -24119,7 +24134,7 @@ fn coerceVarArgParam(
2411924134 };
2412024135
2412124136 const coerced_ty = sema.typeOf(coerced);
24122 if (!sema.validateExternType(coerced_ty, .other)) {
24137 if (!try sema.validateExternType(block, inst_src, coerced_ty, .other)) {
2412324138 const msg = msg: {
2412424139 const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)});
2412524140 errdefer msg.destroy(sema.gpa);
......@@ -28321,7 +28336,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
2832128336 };
2832228337 return sema.failWithOwnedErrorMsg(msg);
2832328338 }
28324 if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) {
28339 if (struct_obj.layout == .Extern and !try sema.validateExternType(&block_scope, src, field.ty, .other)) {
2832528340 const msg = msg: {
2832628341 const tree = try sema.getAstTree(&block_scope);
2832728342 const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i);
......@@ -28658,7 +28673,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2865828673 };
2865928674 return sema.failWithOwnedErrorMsg(msg);
2866028675 }
28661 if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) {
28676 if (union_obj.layout == .Extern and !try sema.validateExternType(&block_scope, src, field_ty, .union_field)) {
2866228677 const msg = msg: {
2866328678 const tree = try sema.getAstTree(&block_scope);
2866428679 const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i);
src/arch/wasm/abi.zig+8
......@@ -23,6 +23,10 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
2323 if (!ty.hasRuntimeBitsIgnoreComptime()) return none;
2424 switch (ty.zigTypeTag()) {
2525 .Struct => {
26 if (ty.containerLayout() == .Packed) {
27 if (ty.bitSize(target) <= 64) return direct;
28 return .{ .direct, .direct };
29 }
2630 // When the struct type is non-scalar
2731 if (ty.structFieldCount() > 1) return memory;
2832 // When the struct's alignment is non-natural
......@@ -57,6 +61,10 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
5761 return direct;
5862 },
5963 .Union => {
64 if (ty.containerLayout() == .Packed) {
65 if (ty.bitSize(target) <= 64) return direct;
66 return .{ .direct, .direct };
67 }
6068 const layout = ty.unionGetLayout(target);
6169 std.debug.assert(layout.tag_size == 0);
6270 if (ty.unionFields().count() > 1) return memory;
src/arch/x86_64/abi.zig+12
......@@ -174,6 +174,12 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
174174 // "If the size of the aggregate exceeds a single eightbyte, each is classified
175175 // separately.".
176176 const ty_size = ty.abiSize(target);
177 if (ty.containerLayout() == .Packed) {
178 assert(ty_size <= 128);
179 result[0] = .integer;
180 if (ty_size > 64) result[1] = .integer;
181 return result;
182 }
177183 if (ty_size > 64)
178184 return memory_class;
179185
......@@ -284,6 +290,12 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
284290 // "If the size of the aggregate exceeds a single eightbyte, each is classified
285291 // separately.".
286292 const ty_size = ty.abiSize(target);
293 if (ty.containerLayout() == .Packed) {
294 assert(ty_size <= 128);
295 result[0] = .integer;
296 if (ty_size > 64) result[1] = .integer;
297 return result;
298 }
287299 if (ty_size > 64)
288300 return memory_class;
289301
src/codegen/llvm.zig+23-34
......@@ -9674,22 +9674,7 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
96749674 }
96759675 },
96769676 .C => {
9677 const is_scalar = switch (fn_info.return_type.zigTypeTag()) {
9678 .Void,
9679 .Bool,
9680 .NoReturn,
9681 .Int,
9682 .Float,
9683 .Pointer,
9684 .Optional,
9685 .ErrorSet,
9686 .Enum,
9687 .AnyFrame,
9688 .Vector,
9689 => true,
9690
9691 else => false,
9692 };
9677 const is_scalar = isScalar(fn_info.return_type);
96939678 switch (target.cpu.arch) {
96949679 .mips, .mipsel => return dg.lowerType(fn_info.return_type),
96959680 .x86_64 => switch (target.os.tag) {
......@@ -9837,24 +9822,7 @@ const ParamTypeIterator = struct {
98379822 @panic("TODO implement async function lowering in the LLVM backend");
98389823 },
98399824 .C => {
9840 const is_scalar = switch (ty.zigTypeTag()) {
9841 .Void,
9842 .Bool,
9843 .NoReturn,
9844 .Int,
9845 .Float,
9846 .Pointer,
9847 .Optional,
9848 .ErrorSet,
9849 .Enum,
9850 .AnyFrame,
9851 .Vector,
9852 => true,
9853 .Struct => ty.containerLayout() == .Packed,
9854 .Union => ty.containerLayout() == .Packed,
9855
9856 else => false,
9857 };
9825 const is_scalar = isScalar(ty);
98589826 switch (it.target.cpu.arch) {
98599827 .riscv32, .riscv64 => {
98609828 it.zig_index += 1;
......@@ -10108,6 +10076,27 @@ fn isByRef(ty: Type) bool {
1010810076 }
1010910077}
1011010078
10079fn isScalar(ty: Type) bool {
10080 return switch (ty.zigTypeTag()) {
10081 .Void,
10082 .Bool,
10083 .NoReturn,
10084 .Int,
10085 .Float,
10086 .Pointer,
10087 .Optional,
10088 .ErrorSet,
10089 .Enum,
10090 .AnyFrame,
10091 .Vector,
10092 => true,
10093
10094 .Struct => ty.containerLayout() == .Packed,
10095 .Union => ty.containerLayout() == .Packed,
10096 else => false,
10097 };
10098}
10099
1011110100/// This function returns true if we expect LLVM to lower x86_fp80 correctly
1011210101/// and false if we expect LLVM to crash if it counters an x86_fp80 type.
1011310102fn backendSupportsF80(target: std.Target) bool {
test/c_abi/cfuncs.c+29-49
......@@ -86,24 +86,8 @@ struct MedStructMixed {
8686void zig_med_struct_mixed(struct MedStructMixed);
8787struct MedStructMixed zig_ret_med_struct_mixed();
8888
89struct SmallPackedStruct {
90 uint8_t a: 2;
91 uint8_t b: 2;
92 uint8_t c: 2;
93 uint8_t d: 2;
94 uint8_t e: 1;
95};
96
97struct BigPackedStruct {
98 uint64_t a: 64;
99 uint64_t b: 64;
100 uint64_t c: 64;
101 uint64_t d: 64;
102 uint8_t e: 8;
103};
104
105//void zig_small_packed_struct(struct SmallPackedStruct); // #1481
106void zig_big_packed_struct(struct BigPackedStruct);
89void zig_small_packed_struct(uint8_t);
90void zig_big_packed_struct(__int128);
10791
10892struct SplitStructInts {
10993 uint64_t a;
......@@ -176,13 +160,19 @@ void run_c_tests(void) {
176160 }
177161
178162 {
179 struct BigPackedStruct s = {1, 2, 3, 4, 5};
163 __int128 s = 0;
164 s |= 1 << 0;
165 s |= (__int128)2 << 64;
180166 zig_big_packed_struct(s);
181167 }
182168
183169 {
184 struct SmallPackedStruct s = {0, 1, 2, 3, 1};
185 //zig_small_packed_struct(s);
170 uint8_t s = 0;
171 s |= 0 << 0;
172 s |= 1 << 2;
173 s |= 2 << 4;
174 s |= 3 << 6;
175 zig_small_packed_struct(s);
186176 }
187177
188178 {
......@@ -378,42 +368,32 @@ void c_split_struct_mixed(struct SplitStructMixed x) {
378368 assert_or_panic(y.c == 1337.0f);
379369}
380370
381struct SmallPackedStruct c_ret_small_packed_struct() {
382 struct SmallPackedStruct s = {
383 .a = 0,
384 .b = 1,
385 .c = 2,
386 .d = 3,
387 .e = 1,
388 };
371uint8_t c_ret_small_packed_struct() {
372 uint8_t s = 0;
373 s |= 0 << 0;
374 s |= 1 << 2;
375 s |= 2 << 4;
376 s |= 3 << 6;
389377 return s;
390378}
391379
392void c_small_packed_struct(struct SmallPackedStruct x) {
393 assert_or_panic(x.a == 0);
394 assert_or_panic(x.a == 1);
395 assert_or_panic(x.a == 2);
396 assert_or_panic(x.a == 3);
397 assert_or_panic(x.e == 1);
380void c_small_packed_struct(uint8_t x) {
381 assert_or_panic(((x >> 0) & 0x3) == 0);
382 assert_or_panic(((x >> 2) & 0x3) == 1);
383 assert_or_panic(((x >> 4) & 0x3) == 2);
384 assert_or_panic(((x >> 6) & 0x3) == 3);
398385}
399386
400struct BigPackedStruct c_ret_big_packed_struct() {
401 struct BigPackedStruct s = {
402 .a = 1,
403 .b = 2,
404 .c = 3,
405 .d = 4,
406 .e = 5,
407 };
387__int128 c_ret_big_packed_struct() {
388 __int128 s = 0;
389 s |= 1 << 0;
390 s |= (__int128)2 << 64;
408391 return s;
409392}
410393
411void c_big_packed_struct(struct BigPackedStruct x) {
412 assert_or_panic(x.a == 1);
413 assert_or_panic(x.b == 2);
414 assert_or_panic(x.c == 3);
415 assert_or_panic(x.d == 4);
416 assert_or_panic(x.e == 5);
394void c_big_packed_struct(__int128 x) {
395 assert_or_panic(((x >> 0) & 0xFFFFFFFFFFFFFFFF) == 1);
396 assert_or_panic(((x >> 64) & 0xFFFFFFFFFFFFFFFF) == 2);
417397}
418398
419399struct SplitStructMixed c_ret_split_struct_mixed() {
test/c_abi/main.zig+10-23
......@@ -263,37 +263,30 @@ const SmallPackedStruct = packed struct {
263263 b: u2,
264264 c: u2,
265265 d: u2,
266 e: bool,
267266};
268const c_small_packed_struct: fn (SmallPackedStruct) callconv(.C) void = @compileError("TODO: #1481");
267extern fn c_small_packed_struct(SmallPackedStruct) void;
269268extern fn c_ret_small_packed_struct() SmallPackedStruct;
270269
271// waiting on #1481
272//export fn zig_small_packed_struct(x: SmallPackedStruct) void {
273// expect(x.a == 0) catch @panic("test failure");
274// expect(x.b == 1) catch @panic("test failure");
275// expect(x.c == 2) catch @panic("test failure");
276// expect(x.d == 3) catch @panic("test failure");
277// expect(x.e) catch @panic("test failure");
278//}
270export fn zig_small_packed_struct(x: SmallPackedStruct) void {
271 expect(x.a == 0) catch @panic("test failure");
272 expect(x.b == 1) catch @panic("test failure");
273 expect(x.c == 2) catch @panic("test failure");
274 expect(x.d == 3) catch @panic("test failure");
275}
279276
280277test "C ABI small packed struct" {
281 var s = SmallPackedStruct{ .a = 0, .b = 1, .c = 2, .d = 3, .e = true };
282 _ = s; //c_small_packed_struct(s); // waiting on #1481
278 var s = SmallPackedStruct{ .a = 0, .b = 1, .c = 2, .d = 3 };
279 c_small_packed_struct(s);
283280 var s2 = c_ret_small_packed_struct();
284281 try expect(s2.a == 0);
285282 try expect(s2.b == 1);
286283 try expect(s2.c == 2);
287284 try expect(s2.d == 3);
288 try expect(s2.e);
289285}
290286
291287const BigPackedStruct = packed struct {
292288 a: u64,
293289 b: u64,
294 c: u64,
295 d: u64,
296 e: u8,
297290};
298291extern fn c_big_packed_struct(BigPackedStruct) void;
299292extern fn c_ret_big_packed_struct() BigPackedStruct;
......@@ -301,20 +294,14 @@ extern fn c_ret_big_packed_struct() BigPackedStruct;
301294export fn zig_big_packed_struct(x: BigPackedStruct) void {
302295 expect(x.a == 1) catch @panic("test failure");
303296 expect(x.b == 2) catch @panic("test failure");
304 expect(x.c == 3) catch @panic("test failure");
305 expect(x.d == 4) catch @panic("test failure");
306 expect(x.e == 5) catch @panic("test failure");
307297}
308298
309299test "C ABI big packed struct" {
310 var s = BigPackedStruct{ .a = 1, .b = 2, .c = 3, .d = 4, .e = 5 };
300 var s = BigPackedStruct{ .a = 1, .b = 2 };
311301 c_big_packed_struct(s);
312302 var s2 = c_ret_big_packed_struct();
313303 try expect(s2.a == 1);
314304 try expect(s2.b == 2);
315 try expect(s2.c == 3);
316 try expect(s2.d == 4);
317 try expect(s2.e == 5);
318305}
319306
320307const SplitStructInt = extern struct {
test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig+1-1
......@@ -10,5 +10,5 @@ export fn a() void {
1010// target=native
1111//
1212// :3:19: error: C pointers cannot point to non-C-ABI-compatible type 'tmp.Foo'
13// :3:19: note: only structs with packed or extern layout are extern compatible
13// :3:19: note: only extern structs and ABI sized packed structs are extern compatible
1414// :1:13: note: struct declared here
test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig+1-1
......@@ -10,5 +10,5 @@ export fn entry(foo: Foo) void { _ = foo; }
1010// target=native
1111//
1212// :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C'
13// :6:17: note: only structs with packed or extern layout are extern compatible
13// :6:17: note: only extern structs and ABI sized packed structs are extern compatible
1414// :1:13: note: struct declared here
test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig+1-1
......@@ -10,5 +10,5 @@ export fn entry(foo: Foo) void { _ = foo; }
1010// target=native
1111//
1212// :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C'
13// :6:17: note: only unions with packed or extern layout are extern compatible
13// :6:17: note: only extern unions and ABI sized packed unions are extern compatible
1414// :1:13: note: union declared here