aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2022-07-11 17:54:53 +0300
committerVeikka Tuominen <git@vexu.eu>2022-07-11 18:47:29 +0300
commitd00da05ecbeca36e8ca25dcb1238bf98b38081a5 (patch)
tree3c47892fb4a3485ba7a3400a523729b16484646b
parent3ceb27c8401df17e5a7f522d68104da79501b5b6 (diff)
downloadzig-d00da05ecbeca36e8ca25dcb1238bf98b38081a5.tar.gz
zig-d00da05ecbeca36e8ca25dcb1238bf98b38081a5.zip
Sema: validate extern types
-rw-r--r--lib/std/start.zig2
-rw-r--r--src/Sema.zig175
-rw-r--r--src/type.zig9
-rw-r--r--test/cases/compile_errors/c_pointer_to_void.zig11
-rw-r--r--test/cases/compile_errors/exported_enum_without_explicit_integer_tag_type.zig18
-rw-r--r--test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig (renamed from test/cases/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig)10
-rw-r--r--test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig17
-rw-r--r--test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig11
-rw-r--r--test/cases/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig15
-rw-r--r--test/cases/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig14
-rw-r--r--test/cases/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig10
-rw-r--r--test/cases/compile_errors/stage1/optional_pointer_to_void_in_extern_struct.zig (renamed from test/cases/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig)0
12 files changed, 240 insertions, 52 deletions
diff --git a/lib/std/start.zig b/lib/std/start.zig
index cf07986387..788c979d48 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 953336ee39..48cab0f04c 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", .{});
}
@@ -18169,6 +18189,119 @@ fn explainWhyTypeIsComptime(
}
}
+const ExternPosition = enum {
+ ret_ty,
+ param_ty,
+ other,
+};
+
+fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool {
+ switch (ty.zigTypeTag()) {
+ .Type,
+ .ComptimeFloat,
+ .ComptimeInt,
+ .EnumLiteral,
+ .Undefined,
+ .Null,
+ .ErrorUnion,
+ .ErrorSet,
+ .BoundFn,
+ .Void,
+ .Frame,
+ => return false,
+ .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,
@@ -24012,6 +24145,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;
}
@@ -24045,6 +24192,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, .other))) {
+ 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, .other);
+
+ 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 f14ad3a85b..765f1da18c 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,
@@ -4631,6 +4630,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()) {
.fn_noreturn_no_args => false,
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 0000000000..aeb5f42dec
--- /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 0000000000..606dda4b6e
--- /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
index 8f45a0e5dc..1740574389 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 0000000000..5fb36b707d
--- /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 0000000000..9a6c77363c
--- /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/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 c432bedd6b..0000000000
--- 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 aa508056a1..0000000000
--- 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 dc2e2690b8..0000000000
--- 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
index 552f672ad6..552f672ad6 100644
--- 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