diff --git a/src/Module.zig b/src/Module.zig index 8389dcec19..a1081517c0 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2482,6 +2482,41 @@ pub const SrcLoc = struct { }; return nodeToSpan(tree, full.ast.return_type); }, + .node_offset_param => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const token_tags = tree.tokens.items(.tag); + const node = src_loc.declRelativeToNodeIndex(node_off); + + var first_tok = tree.firstToken(node); + while (true) switch (token_tags[first_tok - 1]) { + .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1, + else => break, + }; + return tokensToSpan( + tree, + first_tok, + tree.lastToken(node), + first_tok, + ); + }, + .token_offset_param => |token_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const token_tags = tree.tokens.items(.tag); + const main_token = tree.nodes.items(.main_token)[src_loc.parent_decl_node]; + const tok_index = @bitCast(Ast.TokenIndex, token_off + @bitCast(i32, main_token)); + + var first_tok = tok_index; + while (true) switch (token_tags[first_tok - 1]) { + .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1, + else => break, + }; + return tokensToSpan( + tree, + first_tok, + tok_index, + first_tok, + ); + }, .node_offset_anyframe_type => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); @@ -2930,6 +2965,8 @@ pub const LazySrcLoc = union(enum) { /// the return type node. /// The Decl is determined contextually. node_offset_fn_type_ret_ty: i32, + node_offset_param: i32, + token_offset_param: i32, /// The source location points to the type expression of an `anyframe->T` /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a `anyframe->T` expression AST node. Next, navigate @@ -3046,6 +3083,8 @@ pub const LazySrcLoc = union(enum) { .node_offset_fn_type_section, .node_offset_fn_type_cc, .node_offset_fn_type_ret_ty, + .node_offset_param, + .token_offset_param, .node_offset_anyframe_type, .node_offset_lib_name, .node_offset_array_type_len, @@ -5826,6 +5865,52 @@ fn queryFieldSrc( unreachable; } +pub fn paramSrc( + func_node_offset: i32, + gpa: Allocator, + decl: *Decl, + param_i: usize, +) LazySrcLoc { + @setCold(true); + const tree = decl.getFileScope().getTree(gpa) catch |err| { + // In this case we emit a warning + a less precise source location. + log.warn("unable to load {s}: {s}", .{ + decl.getFileScope().sub_file_path, @errorName(err), + }); + return LazySrcLoc.nodeOffset(0); + }; + const node_datas = tree.nodes.items(.data); + const node_tags = tree.nodes.items(.tag); + const node = decl.relativeToNodeIndex(func_node_offset); + var params: [1]Ast.Node.Index = undefined; + const full = switch (node_tags[node]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node), + .fn_proto_multi => tree.fnProtoMulti(node), + .fn_proto_one => tree.fnProtoOne(¶ms, node), + .fn_proto => tree.fnProto(node), + .fn_decl => switch (node_tags[node_datas[node].lhs]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node_datas[node].lhs), + .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs), + .fn_proto_one => tree.fnProtoOne(¶ms, node_datas[node].lhs), + .fn_proto => tree.fnProto(node_datas[node].lhs), + else => unreachable, + }, + else => unreachable, + }; + var it = full.iterate(tree); + while (true) { + if (it.param_i == param_i) { + const param = it.next().?; + if (param.anytype_ellipsis3) |some| { + const main_token = tree.nodes.items(.main_token)[decl.src_node]; + return .{ .token_offset_param = @bitCast(i32, some) - @bitCast(i32, main_token) }; + } + return .{ .node_offset_param = decl.nodeIndexToRelative(param.type_expr) }; + } + _ = it.next(); + } +} + /// Called from `performAllTheWork`, after all AstGen workers have finished, /// and before the main semantic analysis loop begins. pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { diff --git a/src/Sema.zig b/src/Sema.zig index d8a6eddbb6..2001067bbf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7233,6 +7233,7 @@ fn funcCommon( opt_lib_name: ?[]const u8, noalias_bits: u32, ) CompileError!Air.Inst.Ref { + const fn_src = LazySrcLoc.nodeOffset(src_node_offset); const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset }; @@ -7241,10 +7242,6 @@ fn funcCommon( address_space == null or section == .generic or cc == null; - // Check for generic params. - for (block.params.items) |param| { - if (param.ty.tag() == .generic_poison) is_generic = true; - } var destroy_fn_on_error = false; const new_func: *Module.Fn = new_func: { @@ -7304,55 +7301,35 @@ fn funcCommon( 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; - 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); - } + sema.analyzeParameter( + block, + fn_src, + .unneeded, + param, + comptime_params, + i, + &is_generic, + is_extern, + cc_workaround, + ) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + try sema.analyzeParameter( + block, + fn_src, + Module.paramSrc(src_node_offset, sema.gpa, decl, i), + param, + comptime_params, + i, + &is_generic, + is_extern, + cc_workaround, + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; } const ret_poison = if (!is_generic) rp: { @@ -7542,6 +7519,79 @@ fn funcCommon( return sema.addConstant(fn_ty, Value.initPayload(&fn_payload.base)); } +fn analyzeParameter( + sema: *Sema, + block: *Block, + func_src: LazySrcLoc, + param_src: LazySrcLoc, + param: Block.Param, + comptime_params: []bool, + i: usize, + is_generic: *bool, + is_extern: bool, + cc: std.builtin.CallingConvention, +) !void { + const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); + comptime_params[i] = param.is_comptime or requires_comptime; + const this_generic = comptime_params[i] or param.ty.tag() == .generic_poison; + is_generic.* = is_generic.* or this_generic; + if (is_extern and this_generic) { + // TODO this check should exist somewhere for notes. + if (param_src == .unneeded) return error.NeededSourceLocation; + const msg = msg: { + const msg = try sema.errMsg(block, func_src, "extern function cannot be generic", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.errNote(block, param_src, msg, "function is generic because of this parameter", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (this_generic and !Type.fnCallingConventionAllowsZigTypes(cc)) { + return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); + } + 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) 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), + }); + 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); + } +} + fn zirParam( sema: *Sema, block: *Block, @@ -18570,9 +18620,9 @@ fn explainWhyTypeIsNotExtern( .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", .{}); + return 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", .{}); + return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); } try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position); }, diff --git a/test/cases/compile_errors/array_in_c_exported_function.zig b/test/cases/compile_errors/array_in_c_exported_function.zig new file mode 100644 index 0000000000..e938b6afd4 --- /dev/null +++ b/test/cases/compile_errors/array_in_c_exported_function.zig @@ -0,0 +1,16 @@ +export fn zig_array(x: [10]u8) void { + try std.testing.expect(std.mem.eql(u8, &x, "1234567890")); +} +const std = @import("std"); +export fn zig_return_array() [10]u8 { + return "1234567890".*; +} + +// error +// backend=stage2 +// target=native +// +// :1:21: error: parameter of type '[10]u8' not allowed in function with calling convention 'C' +// :1:21: note: arrays are not allowed as a parameter type +// :5:30: error: return type '[10]u8' not allowed in function with calling convention 'C' +// :5:30: note: arrays are not allowed as a return type diff --git a/test/cases/compile_errors/export_function_with_comptime_parameter.zig b/test/cases/compile_errors/export_function_with_comptime_parameter.zig new file mode 100644 index 0000000000..94c8f6de50 --- /dev/null +++ b/test/cases/compile_errors/export_function_with_comptime_parameter.zig @@ -0,0 +1,9 @@ +export fn foo(comptime x: anytype, y: i32) i32{ + return x + y; +} + +// error +// backend=stage2 +// target=native +// +// :1:15: error: generic parameters not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/export_generic_function.zig b/test/cases/compile_errors/export_generic_function.zig new file mode 100644 index 0000000000..4ffbad9df7 --- /dev/null +++ b/test/cases/compile_errors/export_generic_function.zig @@ -0,0 +1,10 @@ +export fn foo(num: anytype) i32 { + _ = num; + return 0; +} + +// error +// backend=stage2 +// target=native +// +// :1:15: error: generic parameters not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/extern_function_with_comptime_parameter.zig b/test/cases/compile_errors/extern_function_with_comptime_parameter.zig new file mode 100644 index 0000000000..cac4c7e5e2 --- /dev/null +++ b/test/cases/compile_errors/extern_function_with_comptime_parameter.zig @@ -0,0 +1,20 @@ +extern fn foo(comptime x: i32, y: i32) i32; +fn f() i32 { + return foo(1, 2); +} +pub extern fn entry1(b: u32, comptime a: [2]u8, c: i32) void; +pub extern fn entry2(b: u32, noalias a: anytype, i43) void; +comptime { _ = f; } +comptime { _ = entry1; } +comptime { _ = entry2; } + +// error +// backend=stage2 +// target=native +// +// :5:12: error: extern function cannot be generic +// :5:30: note: function is generic because of this parameter +// :6:12: error: extern function cannot be generic +// :6:30: note: function is generic because of this parameter +// :1:8: error: extern function cannot be generic +// :1:15: note: function is generic because of this parameter diff --git a/test/cases/compile_errors/function_parameter_is_opaque.zig b/test/cases/compile_errors/function_parameter_is_opaque.zig index 31c477e8bc..57c89bd7f4 100644 --- a/test/cases/compile_errors/function_parameter_is_opaque.zig +++ b/test/cases/compile_errors/function_parameter_is_opaque.zig @@ -23,8 +23,9 @@ export fn entry4() void { // backend=stage2 // target=native // -// :3:24: error: parameter of opaque type 'tmp.FooType' not allowed +// :3:28: 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 +// :8:28: error: parameter of type '@TypeOf(null)' not allowed +// :12:8: error: parameter of opaque type 'tmp.FooType' not allowed +// :1:17: note: opaque declared here +// :17:8: error: parameter of type '@TypeOf(null)' 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 index 0cac6e1e0c..572378d093 100644 --- 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 @@ -5,7 +5,7 @@ export fn entry(foo: Foo) void { _ = foo; } // 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 +// :2:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :2:17: note: enum tag type 'u2' is not extern compatible +// :2:17: 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 index 8ae4ef7523..0007a2014e 100644 --- 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 @@ -9,6 +9,6 @@ export fn entry(foo: Foo) void { _ = foo; } // 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 +// :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :6:17: 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 index a4a96d9172..001d235e18 100644 --- 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 @@ -9,6 +9,6 @@ export fn entry(foo: Foo) void { _ = foo; } // 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 +// :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :6:17: 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/array_in_c_exported_function.zig b/test/cases/compile_errors/stage1/obj/array_in_c_exported_function.zig deleted file mode 100644 index 5137755474..0000000000 --- a/test/cases/compile_errors/stage1/obj/array_in_c_exported_function.zig +++ /dev/null @@ -1,14 +0,0 @@ -export fn zig_array(x: [10]u8) void { - try std.testing.expect(std.mem.eql(u8, &x, "1234567890")); -} -const std = @import("std"); -export fn zig_return_array() [10]u8 { - return "1234567890".*; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:24: error: parameter of type '[10]u8' not allowed in function with calling convention 'C' -// tmp.zig:5:30: error: return type '[10]u8' not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig b/test/cases/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig deleted file mode 100644 index ccfda1ba67..0000000000 --- a/test/cases/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn foo(comptime x: i32, y: i32) i32{ - return x + y; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/export_generic_function.zig b/test/cases/compile_errors/stage1/obj/export_generic_function.zig deleted file mode 100644 index 9f6bcd4cf4..0000000000 --- a/test/cases/compile_errors/stage1/obj/export_generic_function.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn foo(num: anytype) i32 { - _ = num; - return 0; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:15: error: parameter of type 'anytype' not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig b/test/cases/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig deleted file mode 100644 index 93e26b9543..0000000000 --- a/test/cases/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig +++ /dev/null @@ -1,11 +0,0 @@ -extern fn foo(comptime x: i32, y: i32) i32; -fn f() i32 { - return foo(1, 2); -} -export fn entry() usize { return @sizeOf(@TypeOf(f)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C'