authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-11 17:54:53+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-11 18:47:29+03:00
logd00da05ecbeca36e8ca25dcb1238bf98b38081a5
tree3c47892fb4a3485ba7a3400a523729b16484646b
parent3ceb27c8401df17e5a7f522d68104da79501b5b6

Sema: validate extern types


14 files changed, 293 insertions(+), 105 deletions(-)

lib/std/start.zig+1-1
......@@ -108,7 +108,7 @@ fn callMain2() noreturn {
108108 exit2(0);
109109}
110110
111fn wasiMain2() noreturn {
111fn wasiMain2() callconv(.C) noreturn {
112112 switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) {
113113 .Void => {
114114 root.main();
src/Sema.zig+168-7
......@@ -4740,12 +4740,19 @@ pub fn analyzeExport(
47404740
47414741 try mod.ensureDeclAnalyzed(exported_decl_index);
47424742 const exported_decl = mod.declPtr(exported_decl_index);
4743 // TODO run the same checks as we do for C ABI struct fields
4744 switch (exported_decl.ty.zigTypeTag()) {
4745 .Fn, .Int, .Enum, .Struct, .Union, .Array, .Float, .Pointer, .Optional => {},
4746 else => return sema.fail(block, src, "unable to export type '{}'", .{
4747 exported_decl.ty.fmt(sema.mod),
4748 }),
4743
4744 if (!(try sema.validateExternType(exported_decl.ty, .other))) {
4745 const msg = msg: {
4746 const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)});
4747 errdefer msg.destroy(sema.gpa);
4748
4749 const src_decl = sema.mod.declPtr(block.src_decl);
4750 try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), exported_decl.ty, .other);
4751
4752 try sema.addDeclaredHereNote(msg, exported_decl.ty);
4753 break :msg msg;
4754 };
4755 return sema.failWithOwnedErrorMsg(block, msg);
47494756 }
47504757
47514758 const gpa = mod.gpa;
......@@ -13799,7 +13806,20 @@ fn validatePtrTy(sema: *Sema, block: *Block, elem_src: LazySrcLoc, ty: Type) Com
1379913806 } else if (ptr_info.size == .Many and pointee_tag == .Opaque) {
1380013807 return sema.fail(block, elem_src, "unknown-length pointer to opaque not allowed", .{});
1380113808 } else if (ptr_info.size == .C) {
13802 // TODO check extern type
13809 const elem_ty = ptr_info.pointee_type;
13810 if (!(try sema.validateExternType(elem_ty, .other))) {
13811 const msg = msg: {
13812 const msg = try sema.errMsg(block, elem_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
13813 errdefer msg.destroy(sema.gpa);
13814
13815 const src_decl = sema.mod.declPtr(block.src_decl);
13816 try sema.explainWhyTypeIsNotExtern(block, elem_src, msg, elem_src.toSrcLoc(src_decl), elem_ty, .other);
13817
13818 try sema.addDeclaredHereNote(msg, elem_ty);
13819 break :msg msg;
13820 };
13821 return sema.failWithOwnedErrorMsg(block, msg);
13822 }
1380313823 if (pointee_tag == .Opaque) {
1380413824 return sema.fail(block, elem_src, "C pointers cannot point to opaque types", .{});
1380513825 }
......@@ -18169,6 +18189,119 @@ fn explainWhyTypeIsComptime(
1816918189 }
1817018190}
1817118191
18192const ExternPosition = enum {
18193 ret_ty,
18194 param_ty,
18195 other,
18196};
18197
18198fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool {
18199 switch (ty.zigTypeTag()) {
18200 .Type,
18201 .ComptimeFloat,
18202 .ComptimeInt,
18203 .EnumLiteral,
18204 .Undefined,
18205 .Null,
18206 .ErrorUnion,
18207 .ErrorSet,
18208 .BoundFn,
18209 .Void,
18210 .Frame,
18211 => return false,
18212 .NoReturn => return position == .ret_ty,
18213 .Opaque,
18214 .Bool,
18215 .Float,
18216 .Pointer,
18217 .AnyFrame,
18218 => return true,
18219 .Int => switch (ty.intInfo(sema.mod.getTarget()).bits) {
18220 8, 16, 32, 64, 128 => return true,
18221 else => return false,
18222 },
18223 .Fn => return !ty.fnCallingConventionAllowsZigTypes(),
18224 .Enum => {
18225 var buf: Type.Payload.Bits = undefined;
18226 return sema.validateExternType(ty.intTagType(&buf), position);
18227 },
18228 .Struct, .Union => switch (ty.containerLayout()) {
18229 .Extern, .Packed => return true,
18230 else => return false,
18231 },
18232 .Array => {
18233 if (position == .ret_ty or position == .param_ty) return false;
18234 return sema.validateExternType(ty.elemType2(), .other);
18235 },
18236 .Vector => return sema.validateExternType(ty.elemType2(), .other),
18237 .Optional => return ty.isPtrLikeOptional(),
18238 }
18239}
18240
18241fn explainWhyTypeIsNotExtern(
18242 sema: *Sema,
18243 block: *Block,
18244 src: LazySrcLoc,
18245 msg: *Module.ErrorMsg,
18246 src_loc: Module.SrcLoc,
18247 ty: Type,
18248 position: ExternPosition,
18249) CompileError!void {
18250 const mod = sema.mod;
18251 switch (ty.zigTypeTag()) {
18252 .Opaque,
18253 .Bool,
18254 .Float,
18255 .Pointer,
18256 .AnyFrame,
18257 => return,
18258
18259 .Type,
18260 .ComptimeFloat,
18261 .ComptimeInt,
18262 .EnumLiteral,
18263 .Undefined,
18264 .Null,
18265 .ErrorUnion,
18266 .ErrorSet,
18267 .BoundFn,
18268 .Frame,
18269 => return,
18270
18271 .Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}),
18272 .NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
18273 .Int => if (ty.intInfo(sema.mod.getTarget()).bits > 128) {
18274 try mod.errNoteNonLazy(src_loc, msg, "only integers with less than 128 bits are extern compatible", .{});
18275 } else {
18276 try mod.errNoteNonLazy(src_loc, msg, "only integers with power of two bits are extern compatible", .{});
18277 },
18278 .Fn => switch (ty.fnCallingConvention()) {
18279 .Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}),
18280 .Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}),
18281 .Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}),
18282 else => return,
18283 },
18284 .Enum => {
18285 var buf: Type.Payload.Bits = undefined;
18286 const tag_ty = ty.intTagType(&buf);
18287 try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
18288 try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, tag_ty, position);
18289 },
18290 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}),
18291 .Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}),
18292 .Array => {
18293 if (position == .ret_ty) {
18294 try mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{});
18295 } else if (position == .param_ty) {
18296 try mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
18297 }
18298 try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position);
18299 },
18300 .Vector => try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position),
18301 .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
18302 }
18303}
18304
1817218305pub const PanicId = enum {
1817318306 unreach,
1817418307 unwrap_null,
......@@ -24012,6 +24145,20 @@ fn resolveStructFully(
2401224145 struct_obj.status = .fully_resolved_wip;
2401324146 for (struct_obj.fields.values()) |field| {
2401424147 try sema.resolveTypeFully(block, src, field.ty);
24148
24149 if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) {
24150 const msg = msg: {
24151 const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
24152 errdefer msg.destroy(sema.gpa);
24153
24154 const src_decl = sema.mod.declPtr(block.src_decl);
24155 try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), field.ty, .other);
24156
24157 try sema.addDeclaredHereNote(msg, field.ty);
24158 break :msg msg;
24159 };
24160 return sema.failWithOwnedErrorMsg(block, msg);
24161 }
2401524162 }
2401624163 struct_obj.status = .fully_resolved;
2401724164 }
......@@ -24045,6 +24192,20 @@ fn resolveUnionFully(
2404524192 union_obj.status = .fully_resolved_wip;
2404624193 for (union_obj.fields.values()) |field| {
2404724194 try sema.resolveTypeFully(block, src, field.ty);
24195
24196 if (union_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) {
24197 const msg = msg: {
24198 const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
24199 errdefer msg.destroy(sema.gpa);
24200
24201 const src_decl = sema.mod.declPtr(block.src_decl);
24202 try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), field.ty, .other);
24203
24204 try sema.addDeclaredHereNote(msg, field.ty);
24205 break :msg msg;
24206 };
24207 return sema.failWithOwnedErrorMsg(block, msg);
24208 }
2404824209 }
2404924210 union_obj.status = .fully_resolved;
2405024211 }
src/type.zig+8-1
......@@ -3935,7 +3935,6 @@ pub const Type = extern union {
39353935
39363936 /// Returns true if the type is optional and would be lowered to a single pointer
39373937 /// address value, using 0 for null. Note that this returns true for C pointers.
3938 /// See also `hasOptionalRepr`.
39393938 pub fn isPtrLikeOptional(self: Type) bool {
39403939 switch (self.tag()) {
39413940 .optional_single_const_pointer,
......@@ -4630,6 +4629,14 @@ pub const Type = extern union {
46304629 };
46314630 }
46324631
4632 /// Asserts the type is a function.
4633 pub fn fnCallingConventionAllowsZigTypes(self: Type) bool {
4634 return switch (self.fnCallingConvention()) {
4635 .Unspecified, .Async, .Inline, .PtxKernel => true,
4636 else => false,
4637 };
4638 }
4639
46334640 /// Asserts the type is a function.
46344641 pub fn fnIsVarArgs(self: Type) bool {
46354642 return switch (self.tag()) {
test/cases/compile_errors/c_pointer_to_void.zig created+11
......@@ -0,0 +1,11 @@
1export fn entry() void {
2 var a: [*c]void = undefined;
3 _ = a;
4}
5
6// error
7// backend=stage2
8// target=native
9//
10// :1:1: error: C pointers cannot point to non-C-ABI-compatible type 'void'
11// :1:1: note: 'void' is a zero bit type; for C 'void' use 'anyopaque'
test/cases/compile_errors/exported_enum_without_explicit_integer_tag_type.zig created+18
......@@ -0,0 +1,18 @@
1const E = enum { one, two };
2comptime {
3 @export(E, .{ .name = "E" });
4}
5const e: E = .two;
6comptime {
7 @export(e, .{ .name = "e" });
8}
9
10// error
11// backend=stage2
12// target=native
13//
14// :3:5: error: unable to export type 'type'
15// :7:5: error: unable to export type 'tmp.E'
16// :7:5: note: enum tag type 'u1' is not extern compatible
17// :7:5: note: only integers with power of two bits are extern compatible
18// :1:11: note: enum declared here
test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig created+45
......@@ -0,0 +1,45 @@
1pub const E = enum {
2@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",
3@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23",
4@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34",
5@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45",
6@"46",@"47",@"48",@"49",@"50",@"51",@"52",@"53",@"54",@"55",@"56",
7@"57",@"58",@"59",@"60",@"61",@"62",@"63",@"64",@"65",@"66",@"67",
8@"68",@"69",@"70",@"71",@"72",@"73",@"74",@"75",@"76",@"77",@"78",
9@"79",@"80",@"81",@"82",@"83",@"84",@"85",@"86",@"87",@"88",@"89",
10@"90",@"91",@"92",@"93",@"94",@"95",@"96",@"97",@"98",@"99",@"100",
11@"101",@"102",@"103",@"104",@"105",@"106",@"107",@"108",@"109",
12@"110",@"111",@"112",@"113",@"114",@"115",@"116",@"117",@"118",
13@"119",@"120",@"121",@"122",@"123",@"124",@"125",@"126",@"127",
14@"128",@"129",@"130",@"131",@"132",@"133",@"134",@"135",@"136",
15@"137",@"138",@"139",@"140",@"141",@"142",@"143",@"144",@"145",
16@"146",@"147",@"148",@"149",@"150",@"151",@"152",@"153",@"154",
17@"155",@"156",@"157",@"158",@"159",@"160",@"161",@"162",@"163",
18@"164",@"165",@"166",@"167",@"168",@"169",@"170",@"171",@"172",
19@"173",@"174",@"175",@"176",@"177",@"178",@"179",@"180",@"181",
20@"182",@"183",@"184",@"185",@"186",@"187",@"188",@"189",@"190",
21@"191",@"192",@"193",@"194",@"195",@"196",@"197",@"198",@"199",
22@"200",@"201",@"202",@"203",@"204",@"205",@"206",@"207",@"208",
23@"209",@"210",@"211",@"212",@"213",@"214",@"215",@"216",@"217",
24@"218",@"219",@"220",@"221",@"222",@"223",@"224",@"225",@"226",
25@"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235",
26@"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244",
27@"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253",
28@"254",@"255", @"256"
29};
30pub const S = extern struct {
31 e: E,
32};
33export fn entry() void {
34 const s: S = undefined;
35 _ = s;
36}
37
38// error
39// backend=stage2
40// target=native
41//
42// :33:8: error: extern structs cannot contain fields of type 'tmp.E'
43// :33:8: note: enum tag type 'u9' is not extern compatible
44// :33:8: note: only integers with power of two bits are extern compatible
45// :1:15: note: enum declared here
test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig created+17
......@@ -0,0 +1,17 @@
1pub const E = enum(u31) { A, B, C };
2pub const S = extern struct {
3 e: E,
4};
5export fn entry() void {
6 const s: S = undefined;
7 _ = s;
8}
9
10// error
11// backend=stage2
12// target=native
13//
14// :5:8: error: extern structs cannot contain fields of type 'tmp.E'
15// :5:8: note: enum tag type 'u31' is not extern compatible
16// :5:8: note: only integers with power of two bits are extern compatible
17// :1:15: note: enum declared here
test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig created+11
......@@ -0,0 +1,11 @@
1const stroo = extern struct {
2 moo: ?[*c]u8,
3};
4export fn testf(fluff: *stroo) void { _ = fluff; }
5
6// error
7// backend=stage2
8// target=native
9//
10// :4:8: error: extern structs cannot contain fields of type '?[*c]u8'
11// :4:8: note: only pointer like optionals are extern compatible
test/cases/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig deleted-15
......@@ -1,15 +0,0 @@
1const E = enum { one, two };
2comptime {
3 @export(E, .{ .name = "E" });
4}
5const e: E = .two;
6comptime {
7 @export(e, .{ .name = "e" });
8}
9
10// error
11// backend=stage1
12// target=native
13//
14// tmp.zig:3:13: error: exported enum without explicit integer tag type
15// tmp.zig:7:13: error: exported enum value without explicit integer tag type
test/cases/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig deleted-43
......@@ -1,43 +0,0 @@
1pub const E = enum {
2@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",
3@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23",
4@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34",
5@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45",
6@"46",@"47",@"48",@"49",@"50",@"51",@"52",@"53",@"54",@"55",@"56",
7@"57",@"58",@"59",@"60",@"61",@"62",@"63",@"64",@"65",@"66",@"67",
8@"68",@"69",@"70",@"71",@"72",@"73",@"74",@"75",@"76",@"77",@"78",
9@"79",@"80",@"81",@"82",@"83",@"84",@"85",@"86",@"87",@"88",@"89",
10@"90",@"91",@"92",@"93",@"94",@"95",@"96",@"97",@"98",@"99",@"100",
11@"101",@"102",@"103",@"104",@"105",@"106",@"107",@"108",@"109",
12@"110",@"111",@"112",@"113",@"114",@"115",@"116",@"117",@"118",
13@"119",@"120",@"121",@"122",@"123",@"124",@"125",@"126",@"127",
14@"128",@"129",@"130",@"131",@"132",@"133",@"134",@"135",@"136",
15@"137",@"138",@"139",@"140",@"141",@"142",@"143",@"144",@"145",
16@"146",@"147",@"148",@"149",@"150",@"151",@"152",@"153",@"154",
17@"155",@"156",@"157",@"158",@"159",@"160",@"161",@"162",@"163",
18@"164",@"165",@"166",@"167",@"168",@"169",@"170",@"171",@"172",
19@"173",@"174",@"175",@"176",@"177",@"178",@"179",@"180",@"181",
20@"182",@"183",@"184",@"185",@"186",@"187",@"188",@"189",@"190",
21@"191",@"192",@"193",@"194",@"195",@"196",@"197",@"198",@"199",
22@"200",@"201",@"202",@"203",@"204",@"205",@"206",@"207",@"208",
23@"209",@"210",@"211",@"212",@"213",@"214",@"215",@"216",@"217",
24@"218",@"219",@"220",@"221",@"222",@"223",@"224",@"225",@"226",
25@"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235",
26@"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244",
27@"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253",
28@"254",@"255"
29};
30pub const S = extern struct {
31 e: E,
32};
33export fn entry() void {
34 if (@typeInfo(E).Enum.tag_type != u8) @compileError("did not infer u8 tag type");
35 const s: S = undefined;
36 _ = s;
37}
38
39// error
40// backend=stage1
41// target=native
42//
43// tmp.zig:31:5: error: extern structs cannot contain fields of type 'E'
test/cases/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig deleted-14
......@@ -1,14 +0,0 @@
1pub const E = enum(u31) { A, B, C };
2pub const S = extern struct {
3 e: E,
4};
5export fn entry() void {
6 const s: S = undefined;
7 _ = s;
8}
9
10// error
11// backend=stage1
12// target=native
13//
14// tmp.zig:3:5: error: extern structs cannot contain fields of type 'E'
test/cases/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig deleted-10
......@@ -1,10 +0,0 @@
1const stroo = extern struct {
2 moo: ?[*c]u8,
3};
4export fn testf(fluff: *stroo) void { _ = fluff; }
5
6// error
7// backend=stage1
8// target=native
9//
10// tmp.zig:2:5: error: extern structs cannot contain fields of type '?[*c]u8'
test/cases/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig deleted-14
......@@ -1,14 +0,0 @@
1const Foo = extern struct {
2 x: ?*const void,
3};
4const Bar = extern struct {
5 foo: Foo,
6 y: i32,
7};
8export fn entry(bar: *Bar) void {_ = bar;}
9
10// error
11// backend=stage1
12// target=native
13//
14// tmp.zig:2:5: error: extern structs cannot contain fields of type '?*const void'
test/cases/compile_errors/stage1/optional_pointer_to_void_in_extern_struct.zig created+14
......@@ -0,0 +1,14 @@
1const Foo = extern struct {
2 x: ?*const void,
3};
4const Bar = extern struct {
5 foo: Foo,
6 y: i32,
7};
8export fn entry(bar: *Bar) void {_ = bar;}
9
10// error
11// backend=stage1
12// target=native
13//
14// tmp.zig:2:5: error: extern structs cannot contain fields of type '?*const void'