| author | |
| committer | |
| log | 8feb3987608945040c955bd7b24be3841ebf74ac |
| tree | 192a1ec257081c7f4771e59ee1fc3e499ae7cf6f |
| parent | d851b24180fdf2b622b06e9a35e315541fb10aa1 |
13 files changed, 204 insertions(+), 95 deletions(-)
src/Module.zig+11| ... | ... | @@ -2439,6 +2439,7 @@ pub const SrcLoc = struct { |
| 2439 | 2439 | |
| 2440 | 2440 | .node_offset_fn_type_ret_ty => |node_off| { |
| 2441 | 2441 | const tree = try src_loc.file_scope.getTree(gpa); |
| 2442 | const node_datas = tree.nodes.items(.data); | |
| 2442 | 2443 | const node_tags = tree.nodes.items(.tag); |
| 2443 | 2444 | const node = src_loc.declRelativeToNodeIndex(node_off); |
| 2444 | 2445 | var params: [1]Ast.Node.Index = undefined; |
| ... | ... | @@ -2447,6 +2448,16 @@ pub const SrcLoc = struct { |
| 2447 | 2448 | .fn_proto_multi => tree.fnProtoMulti(node), |
| 2448 | 2449 | .fn_proto_one => tree.fnProtoOne(&params, node), |
| 2449 | 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 | 2461 | else => unreachable, |
| 2451 | 2462 | }; |
| 2452 | 2463 | return nodeToSpan(tree, full.ast.return_type); |
src/Sema.zig+85-13| ... | ... | @@ -7243,7 +7243,10 @@ fn funcCommon( |
| 7243 | 7243 | break :new_func new_func; |
| 7244 | 7244 | } |
| 7245 | 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 | 7251 | errdefer if (destroy_fn_on_error) sema.gpa.destroy(new_func); |
| 7249 | 7252 | |
| ... | ... | @@ -7277,18 +7280,67 @@ fn funcCommon( |
| 7277 | 7280 | } |
| 7278 | 7281 | } |
| 7279 | 7282 | |
| 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 | 7292 | const param_types = try sema.arena.alloc(Type, block.params.items.len); |
| 7281 | 7293 | const comptime_params = try sema.arena.alloc(bool, block.params.items.len); |
| 7282 | 7294 | for (block.params.items) |param, i| { |
| 7283 | 7295 | const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better soruce location |
| 7284 | 7296 | param_types[i] = param.ty; |
| 7285 | comptime_params[i] = param.is_comptime or | |
| 7286 | try sema.typeRequiresComptime(block, param_src, param.ty); | |
| 7297 | const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); | |
| 7298 | comptime_params[i] = param.is_comptime or requires_comptime; | |
| 7287 | 7299 | is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison; |
| 7288 | 7300 | if (is_extern and is_generic) { |
| 7289 | 7301 | // TODO add note: function is generic because of this parameter |
| 7290 | 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 | } |
| 7293 | 7345 | |
| 7294 | 7346 | const ret_poison = if (!is_generic) rp: { |
| ... | ... | @@ -7318,14 +7370,34 @@ fn funcCommon( |
| 7318 | 7370 | }); |
| 7319 | 7371 | }; |
| 7320 | 7372 | |
| 7321 | // These locals are pulled out from the init expression below to work around | |
| 7322 | // a stage1 compiler bug. | |
| 7323 | // In the case of generic calling convention, or generic alignment, we use | |
| 7324 | // default values which are only meaningful for the generic function, *not* | |
| 7325 | // the instantiation, which can depend on comptime parameters. | |
| 7326 | // Related proposal: https://github.com/ziglang/zig/issues/11834 | |
| 7327 | const cc_workaround = cc orelse .Unspecified; | |
| 7328 | const align_workaround = alignment orelse 0; | |
| 7373 | if (!bare_return_type.isValidReturnType()) { | |
| 7374 | const opaque_str = if (bare_return_type.zigTypeTag() == .Opaque) "opaque " else ""; | |
| 7375 | const msg = msg: { | |
| 7376 | const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{ | |
| 7377 | opaque_str, bare_return_type.fmt(sema.mod), | |
| 7378 | }); | |
| 7379 | errdefer msg.destroy(sema.gpa); | |
| 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 | } | |
| 7329 | 7401 | |
| 7330 | 7402 | const arch = sema.mod.getTarget().cpu.arch; |
| 7331 | 7403 | if (switch (cc_workaround) { |
| ... | ... | @@ -18399,7 +18471,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr |
| 18399 | 18471 | .BoundFn, |
| 18400 | 18472 | .Frame, |
| 18401 | 18473 | => return false, |
| 18402 | .Void => return position == .union_field, | |
| 18474 | .Void => return position == .union_field or position == .ret_ty, | |
| 18403 | 18475 | .NoReturn => return position == .ret_ty, |
| 18404 | 18476 | .Opaque, |
| 18405 | 18477 | .Bool, |
| ... | ... | @@ -18411,7 +18483,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr |
| 18411 | 18483 | 8, 16, 32, 64, 128 => return true, |
| 18412 | 18484 | else => return false, |
| 18413 | 18485 | }, |
| 18414 | .Fn => return !ty.fnCallingConventionAllowsZigTypes(), | |
| 18486 | .Fn => return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()), | |
| 18415 | 18487 | .Enum => { |
| 18416 | 18488 | var buf: Type.Payload.Bits = undefined; |
| 18417 | 18489 | return sema.validateExternType(ty.intTagType(&buf), position); |
src/type.zig+20-2| ... | ... | @@ -4643,13 +4643,27 @@ pub const Type = extern union { |
| 4643 | 4643 | } |
| 4644 | 4644 | |
| 4645 | 4645 | /// Asserts the type is a function. |
| 4646 | pub fn fnCallingConventionAllowsZigTypes(self: Type) bool { | |
| 4647 | return switch (self.fnCallingConvention()) { | |
| 4646 | pub fn fnCallingConventionAllowsZigTypes(cc: std.builtin.CallingConvention) bool { | |
| 4647 | return switch (cc) { | |
| 4648 | 4648 | .Unspecified, .Async, .Inline, .PtxKernel => true, |
| 4649 | 4649 | else => false, |
| 4650 | 4650 | }; |
| 4651 | 4651 | } |
| 4652 | 4652 | |
| 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 | 4667 | /// Asserts the type is a function. |
| 4654 | 4668 | pub fn fnIsVarArgs(self: Type) bool { |
| 4655 | 4669 | return switch (self.tag()) { |
| ... | ... | @@ -5650,6 +5664,10 @@ pub const Type = extern union { |
| 5650 | 5664 | const union_obj = ty.cast(Payload.Union).?.data; |
| 5651 | 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 | 5671 | .atomic_order, |
| 5654 | 5672 | .atomic_rmw_op, |
| 5655 | 5673 | .calling_convention, |
test/cases/compile_errors/function_parameter_is_opaque.zig created+30| ... | ... | @@ -0,0 +1,30 @@ |
| 1 | const FooType = opaque {}; | |
| 2 | export fn entry1() void { | |
| 3 | const someFuncPtr: fn (FooType) void = undefined; | |
| 4 | _ = someFuncPtr; | |
| 5 | } | |
| 6 | ||
| 7 | export fn entry2() void { | |
| 8 | const someFuncPtr: fn (@TypeOf(null)) void = undefined; | |
| 9 | _ = someFuncPtr; | |
| 10 | } | |
| 11 | ||
| 12 | fn foo(p: FooType) void {_ = p;} | |
| 13 | export fn entry3() void { | |
| 14 | _ = foo; | |
| 15 | } | |
| 16 | ||
| 17 | fn bar(p: @TypeOf(null)) void {_ = p;} | |
| 18 | export 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 @@ |
| 1 | const FooType = opaque {}; | |
| 2 | export fn bar() !FooType { | |
| 3 | return error.InvalidValue; | |
| 4 | } | |
| 5 | export fn bav() !@TypeOf(null) { | |
| 6 | return error.InvalidValue; | |
| 7 | } | |
| 8 | export 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 @@ |
| 1 | const Foo = enum { A, B, C }; | |
| 2 | export 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 @@ |
| 1 | const Foo = struct { | |
| 2 | A: i32, | |
| 3 | B: f32, | |
| 4 | C: bool, | |
| 5 | }; | |
| 6 | export 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 @@ |
| 1 | const Foo = union { | |
| 2 | A: i32, | |
| 3 | B: f32, | |
| 4 | C: bool, | |
| 5 | }; | |
| 6 | export 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 @@ |
| 1 | const FooType = opaque {}; | |
| 2 | export fn entry1() void { | |
| 3 | const someFuncPtr: fn (FooType) void = undefined; | |
| 4 | _ = someFuncPtr; | |
| 5 | } | |
| 6 | ||
| 7 | export fn entry2() void { | |
| 8 | const someFuncPtr: fn (@TypeOf(null)) void = undefined; | |
| 9 | _ = someFuncPtr; | |
| 10 | } | |
| 11 | ||
| 12 | fn foo(p: FooType) void {_ = p;} | |
| 13 | export fn entry3() void { | |
| 14 | _ = foo; | |
| 15 | } | |
| 16 | ||
| 17 | fn bar(p: @TypeOf(null)) void {_ = p;} | |
| 18 | export 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 @@ |
| 1 | const FooType = opaque {}; | |
| 2 | export fn bar() !FooType { | |
| 3 | return error.InvalidValue; | |
| 4 | } | |
| 5 | export fn bav() !@TypeOf(null) { | |
| 6 | return error.InvalidValue; | |
| 7 | } | |
| 8 | export 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 @@ |
| 1 | const Foo = enum { A, B, C }; | |
| 2 | export 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 @@ |
| 1 | const Foo = struct { | |
| 2 | A: i32, | |
| 3 | B: f32, | |
| 4 | C: bool, | |
| 5 | }; | |
| 6 | export 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 @@ |
| 1 | const Foo = union { | |
| 2 | A: i32, | |
| 3 | B: f32, | |
| 4 | C: bool, | |
| 5 | }; | |
| 6 | export 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' |