diff options
| author | Veikka Tuominen <git@vexu.eu> | 2022-07-17 17:03:52 +0300 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-07-21 12:21:30 -0700 |
| commit | 8feb3987608945040c955bd7b24be3841ebf74ac (patch) | |
| tree | 192a1ec257081c7f4771e59ee1fc3e499ae7cf6f | |
| parent | d851b24180fdf2b622b06e9a35e315541fb10aa1 (diff) | |
| download | zig-8feb3987608945040c955bd7b24be3841ebf74ac.tar.gz zig-8feb3987608945040c955bd7b24be3841ebf74ac.zip | |
Sema: validate function parameter types and return type
| -rw-r--r-- | src/Module.zig | 11 | ||||
| -rw-r--r-- | src/Sema.zig | 98 | ||||
| -rw-r--r-- | src/type.zig | 22 | ||||
| -rw-r--r-- | test/cases/compile_errors/function_parameter_is_opaque.zig (renamed from test/cases/compile_errors/stage1/obj/function_parameter_is_opaque.zig) | 11 | ||||
| -rw-r--r-- | test/cases/compile_errors/function_returning_opaque_type.zig | 19 | ||||
| -rw-r--r-- | test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig | 11 | ||||
| -rw-r--r-- | test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig | 14 | ||||
| -rw-r--r-- | test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig | 14 | ||||
| -rw-r--r-- | test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig | 19 | ||||
| -rw-r--r-- | test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig | 8 | ||||
| -rw-r--r-- | test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig | 12 | ||||
| -rw-r--r-- | test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig | 12 |
12 files changed, 180 insertions, 71 deletions
diff --git a/src/Module.zig b/src/Module.zig index b174b88596..c9d956edcf 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2439,6 +2439,7 @@ pub const SrcLoc = struct { .node_offset_fn_type_ret_ty => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); + const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = src_loc.declRelativeToNodeIndex(node_off); var params: [1]Ast.Node.Index = undefined; @@ -2447,6 +2448,16 @@ pub const SrcLoc = struct { .fn_proto_multi => tree.fnProtoMulti(node), .fn_proto_one => tree.fnProtoOne(¶ms, node), .fn_proto => tree.fnProto(node), + .fn_decl => blk: { + const fn_proto = node_datas[node].lhs; + break :blk switch (node_tags[fn_proto]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, fn_proto), + .fn_proto_multi => tree.fnProtoMulti(fn_proto), + .fn_proto_one => tree.fnProtoOne(¶ms, fn_proto), + .fn_proto => tree.fnProto(fn_proto), + else => unreachable, + }; + }, else => unreachable, }; return nodeToSpan(tree, full.ast.return_type); diff --git a/src/Sema.zig b/src/Sema.zig index 0a6bd12284..166e5592c8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7243,7 +7243,10 @@ fn funcCommon( break :new_func new_func; } destroy_fn_on_error = true; - break :new_func try sema.gpa.create(Module.Fn); + const new_func = try sema.gpa.create(Module.Fn); + // Set this here so that the inferred return type can be printed correctly if it appears in an error. + new_func.owner_decl = sema.owner_decl_index; + break :new_func new_func; }; errdefer if (destroy_fn_on_error) sema.gpa.destroy(new_func); @@ -7277,18 +7280,67 @@ fn funcCommon( } } + // These locals are pulled out from the init expression below to work around + // a stage1 compiler bug. + // In the case of generic calling convention, or generic alignment, we use + // default values which are only meaningful for the generic function, *not* + // the instantiation, which can depend on comptime parameters. + // Related proposal: https://github.com/ziglang/zig/issues/11834 + const cc_workaround = cc orelse .Unspecified; + const align_workaround = alignment orelse 0; + const param_types = try sema.arena.alloc(Type, block.params.items.len); const comptime_params = try sema.arena.alloc(bool, block.params.items.len); for (block.params.items) |param, i| { const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better soruce location param_types[i] = param.ty; - comptime_params[i] = param.is_comptime or - try sema.typeRequiresComptime(block, param_src, param.ty); + const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); + comptime_params[i] = param.is_comptime or requires_comptime; is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison; if (is_extern and is_generic) { // TODO add note: function is generic because of this parameter return sema.fail(block, param_src, "extern function cannot be generic", .{}); } + if (!param.ty.isValidParamType()) { + const opaque_str = if (param.ty.zigTypeTag() == .Opaque) "opaque " else ""; + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{ + opaque_str, param.ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(param.ty, .param_ty))) { + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ + param.ty.fmt(sema.mod), @tagName(cc_workaround), + }); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (requires_comptime and !param.is_comptime) { + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parametter of type '{}' must be declared comptime", .{ + param.ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } } const ret_poison = if (!is_generic) rp: { @@ -7318,14 +7370,34 @@ fn funcCommon( }); }; - // These locals are pulled out from the init expression below to work around - // a stage1 compiler bug. - // In the case of generic calling convention, or generic alignment, we use - // default values which are only meaningful for the generic function, *not* - // the instantiation, which can depend on comptime parameters. - // Related proposal: https://github.com/ziglang/zig/issues/11834 - const cc_workaround = cc orelse .Unspecified; - const align_workaround = alignment orelse 0; + if (!bare_return_type.isValidReturnType()) { + const opaque_str = if (bare_return_type.zigTypeTag() == .Opaque) "opaque " else ""; + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{ + opaque_str, bare_return_type.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, bare_return_type); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(return_type, .ret_ty))) { + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ + return_type.fmt(sema.mod), @tagName(cc_workaround), + }); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty); + + try sema.addDeclaredHereNote(msg, return_type); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } const arch = sema.mod.getTarget().cpu.arch; if (switch (cc_workaround) { @@ -18399,7 +18471,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr .BoundFn, .Frame, => return false, - .Void => return position == .union_field, + .Void => return position == .union_field or position == .ret_ty, .NoReturn => return position == .ret_ty, .Opaque, .Bool, @@ -18411,7 +18483,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr 8, 16, 32, 64, 128 => return true, else => return false, }, - .Fn => return !ty.fnCallingConventionAllowsZigTypes(), + .Fn => return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()), .Enum => { var buf: Type.Payload.Bits = undefined; return sema.validateExternType(ty.intTagType(&buf), position); diff --git a/src/type.zig b/src/type.zig index 59f0668770..bdddaab070 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4643,13 +4643,27 @@ pub const Type = extern union { } /// Asserts the type is a function. - pub fn fnCallingConventionAllowsZigTypes(self: Type) bool { - return switch (self.fnCallingConvention()) { + pub fn fnCallingConventionAllowsZigTypes(cc: std.builtin.CallingConvention) bool { + return switch (cc) { .Unspecified, .Async, .Inline, .PtxKernel => true, else => false, }; } + pub fn isValidParamType(self: Type) bool { + return switch (self.zigTypeTagOrPoison() catch return true) { + .Undefined, .Null, .Opaque, .NoReturn => false, + else => true, + }; + } + + pub fn isValidReturnType(self: Type) bool { + return switch (self.zigTypeTagOrPoison() catch return true) { + .Undefined, .Null, .Opaque => false, + else => true, + }; + } + /// Asserts the type is a function. pub fn fnIsVarArgs(self: Type) bool { return switch (self.tag()) { @@ -5650,6 +5664,10 @@ pub const Type = extern union { const union_obj = ty.cast(Payload.Union).?.data; return union_obj.srcLoc(mod); }, + .@"opaque" => { + const opaque_obj = ty.cast(Payload.Opaque).?.data; + return opaque_obj.srcLoc(mod); + }, .atomic_order, .atomic_rmw_op, .calling_convention, diff --git a/test/cases/compile_errors/stage1/obj/function_parameter_is_opaque.zig b/test/cases/compile_errors/function_parameter_is_opaque.zig index 5b52370d86..31c477e8bc 100644 --- a/test/cases/compile_errors/stage1/obj/function_parameter_is_opaque.zig +++ b/test/cases/compile_errors/function_parameter_is_opaque.zig @@ -20,10 +20,11 @@ export fn entry4() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:28: error: parameter of opaque type 'FooType' not allowed -// tmp.zig:8:28: error: parameter of type '@Type(.Null)' not allowed -// tmp.zig:12:11: error: parameter of opaque type 'FooType' not allowed -// tmp.zig:17:11: error: parameter of type '@Type(.Null)' not allowed +// :3:24: error: parameter of opaque type 'tmp.FooType' not allowed +// :1:17: note: opaque declared here +// :8:24: error: parameter of type '@TypeOf(null)' not allowed +// :12:1: error: parameter of opaque type 'tmp.FooType' not allowed +// :17:1: error: parameter of type '@TypeOf(null)' not allowed diff --git a/test/cases/compile_errors/function_returning_opaque_type.zig b/test/cases/compile_errors/function_returning_opaque_type.zig new file mode 100644 index 0000000000..caf5d74d40 --- /dev/null +++ b/test/cases/compile_errors/function_returning_opaque_type.zig @@ -0,0 +1,19 @@ +const FooType = opaque {}; +export fn bar() !FooType { + return error.InvalidValue; +} +export fn bav() !@TypeOf(null) { + return error.InvalidValue; +} +export fn baz() !@TypeOf(undefined) { + return error.InvalidValue; +} + +// error +// backend=stage2 +// target=native +// +// :2:18: error: opaque return type 'tmp.FooType' not allowed +// :1:17: note: opaque declared here +// :5:18: error: return type '@TypeOf(null)' not allowed +// :8:18: error: return type '@TypeOf(undefined)' not allowed diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig new file mode 100644 index 0000000000..0cac6e1e0c --- /dev/null +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig @@ -0,0 +1,11 @@ +const Foo = enum { A, B, C }; +export fn entry(foo: Foo) void { _ = foo; } + +// error +// backend=stage2 +// target=native +// +// :2:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :2:8: note: enum tag type 'u2' is not extern compatible +// :2:8: note: only integers with power of two bits are extern compatible +// :1:13: note: enum declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig new file mode 100644 index 0000000000..8ae4ef7523 --- /dev/null +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig @@ -0,0 +1,14 @@ +const Foo = struct { + A: i32, + B: f32, + C: bool, +}; +export fn entry(foo: Foo) void { _ = foo; } + +// error +// backend=stage2 +// target=native +// +// :6:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :6:8: note: only structs with packed or extern layout are extern compatible +// :1:13: note: struct declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig new file mode 100644 index 0000000000..a4a96d9172 --- /dev/null +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig @@ -0,0 +1,14 @@ +const Foo = union { + A: i32, + B: f32, + C: bool, +}; +export fn entry(foo: Foo) void { _ = foo; } + +// error +// backend=stage2 +// target=native +// +// :6:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :6:8: note: only unions with packed or extern layout are extern compatible +// :1:13: note: union declared here diff --git a/test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig b/test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig deleted file mode 100644 index e2c084b517..0000000000 --- a/test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig +++ /dev/null @@ -1,19 +0,0 @@ -const FooType = opaque {}; -export fn bar() !FooType { - return error.InvalidValue; -} -export fn bav() !@TypeOf(null) { - return error.InvalidValue; -} -export fn baz() !@TypeOf(undefined) { - return error.InvalidValue; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:18: error: Opaque return type 'FooType' not allowed -// tmp.zig:1:1: note: type declared here -// tmp.zig:5:18: error: Null return type '@Type(.Null)' not allowed -// tmp.zig:8:18: error: Undefined return type '@Type(.Undefined)' not allowed diff --git a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig b/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig deleted file mode 100644 index 8de26c08b8..0000000000 --- a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig +++ /dev/null @@ -1,8 +0,0 @@ -const Foo = enum { A, B, C }; -export fn entry(foo: Foo) void { _ = foo; } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig b/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig deleted file mode 100644 index ecb4a36267..0000000000 --- a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig +++ /dev/null @@ -1,12 +0,0 @@ -const Foo = struct { - A: i32, - B: f32, - C: bool, -}; -export fn entry(foo: Foo) void { _ = foo; } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig b/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig deleted file mode 100644 index bfe2dc6cfa..0000000000 --- a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig +++ /dev/null @@ -1,12 +0,0 @@ -const Foo = union { - A: i32, - B: f32, - C: bool, -}; -export fn entry(foo: Foo) void { _ = foo; } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' |
