authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-17 17:03:52+03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-21 12:21:30-07:00
log8feb3987608945040c955bd7b24be3841ebf74ac
tree192a1ec257081c7f4771e59ee1fc3e499ae7cf6f
parentd851b24180fdf2b622b06e9a35e315541fb10aa1

Sema: validate function parameter types and return type


13 files changed, 204 insertions(+), 95 deletions(-)

src/Module.zig+11
...@@ -2439,6 +2439,7 @@ pub const SrcLoc = struct {...@@ -2439,6 +2439,7 @@ pub const SrcLoc = struct {
24392439
2440 .node_offset_fn_type_ret_ty => |node_off| {2440 .node_offset_fn_type_ret_ty => |node_off| {
2441 const tree = try src_loc.file_scope.getTree(gpa);2441 const tree = try src_loc.file_scope.getTree(gpa);
2442 const node_datas = tree.nodes.items(.data);
2442 const node_tags = tree.nodes.items(.tag);2443 const node_tags = tree.nodes.items(.tag);
2443 const node = src_loc.declRelativeToNodeIndex(node_off);2444 const node = src_loc.declRelativeToNodeIndex(node_off);
2444 var params: [1]Ast.Node.Index = undefined;2445 var params: [1]Ast.Node.Index = undefined;
...@@ -2447,6 +2448,16 @@ pub const SrcLoc = struct {...@@ -2447,6 +2448,16 @@ pub const SrcLoc = struct {
2447 .fn_proto_multi => tree.fnProtoMulti(node),2448 .fn_proto_multi => tree.fnProtoMulti(node),
2448 .fn_proto_one => tree.fnProtoOne(&params, node),2449 .fn_proto_one => tree.fnProtoOne(&params, node),
2449 .fn_proto => tree.fnProto(node),2450 .fn_proto => tree.fnProto(node),
2451 .fn_decl => blk: {
2452 const fn_proto = node_datas[node].lhs;
2453 break :blk switch (node_tags[fn_proto]) {
2454 .fn_proto_simple => tree.fnProtoSimple(&params, fn_proto),
2455 .fn_proto_multi => tree.fnProtoMulti(fn_proto),
2456 .fn_proto_one => tree.fnProtoOne(&params, fn_proto),
2457 .fn_proto => tree.fnProto(fn_proto),
2458 else => unreachable,
2459 };
2460 },
2450 else => unreachable,2461 else => unreachable,
2451 };2462 };
2452 return nodeToSpan(tree, full.ast.return_type);2463 return nodeToSpan(tree, full.ast.return_type);
src/Sema.zig+85-13
...@@ -7243,7 +7243,10 @@ fn funcCommon(...@@ -7243,7 +7243,10 @@ fn funcCommon(
7243 break :new_func new_func;7243 break :new_func new_func;
7244 }7244 }
7245 destroy_fn_on_error = true;7245 destroy_fn_on_error = true;
7246 break :new_func try sema.gpa.create(Module.Fn);7246 const new_func = try sema.gpa.create(Module.Fn);
7247 // Set this here so that the inferred return type can be printed correctly if it appears in an error.
7248 new_func.owner_decl = sema.owner_decl_index;
7249 break :new_func new_func;
7247 };7250 };
7248 errdefer if (destroy_fn_on_error) sema.gpa.destroy(new_func);7251 errdefer if (destroy_fn_on_error) sema.gpa.destroy(new_func);
72497252
...@@ -7277,18 +7280,67 @@ fn funcCommon(...@@ -7277,18 +7280,67 @@ fn funcCommon(
7277 }7280 }
7278 }7281 }
72797282
7283 // These locals are pulled out from the init expression below to work around
7284 // a stage1 compiler bug.
7285 // In the case of generic calling convention, or generic alignment, we use
7286 // default values which are only meaningful for the generic function, *not*
7287 // the instantiation, which can depend on comptime parameters.
7288 // Related proposal: https://github.com/ziglang/zig/issues/11834
7289 const cc_workaround = cc orelse .Unspecified;
7290 const align_workaround = alignment orelse 0;
7291
7280 const param_types = try sema.arena.alloc(Type, block.params.items.len);7292 const param_types = try sema.arena.alloc(Type, block.params.items.len);
7281 const comptime_params = try sema.arena.alloc(bool, block.params.items.len);7293 const comptime_params = try sema.arena.alloc(bool, block.params.items.len);
7282 for (block.params.items) |param, i| {7294 for (block.params.items) |param, i| {
7283 const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better soruce location7295 const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better soruce location
7284 param_types[i] = param.ty;7296 param_types[i] = param.ty;
7285 comptime_params[i] = param.is_comptime or7297 const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty);
7286 try sema.typeRequiresComptime(block, param_src, param.ty);7298 comptime_params[i] = param.is_comptime or requires_comptime;
7287 is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison;7299 is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison;
7288 if (is_extern and is_generic) {7300 if (is_extern and is_generic) {
7289 // TODO add note: function is generic because of this parameter7301 // TODO add note: function is generic because of this parameter
7290 return sema.fail(block, param_src, "extern function cannot be generic", .{});7302 return sema.fail(block, param_src, "extern function cannot be generic", .{});
7291 }7303 }
7304 if (!param.ty.isValidParamType()) {
7305 const opaque_str = if (param.ty.zigTypeTag() == .Opaque) "opaque " else "";
7306 const msg = msg: {
7307 const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{
7308 opaque_str, param.ty.fmt(sema.mod),
7309 });
7310 errdefer msg.destroy(sema.gpa);
7311
7312 try sema.addDeclaredHereNote(msg, param.ty);
7313 break :msg msg;
7314 };
7315 return sema.failWithOwnedErrorMsg(block, msg);
7316 }
7317 if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(param.ty, .param_ty))) {
7318 const msg = msg: {
7319 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{
7320 param.ty.fmt(sema.mod), @tagName(cc_workaround),
7321 });
7322 errdefer msg.destroy(sema.gpa);
7323
7324 const src_decl = sema.mod.declPtr(block.src_decl);
7325 try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty);
7326
7327 try sema.addDeclaredHereNote(msg, param.ty);
7328 break :msg msg;
7329 };
7330 return sema.failWithOwnedErrorMsg(block, msg);
7331 }
7332 if (requires_comptime and !param.is_comptime) {
7333 const msg = msg: {
7334 const msg = try sema.errMsg(block, param_src, "parametter of type '{}' must be declared comptime", .{
7335 param.ty.fmt(sema.mod),
7336 });
7337 errdefer msg.destroy(sema.gpa);
7338
7339 try sema.addDeclaredHereNote(msg, param.ty);
7340 break :msg msg;
7341 };
7342 return sema.failWithOwnedErrorMsg(block, msg);
7343 }
7292 }7344 }
72937345
7294 const ret_poison = if (!is_generic) rp: {7346 const ret_poison = if (!is_generic) rp: {
...@@ -7318,14 +7370,34 @@ fn funcCommon(...@@ -7318,14 +7370,34 @@ fn funcCommon(
7318 });7370 });
7319 };7371 };
73207372
7321 // These locals are pulled out from the init expression below to work around7373 if (!bare_return_type.isValidReturnType()) {
7322 // a stage1 compiler bug.7374 const opaque_str = if (bare_return_type.zigTypeTag() == .Opaque) "opaque " else "";
7323 // In the case of generic calling convention, or generic alignment, we use7375 const msg = msg: {
7324 // default values which are only meaningful for the generic function, *not*7376 const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{
7325 // the instantiation, which can depend on comptime parameters.7377 opaque_str, bare_return_type.fmt(sema.mod),
7326 // Related proposal: https://github.com/ziglang/zig/issues/118347378 });
7327 const cc_workaround = cc orelse .Unspecified;7379 errdefer msg.destroy(sema.gpa);
7328 const align_workaround = alignment orelse 0;7380
7381 try sema.addDeclaredHereNote(msg, bare_return_type);
7382 break :msg msg;
7383 };
7384 return sema.failWithOwnedErrorMsg(block, msg);
7385 }
7386 if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(return_type, .ret_ty))) {
7387 const msg = msg: {
7388 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
7389 return_type.fmt(sema.mod), @tagName(cc_workaround),
7390 });
7391 errdefer msg.destroy(sema.gpa);
7392
7393 const src_decl = sema.mod.declPtr(block.src_decl);
7394 try sema.explainWhyTypeIsNotExtern(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty);
7395
7396 try sema.addDeclaredHereNote(msg, return_type);
7397 break :msg msg;
7398 };
7399 return sema.failWithOwnedErrorMsg(block, msg);
7400 }
73297401
7330 const arch = sema.mod.getTarget().cpu.arch;7402 const arch = sema.mod.getTarget().cpu.arch;
7331 if (switch (cc_workaround) {7403 if (switch (cc_workaround) {
...@@ -18399,7 +18471,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr...@@ -18399,7 +18471,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr
18399 .BoundFn,18471 .BoundFn,
18400 .Frame,18472 .Frame,
18401 => return false,18473 => return false,
18402 .Void => return position == .union_field,18474 .Void => return position == .union_field or position == .ret_ty,
18403 .NoReturn => return position == .ret_ty,18475 .NoReturn => return position == .ret_ty,
18404 .Opaque,18476 .Opaque,
18405 .Bool,18477 .Bool,
...@@ -18411,7 +18483,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr...@@ -18411,7 +18483,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr
18411 8, 16, 32, 64, 128 => return true,18483 8, 16, 32, 64, 128 => return true,
18412 else => return false,18484 else => return false,
18413 },18485 },
18414 .Fn => return !ty.fnCallingConventionAllowsZigTypes(),18486 .Fn => return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()),
18415 .Enum => {18487 .Enum => {
18416 var buf: Type.Payload.Bits = undefined;18488 var buf: Type.Payload.Bits = undefined;
18417 return sema.validateExternType(ty.intTagType(&buf), position);18489 return sema.validateExternType(ty.intTagType(&buf), position);
src/type.zig+20-2
...@@ -4643,13 +4643,27 @@ pub const Type = extern union {...@@ -4643,13 +4643,27 @@ pub const Type = extern union {
4643 }4643 }
46444644
4645 /// Asserts the type is a function.4645 /// Asserts the type is a function.
4646 pub fn fnCallingConventionAllowsZigTypes(self: Type) bool {4646 pub fn fnCallingConventionAllowsZigTypes(cc: std.builtin.CallingConvention) bool {
4647 return switch (self.fnCallingConvention()) {4647 return switch (cc) {
4648 .Unspecified, .Async, .Inline, .PtxKernel => true,4648 .Unspecified, .Async, .Inline, .PtxKernel => true,
4649 else => false,4649 else => false,
4650 };4650 };
4651 }4651 }
46524652
4653 pub fn isValidParamType(self: Type) bool {
4654 return switch (self.zigTypeTagOrPoison() catch return true) {
4655 .Undefined, .Null, .Opaque, .NoReturn => false,
4656 else => true,
4657 };
4658 }
4659
4660 pub fn isValidReturnType(self: Type) bool {
4661 return switch (self.zigTypeTagOrPoison() catch return true) {
4662 .Undefined, .Null, .Opaque => false,
4663 else => true,
4664 };
4665 }
4666
4653 /// Asserts the type is a function.4667 /// Asserts the type is a function.
4654 pub fn fnIsVarArgs(self: Type) bool {4668 pub fn fnIsVarArgs(self: Type) bool {
4655 return switch (self.tag()) {4669 return switch (self.tag()) {
...@@ -5650,6 +5664,10 @@ pub const Type = extern union {...@@ -5650,6 +5664,10 @@ pub const Type = extern union {
5650 const union_obj = ty.cast(Payload.Union).?.data;5664 const union_obj = ty.cast(Payload.Union).?.data;
5651 return union_obj.srcLoc(mod);5665 return union_obj.srcLoc(mod);
5652 },5666 },
5667 .@"opaque" => {
5668 const opaque_obj = ty.cast(Payload.Opaque).?.data;
5669 return opaque_obj.srcLoc(mod);
5670 },
5653 .atomic_order,5671 .atomic_order,
5654 .atomic_rmw_op,5672 .atomic_rmw_op,
5655 .calling_convention,5673 .calling_convention,
test/cases/compile_errors/function_parameter_is_opaque.zig created+30
...@@ -0,0 +1,30 @@
1const FooType = opaque {};
2export fn entry1() void {
3 const someFuncPtr: fn (FooType) void = undefined;
4 _ = someFuncPtr;
5}
6
7export fn entry2() void {
8 const someFuncPtr: fn (@TypeOf(null)) void = undefined;
9 _ = someFuncPtr;
10}
11
12fn foo(p: FooType) void {_ = p;}
13export fn entry3() void {
14 _ = foo;
15}
16
17fn bar(p: @TypeOf(null)) void {_ = p;}
18export fn entry4() void {
19 _ = bar;
20}
21
22// error
23// backend=stage2
24// target=native
25//
26// :3:24: error: parameter of opaque type 'tmp.FooType' not allowed
27// :1:17: note: opaque declared here
28// :8:24: error: parameter of type '@TypeOf(null)' not allowed
29// :12:1: error: parameter of opaque type 'tmp.FooType' not allowed
30// :17:1: error: parameter of type '@TypeOf(null)' not allowed
test/cases/compile_errors/function_returning_opaque_type.zig created+19
...@@ -0,0 +1,19 @@
1const FooType = opaque {};
2export fn bar() !FooType {
3 return error.InvalidValue;
4}
5export fn bav() !@TypeOf(null) {
6 return error.InvalidValue;
7}
8export fn baz() !@TypeOf(undefined) {
9 return error.InvalidValue;
10}
11
12// error
13// backend=stage2
14// target=native
15//
16// :2:18: error: opaque return type 'tmp.FooType' not allowed
17// :1:17: note: opaque declared here
18// :5:18: error: return type '@TypeOf(null)' not allowed
19// :8:18: error: return type '@TypeOf(undefined)' not allowed
test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig created+11
...@@ -0,0 +1,11 @@
1const Foo = enum { A, B, C };
2export fn entry(foo: Foo) void { _ = foo; }
3
4// error
5// backend=stage2
6// target=native
7//
8// :2:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C'
9// :2:8: note: enum tag type 'u2' is not extern compatible
10// :2:8: note: only integers with power of two bits are extern compatible
11// :1:13: note: enum declared here
test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig created+14
...@@ -0,0 +1,14 @@
1const Foo = struct {
2 A: i32,
3 B: f32,
4 C: bool,
5};
6export fn entry(foo: Foo) void { _ = foo; }
7
8// error
9// backend=stage2
10// target=native
11//
12// :6:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C'
13// :6:8: note: only structs with packed or extern layout are extern compatible
14// :1:13: note: struct declared here
test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig created+14
...@@ -0,0 +1,14 @@
1const Foo = union {
2 A: i32,
3 B: f32,
4 C: bool,
5};
6export fn entry(foo: Foo) void { _ = foo; }
7
8// error
9// backend=stage2
10// target=native
11//
12// :6:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C'
13// :6:8: note: only unions with packed or extern layout are extern compatible
14// :1:13: note: union declared here
test/cases/compile_errors/stage1/obj/function_parameter_is_opaque.zig deleted-29
...@@ -1,29 +0,0 @@
1const FooType = opaque {};
2export fn entry1() void {
3 const someFuncPtr: fn (FooType) void = undefined;
4 _ = someFuncPtr;
5}
6
7export fn entry2() void {
8 const someFuncPtr: fn (@TypeOf(null)) void = undefined;
9 _ = someFuncPtr;
10}
11
12fn foo(p: FooType) void {_ = p;}
13export fn entry3() void {
14 _ = foo;
15}
16
17fn bar(p: @TypeOf(null)) void {_ = p;}
18export fn entry4() void {
19 _ = bar;
20}
21
22// error
23// backend=stage1
24// target=native
25//
26// tmp.zig:3:28: error: parameter of opaque type 'FooType' not allowed
27// tmp.zig:8:28: error: parameter of type '@Type(.Null)' not allowed
28// tmp.zig:12:11: error: parameter of opaque type 'FooType' not allowed
29// tmp.zig:17:11: error: parameter of type '@Type(.Null)' not allowed
test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig deleted-19
...@@ -1,19 +0,0 @@
1const FooType = opaque {};
2export fn bar() !FooType {
3 return error.InvalidValue;
4}
5export fn bav() !@TypeOf(null) {
6 return error.InvalidValue;
7}
8export fn baz() !@TypeOf(undefined) {
9 return error.InvalidValue;
10}
11
12// error
13// backend=stage1
14// target=native
15//
16// tmp.zig:2:18: error: Opaque return type 'FooType' not allowed
17// tmp.zig:1:1: note: type declared here
18// tmp.zig:5:18: error: Null return type '@Type(.Null)' not allowed
19// tmp.zig:8:18: error: Undefined return type '@Type(.Undefined)' not allowed
test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig deleted-8
...@@ -1,8 +0,0 @@
1const Foo = enum { A, B, C };
2export fn entry(foo: Foo) void { _ = foo; }
3
4// error
5// backend=stage1
6// target=native
7//
8// tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'
test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig deleted-12
...@@ -1,12 +0,0 @@
1const Foo = struct {
2 A: i32,
3 B: f32,
4 C: bool,
5};
6export fn entry(foo: Foo) void { _ = foo; }
7
8// error
9// backend=stage1
10// target=native
11//
12// tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'
test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig deleted-12
...@@ -1,12 +0,0 @@
1const Foo = union {
2 A: i32,
3 B: f32,
4 C: bool,
5};
6export fn entry(foo: Foo) void { _ = foo; }
7
8// error
9// backend=stage1
10// target=native
11//
12// tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'