diff --git a/lib/compiler_rt/multi3.zig b/lib/compiler_rt/multi3.zig index 42994a81bda2..9502dc5677ce 100644 --- a/lib/compiler_rt/multi3.zig +++ b/lib/compiler_rt/multi3.zig @@ -59,12 +59,12 @@ const twords = extern union { s: S, const S = if (native_endian == .Little) - struct { + extern struct { low: u64, high: u64, } else - struct { + extern struct { high: u64, low: u64, }; diff --git a/lib/compiler_rt/shift.zig b/lib/compiler_rt/shift.zig index ee8b634fbb2e..e38c3973bc2b 100644 --- a/lib/compiler_rt/shift.zig +++ b/lib/compiler_rt/shift.zig @@ -31,9 +31,9 @@ fn Dwords(comptime T: type, comptime signed_half: bool) type { all: T, s: if (native_endian == .Little) - struct { low: HalfT, high: HalfT } + extern struct { low: HalfT, high: HalfT } else - struct { high: HalfT, low: HalfT }, + extern struct { high: HalfT, low: HalfT }, }; } diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index 0d238d833edc..672f4fa4baa8 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -30,8 +30,8 @@ pub extern "c" fn _kern_get_current_team() i32; pub const sem_t = extern struct { type: i32, u: extern union { - named_sem_id: ?i32, - unnamed_sem: ?i32, + named_sem_id: i32, + unnamed_sem: i32, }, padding: [2]i32, }; diff --git a/lib/std/start.zig b/lib/std/start.zig index cf079863873b..788c979d4826 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -108,7 +108,7 @@ fn callMain2() noreturn { exit2(0); } -fn wasiMain2() noreturn { +fn wasiMain2() callconv(.C) noreturn { switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) { .Void => { root.main(); diff --git a/src/Sema.zig b/src/Sema.zig index 0e7c1c5937f8..d66c447e7c57 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4740,12 +4740,19 @@ pub fn analyzeExport( try mod.ensureDeclAnalyzed(exported_decl_index); const exported_decl = mod.declPtr(exported_decl_index); - // TODO run the same checks as we do for C ABI struct fields - switch (exported_decl.ty.zigTypeTag()) { - .Fn, .Int, .Enum, .Struct, .Union, .Array, .Float, .Pointer, .Optional => {}, - else => return sema.fail(block, src, "unable to export type '{}'", .{ - exported_decl.ty.fmt(sema.mod), - }), + + if (!(try sema.validateExternType(exported_decl.ty, .other))) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), exported_decl.ty, .other); + + try sema.addDeclaredHereNote(msg, exported_decl.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } const gpa = mod.gpa; @@ -13799,7 +13806,20 @@ fn validatePtrTy(sema: *Sema, block: *Block, elem_src: LazySrcLoc, ty: Type) Com } else if (ptr_info.size == .Many and pointee_tag == .Opaque) { return sema.fail(block, elem_src, "unknown-length pointer to opaque not allowed", .{}); } else if (ptr_info.size == .C) { - // TODO check extern type + const elem_ty = ptr_info.pointee_type; + if (!(try sema.validateExternType(elem_ty, .other))) { + const msg = msg: { + const msg = try sema.errMsg(block, elem_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, elem_src, msg, elem_src.toSrcLoc(src_decl), elem_ty, .other); + + try sema.addDeclaredHereNote(msg, elem_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } if (pointee_tag == .Opaque) { return sema.fail(block, elem_src, "C pointers cannot point to opaque types", .{}); } @@ -18098,10 +18118,12 @@ fn explainWhyTypeIsComptime( .NoReturn, .Undefined, .Null, - .Opaque, - .Optional, => return, + .Opaque => { + try mod.errNoteNonLazy(src_loc, msg, "opaque type '{}' has undefined size", .{ty.fmt(sema.mod)}); + }, + .Array, .Vector => { try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType()); }, @@ -18124,6 +18146,10 @@ fn explainWhyTypeIsComptime( try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType()); }, + .Optional => { + var buf: Type.Payload.ElemType = undefined; + try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.optionalChild(&buf)); + }, .ErrorUnion => { try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.errorUnionPayload()); }, @@ -18163,6 +18189,120 @@ fn explainWhyTypeIsComptime( } } +const ExternPosition = enum { + ret_ty, + param_ty, + union_field, + other, +}; + +fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool { + switch (ty.zigTypeTag()) { + .Type, + .ComptimeFloat, + .ComptimeInt, + .EnumLiteral, + .Undefined, + .Null, + .ErrorUnion, + .ErrorSet, + .BoundFn, + .Frame, + => return false, + .Void => return position == .union_field, + .NoReturn => return position == .ret_ty, + .Opaque, + .Bool, + .Float, + .Pointer, + .AnyFrame, + => return true, + .Int => switch (ty.intInfo(sema.mod.getTarget()).bits) { + 8, 16, 32, 64, 128 => return true, + else => return false, + }, + .Fn => return !ty.fnCallingConventionAllowsZigTypes(), + .Enum => { + var buf: Type.Payload.Bits = undefined; + return sema.validateExternType(ty.intTagType(&buf), position); + }, + .Struct, .Union => switch (ty.containerLayout()) { + .Extern, .Packed => return true, + else => return false, + }, + .Array => { + if (position == .ret_ty or position == .param_ty) return false; + return sema.validateExternType(ty.elemType2(), .other); + }, + .Vector => return sema.validateExternType(ty.elemType2(), .other), + .Optional => return ty.isPtrLikeOptional(), + } +} + +fn explainWhyTypeIsNotExtern( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + msg: *Module.ErrorMsg, + src_loc: Module.SrcLoc, + ty: Type, + position: ExternPosition, +) CompileError!void { + const mod = sema.mod; + switch (ty.zigTypeTag()) { + .Opaque, + .Bool, + .Float, + .Pointer, + .AnyFrame, + => return, + + .Type, + .ComptimeFloat, + .ComptimeInt, + .EnumLiteral, + .Undefined, + .Null, + .ErrorUnion, + .ErrorSet, + .BoundFn, + .Frame, + => return, + + .Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}), + .NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), + .Int => if (ty.intInfo(sema.mod.getTarget()).bits > 128) { + try mod.errNoteNonLazy(src_loc, msg, "only integers with less than 128 bits are extern compatible", .{}); + } else { + try mod.errNoteNonLazy(src_loc, msg, "only integers with power of two bits are extern compatible", .{}); + }, + .Fn => switch (ty.fnCallingConvention()) { + .Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}), + .Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}), + .Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}), + else => return, + }, + .Enum => { + var buf: Type.Payload.Bits = undefined; + const tag_ty = ty.intTagType(&buf); + try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)}); + try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, tag_ty, position); + }, + .Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}), + .Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}), + .Array => { + if (position == .ret_ty) { + try mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{}); + } else if (position == .param_ty) { + try mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); + } + try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position); + }, + .Vector => try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position), + .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}), + } +} + pub const PanicId = enum { unreach, unwrap_null, @@ -24006,6 +24146,20 @@ fn resolveStructFully( struct_obj.status = .fully_resolved_wip; for (struct_obj.fields.values()) |field| { try sema.resolveTypeFully(block, src, field.ty); + + if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), field.ty, .other); + + try sema.addDeclaredHereNote(msg, field.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } } struct_obj.status = .fully_resolved; } @@ -24039,6 +24193,20 @@ fn resolveUnionFully( union_obj.status = .fully_resolved_wip; for (union_obj.fields.values()) |field| { try sema.resolveTypeFully(block, src, field.ty); + + if (union_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .union_field))) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), field.ty, .union_field); + + try sema.addDeclaredHereNote(msg, field.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } } union_obj.status = .fully_resolved; } diff --git a/src/type.zig b/src/type.zig index f14ad3a85bf5..765f1da18c12 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3935,7 +3935,6 @@ pub const Type = extern union { /// Returns true if the type is optional and would be lowered to a single pointer /// address value, using 0 for null. Note that this returns true for C pointers. - /// See also `hasOptionalRepr`. pub fn isPtrLikeOptional(self: Type) bool { switch (self.tag()) { .optional_single_const_pointer, @@ -4630,6 +4629,14 @@ pub const Type = extern union { }; } + /// Asserts the type is a function. + pub fn fnCallingConventionAllowsZigTypes(self: Type) bool { + return switch (self.fnCallingConvention()) { + .Unspecified, .Async, .Inline, .PtxKernel => true, + else => false, + }; + } + /// Asserts the type is a function. pub fn fnIsVarArgs(self: Type) bool { return switch (self.tag()) { diff --git a/test/behavior/union.zig b/test/behavior/union.zig index be01b3a048af..caa78be97792 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -84,18 +84,19 @@ test "comptime union field access" { const FooExtern = extern union { int: i32, - str: struct { - slice: []const u8, + str: extern struct { + slice: [*:0]const u8, }, }; test "basic extern unions" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var foo = FooExtern{ .int = 1 }; try expect(foo.int == 1); foo.str.slice = "Well"; - try expect(std.mem.eql(u8, foo.str.slice, "Well")); + try expect(std.mem.eql(u8, std.mem.sliceTo(foo.str.slice, 0), "Well")); } const ExternPtrOrInt = extern union { diff --git a/test/cases/compile_errors/c_pointer_to_void.zig b/test/cases/compile_errors/c_pointer_to_void.zig new file mode 100644 index 000000000000..aeb5f42dec8c --- /dev/null +++ b/test/cases/compile_errors/c_pointer_to_void.zig @@ -0,0 +1,11 @@ +export fn entry() void { + var a: [*c]void = undefined; + _ = a; +} + +// error +// backend=stage2 +// target=native +// +// :1:1: error: C pointers cannot point to non-C-ABI-compatible type 'void' +// :1:1: note: 'void' is a zero bit type; for C 'void' use 'anyopaque' diff --git a/test/cases/compile_errors/exported_enum_without_explicit_integer_tag_type.zig b/test/cases/compile_errors/exported_enum_without_explicit_integer_tag_type.zig new file mode 100644 index 000000000000..606dda4b6e8b --- /dev/null +++ b/test/cases/compile_errors/exported_enum_without_explicit_integer_tag_type.zig @@ -0,0 +1,18 @@ +const E = enum { one, two }; +comptime { + @export(E, .{ .name = "E" }); +} +const e: E = .two; +comptime { + @export(e, .{ .name = "e" }); +} + +// error +// backend=stage2 +// target=native +// +// :3:5: error: unable to export type 'type' +// :7:5: error: unable to export type 'tmp.E' +// :7:5: note: enum tag type 'u1' is not extern compatible +// :7:5: note: only integers with power of two bits are extern compatible +// :1:11: note: enum declared here diff --git a/test/cases/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig b/test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig similarity index 86% rename from test/cases/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig rename to test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig index 8f45a0e5dc2f..17405743895b 100644 --- a/test/cases/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig +++ b/test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig @@ -25,19 +25,21 @@ pub const E = enum { @"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235", @"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244", @"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253", -@"254",@"255" +@"254",@"255", @"256" }; pub const S = extern struct { e: E, }; export fn entry() void { - if (@typeInfo(E).Enum.tag_type != u8) @compileError("did not infer u8 tag type"); const s: S = undefined; _ = s; } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:31:5: error: extern structs cannot contain fields of type 'E' +// :33:8: error: extern structs cannot contain fields of type 'tmp.E' +// :33:8: note: enum tag type 'u9' is not extern compatible +// :33:8: note: only integers with power of two bits are extern compatible +// :1:15: note: enum declared here diff --git a/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig b/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig new file mode 100644 index 000000000000..5fb36b707d4e --- /dev/null +++ b/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig @@ -0,0 +1,17 @@ +pub const E = enum(u31) { A, B, C }; +pub const S = extern struct { + e: E, +}; +export fn entry() void { + const s: S = undefined; + _ = s; +} + +// error +// backend=stage2 +// target=native +// +// :5:8: error: extern structs cannot contain fields of type 'tmp.E' +// :5:8: note: enum tag type 'u31' is not extern compatible +// :5:8: note: only integers with power of two bits are extern compatible +// :1:15: note: enum declared here diff --git a/test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig b/test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig new file mode 100644 index 000000000000..9a6c77363cc1 --- /dev/null +++ b/test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig @@ -0,0 +1,11 @@ +const stroo = extern struct { + moo: ?[*c]u8, +}; +export fn testf(fluff: *stroo) void { _ = fluff; } + +// error +// backend=stage2 +// target=native +// +// :4:8: error: extern structs cannot contain fields of type '?[*c]u8' +// :4:8: note: only pointer like optionals are extern compatible diff --git a/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig b/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig index c6713b755a9c..b6edbc265a5e 100644 --- a/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig +++ b/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig @@ -40,5 +40,6 @@ const Opaque = opaque {}; // :14:8: note: to modify this variable at runtime, it must be given an explicit fixed-size number type // :18:8: error: variable of type '@TypeOf(null)' must be const or comptime // :22:19: error: values of type 'tmp.Opaque' must be comptime known, but operand value is runtime known +// :22:19: note: opaque type 'tmp.Opaque' has undefined size // :26:8: error: variable of type 'type' must be const or comptime // :26:8: note: types are not available at runtime diff --git a/test/cases/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig b/test/cases/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig deleted file mode 100644 index c432bedd6b71..000000000000 --- a/test/cases/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig +++ /dev/null @@ -1,15 +0,0 @@ -const E = enum { one, two }; -comptime { - @export(E, .{ .name = "E" }); -} -const e: E = .two; -comptime { - @export(e, .{ .name = "e" }); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:13: error: exported enum without explicit integer tag type -// tmp.zig:7:13: error: exported enum value without explicit integer tag type diff --git a/test/cases/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig b/test/cases/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig deleted file mode 100644 index aa508056a192..000000000000 --- a/test/cases/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig +++ /dev/null @@ -1,14 +0,0 @@ -pub const E = enum(u31) { A, B, C }; -pub const S = extern struct { - e: E, -}; -export fn entry() void { - const s: S = undefined; - _ = s; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:5: error: extern structs cannot contain fields of type 'E' diff --git a/test/cases/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig b/test/cases/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig deleted file mode 100644 index dc2e2690b8cf..000000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig +++ /dev/null @@ -1,10 +0,0 @@ -const stroo = extern struct { - moo: ?[*c]u8, -}; -export fn testf(fluff: *stroo) void { _ = fluff; } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: extern structs cannot contain fields of type '?[*c]u8' diff --git a/test/cases/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig b/test/cases/compile_errors/stage1/optional_pointer_to_void_in_extern_struct.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig rename to test/cases/compile_errors/stage1/optional_pointer_to_void_in_extern_struct.zig