From 9630379a8ec593adc65880e814e4c7161b6d5775 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 9 Aug 2023 08:01:31 -0400 Subject: [PATCH 1/3] Sema: fix generic method argument source locations --- src/Sema.zig | 23 +++++++++++--- .../generic_method_call_invalid_coercion.zig | 21 ------------- ...generic_method_call_with_invalid_param.zig | 30 +++++++++++++++++++ 3 files changed, 49 insertions(+), 25 deletions(-) delete mode 100644 test/cases/compile_errors/generic_method_call_invalid_coercion.zig create mode 100644 test/cases/compile_errors/generic_method_call_with_invalid_param.zig diff --git a/src/Sema.zig b/src/Sema.zig index f5f1bc15b3..973cc35be2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -70,6 +70,7 @@ generic_owner: InternPool.Index = .none, /// instantiation can point back to the instantiation site in addition to the /// declaration site. generic_call_src: LazySrcLoc = .unneeded, +generic_bound_arg_src: ?LazySrcLoc = null, /// Corresponds to `generic_call_src`. generic_call_decl: Decl.OptionalIndex = .none, /// The key is types that must be fully resolved prior to machine code @@ -7077,16 +7078,19 @@ fn analyzeCall( const parent_fn_ret_ty_ies = sema.fn_ret_ty_ies; const parent_generic_owner = sema.generic_owner; const parent_generic_call_src = sema.generic_call_src; + const parent_generic_bound_arg_src = sema.generic_bound_arg_src; const parent_generic_call_decl = sema.generic_call_decl; sema.fn_ret_ty = bare_return_type; sema.fn_ret_ty_ies = null; sema.generic_owner = .none; sema.generic_call_src = .unneeded; + sema.generic_bound_arg_src = null; sema.generic_call_decl = .none; defer sema.fn_ret_ty = parent_fn_ret_ty; defer sema.fn_ret_ty_ies = parent_fn_ret_ty_ies; defer sema.generic_owner = parent_generic_owner; defer sema.generic_call_src = parent_generic_call_src; + defer sema.generic_bound_arg_src = parent_generic_bound_arg_src; defer sema.generic_call_decl = parent_generic_call_decl; if (module_fn.analysis(ip).inferred_error_set) { @@ -7545,6 +7549,7 @@ fn instantiateGenericCall( .comptime_args = comptime_args, .generic_owner = generic_owner, .generic_call_src = call_src, + .generic_bound_arg_src = bound_arg_src, .generic_call_decl = block.src_decl.toOptional(), .branch_quota = sema.branch_quota, .branch_count = sema.branch_count, @@ -8583,17 +8588,20 @@ fn resolveGenericBody( const prev_no_partial_func_type = sema.no_partial_func_ty; const prev_generic_owner = sema.generic_owner; const prev_generic_call_src = sema.generic_call_src; + const prev_generic_bound_arg_src = sema.generic_bound_arg_src; const prev_generic_call_decl = sema.generic_call_decl; block.params = .{}; sema.no_partial_func_ty = true; sema.generic_owner = .none; sema.generic_call_src = .unneeded; + sema.generic_bound_arg_src = null; sema.generic_call_decl = .none; defer { block.params = prev_params; sema.no_partial_func_ty = prev_no_partial_func_type; sema.generic_owner = prev_generic_owner; sema.generic_call_src = prev_generic_call_src; + sema.generic_bound_arg_src = prev_generic_bound_arg_src; sema.generic_call_decl = prev_generic_call_decl; } @@ -9235,17 +9243,20 @@ fn zirParam( const prev_no_partial_func_type = sema.no_partial_func_ty; const prev_generic_owner = sema.generic_owner; const prev_generic_call_src = sema.generic_call_src; + const prev_generic_bound_arg_src = sema.generic_bound_arg_src; const prev_generic_call_decl = sema.generic_call_decl; block.params = .{}; sema.no_partial_func_ty = true; sema.generic_owner = .none; sema.generic_call_src = .unneeded; + sema.generic_bound_arg_src = null; sema.generic_call_decl = .none; defer { block.params = prev_params; sema.no_partial_func_ty = prev_no_partial_func_type; sema.generic_owner = prev_generic_owner; sema.generic_call_src = prev_generic_call_src; + sema.generic_bound_arg_src = prev_generic_bound_arg_src; sema.generic_call_decl = prev_generic_call_decl; } @@ -9319,10 +9330,12 @@ fn zirParam( sema.comptime_args[param_index] = val.toIntern(); return; } - const arg_src: LazySrcLoc = if (sema.generic_call_src == .node_offset) .{ .call_arg = .{ + const arg_src: LazySrcLoc = if (param_index == 0 and sema.generic_bound_arg_src != null) + sema.generic_bound_arg_src.? + else if (sema.generic_call_src == .node_offset) .{ .call_arg = .{ .decl = sema.generic_call_decl.unwrap().?, .call_node_offset = sema.generic_call_src.node_offset.x, - .arg_index = param_index, + .arg_index = param_index - @intFromBool(sema.generic_bound_arg_src != null), } } else src; const msg = msg: { const src_loc = arg_src.toSrcLoc(mod.declPtr(block.src_decl), mod); @@ -9385,10 +9398,12 @@ fn zirParamAnytype( sema.comptime_args[param_index] = opv.toIntern(); return; } - const arg_src: LazySrcLoc = if (sema.generic_call_src == .node_offset) .{ .call_arg = .{ + const arg_src: LazySrcLoc = if (param_index == 0 and sema.generic_bound_arg_src != null) + sema.generic_bound_arg_src.? + else if (sema.generic_call_src == .node_offset) .{ .call_arg = .{ .decl = sema.generic_call_decl.unwrap().?, .call_node_offset = sema.generic_call_src.node_offset.x, - .arg_index = param_index, + .arg_index = param_index - @intFromBool(sema.generic_bound_arg_src != null), } } else src; if (comptime_syntax) { diff --git a/test/cases/compile_errors/generic_method_call_invalid_coercion.zig b/test/cases/compile_errors/generic_method_call_invalid_coercion.zig deleted file mode 100644 index 59fa50da37..0000000000 --- a/test/cases/compile_errors/generic_method_call_invalid_coercion.zig +++ /dev/null @@ -1,21 +0,0 @@ -export fn callBoolMethod() void { - const s = S{}; - s.boolMethod({}); -} - -export fn callVoidMethod() void { - const s = S{}; - s.voidMethod(false); -} - -const S = struct { - fn boolMethod(comptime _: @This(), _: bool) void {} - fn voidMethod(comptime _: @This(), _: void) void {} -}; - -// error -// backend=stage2 -// target=native -// -// :3:18: error: expected type 'bool', found 'void' -// :8:18: error: expected type 'void', found 'bool' diff --git a/test/cases/compile_errors/generic_method_call_with_invalid_param.zig b/test/cases/compile_errors/generic_method_call_with_invalid_param.zig new file mode 100644 index 0000000000..0cea61301c --- /dev/null +++ b/test/cases/compile_errors/generic_method_call_with_invalid_param.zig @@ -0,0 +1,30 @@ +export fn callBoolMethodWithVoid() void { + const s = S{}; + s.boolMethod({}); +} + +export fn callVoidMethodWithBool() void { + const s = S{}; + s.voidMethod(false); +} + +export fn callComptimeBoolMethodWithRuntimeBool() void { + const s = S{}; + var arg = true; + s.comptimeBoolMethod(arg); +} + +const S = struct { + fn boolMethod(comptime _: @This(), _: bool) void {} + fn voidMethod(comptime _: @This(), _: void) void {} + fn comptimeBoolMethod(comptime _: @This(), comptime _: bool) void {} +}; + +// error +// backend=stage2 +// target=native +// +// :3:18: error: expected type 'bool', found 'void' +// :8:18: error: expected type 'void', found 'bool' +// :14:26: error: runtime-known argument passed to comptime parameter +// :20:57: note: declared comptime here From 736df276632ad66294a7491a5e35a039dd2c836f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 9 Aug 2023 09:26:16 -0400 Subject: [PATCH 2/3] Sema: use the correct decl for generic argument source locations Closes #16746 --- src/Sema.zig | 37 ++++++++++++++++++------------------- test/compile_errors.zig | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 973cc35be2..ac9886fa62 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9219,6 +9219,21 @@ fn finishFunc( return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty); } +fn genericArgSrcLoc(sema: *Sema, block: *Block, param_index: u32, param_src: LazySrcLoc) Module.SrcLoc { + const mod = sema.mod; + if (sema.generic_owner == .none) return param_src.toSrcLoc(mod.declPtr(block.src_decl), mod); + const arg_decl = sema.generic_call_decl.unwrap().?; + const arg_src: LazySrcLoc = if (param_index == 0 and sema.generic_bound_arg_src != null) + sema.generic_bound_arg_src.? + else + .{ .call_arg = .{ + .decl = arg_decl, + .call_node_offset = sema.generic_call_src.node_offset.x, + .arg_index = param_index - @intFromBool(sema.generic_bound_arg_src != null), + } }; + return arg_src.toSrcLoc(mod.declPtr(arg_decl), mod); +} + fn zirParam( sema: *Sema, block: *Block, @@ -9226,7 +9241,6 @@ fn zirParam( param_index: u32, comptime_syntax: bool, ) CompileError!void { - const mod = sema.mod; const gpa = sema.gpa; const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; const src = inst_data.src(); @@ -9330,15 +9344,8 @@ fn zirParam( sema.comptime_args[param_index] = val.toIntern(); return; } - const arg_src: LazySrcLoc = if (param_index == 0 and sema.generic_bound_arg_src != null) - sema.generic_bound_arg_src.? - else if (sema.generic_call_src == .node_offset) .{ .call_arg = .{ - .decl = sema.generic_call_decl.unwrap().?, - .call_node_offset = sema.generic_call_src.node_offset.x, - .arg_index = param_index - @intFromBool(sema.generic_bound_arg_src != null), - } } else src; const msg = msg: { - const src_loc = arg_src.toSrcLoc(mod.declPtr(block.src_decl), mod); + const src_loc = sema.genericArgSrcLoc(block, param_index, src); const msg = try Module.ErrorMsg.create(gpa, src_loc, "{s}", .{ @as([]const u8, "runtime-known argument passed to comptime parameter"), }); @@ -9384,7 +9391,6 @@ fn zirParamAnytype( param_index: u32, comptime_syntax: bool, ) CompileError!void { - const mod = sema.mod; const gpa = sema.gpa; const inst_data = sema.code.instructions.items(.data)[inst].str_tok; const param_name: Zir.NullTerminatedString = @enumFromInt(inst_data.start); @@ -9398,13 +9404,6 @@ fn zirParamAnytype( sema.comptime_args[param_index] = opv.toIntern(); return; } - const arg_src: LazySrcLoc = if (param_index == 0 and sema.generic_bound_arg_src != null) - sema.generic_bound_arg_src.? - else if (sema.generic_call_src == .node_offset) .{ .call_arg = .{ - .decl = sema.generic_call_decl.unwrap().?, - .call_node_offset = sema.generic_call_src.node_offset.x, - .arg_index = param_index - @intFromBool(sema.generic_bound_arg_src != null), - } } else src; if (comptime_syntax) { if (try sema.resolveMaybeUndefVal(air_ref)) |val| { @@ -9412,7 +9411,7 @@ fn zirParamAnytype( return; } const msg = msg: { - const src_loc = arg_src.toSrcLoc(mod.declPtr(block.src_decl), mod); + const src_loc = sema.genericArgSrcLoc(block, param_index, src); const msg = try Module.ErrorMsg.create(gpa, src_loc, "{s}", .{ @as([]const u8, "runtime-known argument passed to comptime parameter"), }); @@ -9432,7 +9431,7 @@ fn zirParamAnytype( return; } const msg = msg: { - const src_loc = arg_src.toSrcLoc(mod.declPtr(block.src_decl), mod); + const src_loc = sema.genericArgSrcLoc(block, param_index, src); const msg = try Module.ErrorMsg.create(gpa, src_loc, "{s}", .{ @as([]const u8, "runtime-known argument passed to comptime-only type parameter"), }); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5a60ef2e55..8675eb9ed7 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -183,4 +183,40 @@ pub fn addCases(ctx: *Cases) !void { ":1:1: note: invalid byte: '\\xff'", }); } + + { + const case = ctx.obj("imported generic method call with invalid param", .{}); + + case.addError( + \\pub const import = @import("import.zig"); + \\ + \\export fn callComptimeBoolFunctionWithRuntimeBool(x: bool) void { + \\ import.comptimeBoolFunction(x); + \\} + \\ + \\export fn callComptimeAnytypeFunctionWithRuntimeBool(x: bool) void { + \\ import.comptimeAnytypeFunction(x); + \\} + \\ + \\export fn callAnytypeFunctionWithRuntimeComptimeOnlyType(x: u32) void { + \\ const S = struct { x: u32, y: type }; + \\ import.anytypeFunction(S{ .x = x, .y = u32 }); + \\} + , &[_][]const u8{ + ":4:33: error: runtime-known argument passed to comptime parameter", + ":1:38: note: declared comptime here", + ":8:36: error: runtime-known argument passed to comptime parameter", + ":2:41: note: declared comptime here", + ":13:29: error: runtime-known argument passed to comptime-only type parameter", + ":3:24: note: declared here", + ":12:35: note: struct requires comptime because of this field", + ":12:35: note: types are not available at runtime", + }); + + case.addSourceFile("import.zig", + \\pub fn comptimeBoolFunction(comptime _: bool) void {} + \\pub fn comptimeAnytypeFunction(comptime _: anytype) void {} + \\pub fn anytypeFunction(_: anytype) void {} + ); + } } From 57470e833e4ac545a75ad7b3dd6830fa666cd325 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 9 Aug 2023 10:07:55 -0400 Subject: [PATCH 3/3] Module: implement `span` for `.call_arg` of a `@call` Closes #16750 --- src/Module.zig | 56 +++++++++++++------ .../builtin_call_with_invalid_param.zig | 12 ++++ 2 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 test/cases/compile_errors/builtin_call_with_invalid_param.zig diff --git a/src/Module.zig b/src/Module.zig index f5438ebb9f..67d0494a56 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2133,10 +2133,40 @@ pub const SrcLoc = struct { .call_arg => |call_arg| { const tree = try src_loc.file_scope.getTree(gpa); const node = src_loc.declRelativeToNodeIndex(call_arg.call_node_offset); - var buf: [1]Ast.Node.Index = undefined; - const call_full = tree.fullCall(&buf, node).?; - const src_node = call_full.ast.params[call_arg.arg_index]; - return nodeToSpan(tree, src_node); + var buf: [2]Ast.Node.Index = undefined; + const call_full = tree.fullCall(buf[0..1], node) orelse { + const node_tags = tree.nodes.items(.tag); + assert(node_tags[node] == .builtin_call); + const call_args_node = tree.extra_data[tree.nodes.items(.data)[node].rhs - 1]; + switch (node_tags[call_args_node]) { + .array_init_one, + .array_init_one_comma, + .array_init_dot_two, + .array_init_dot_two_comma, + .array_init_dot, + .array_init_dot_comma, + .array_init, + .array_init_comma, + => { + const full = tree.fullArrayInit(&buf, call_args_node).?.ast.elements; + return nodeToSpan(tree, full[call_arg.arg_index]); + }, + .struct_init_one, + .struct_init_one_comma, + .struct_init_dot_two, + .struct_init_dot_two_comma, + .struct_init_dot, + .struct_init_dot_comma, + .struct_init, + .struct_init_comma, + => { + const full = tree.fullStructInit(&buf, call_args_node).?.ast.fields; + return nodeToSpan(tree, full[call_arg.arg_index]); + }, + else => return nodeToSpan(tree, call_args_node), + } + }; + return nodeToSpan(tree, call_full.ast.params[call_arg.arg_index]); }, .fn_proto_param => |fn_proto_param| { const tree = try src_loc.file_scope.getTree(gpa); @@ -5926,21 +5956,15 @@ pub fn argSrc( }); return LazySrcLoc.nodeOffset(0); }; - const node_tags = tree.nodes.items(.tag); const node = decl.relativeToNodeIndex(call_node_offset); var args: [1]Ast.Node.Index = undefined; - const full = switch (node_tags[node]) { - .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(&args, node), - .call, .call_comma, .async_call, .async_call_comma => tree.callFull(node), - .builtin_call => { - const node_datas = tree.nodes.items(.data); - const call_args_node = tree.extra_data[node_datas[node].rhs - 1]; - const call_args_offset = decl.nodeIndexToRelative(call_args_node); - return mod.initSrc(call_args_offset, decl, arg_i); - }, - else => unreachable, + const call_full = tree.fullCall(&args, node) orelse { + assert(tree.nodes.items(.tag)[node] == .builtin_call); + const call_args_node = tree.extra_data[tree.nodes.items(.data)[node].rhs - 1]; + const call_args_offset = decl.nodeIndexToRelative(call_args_node); + return mod.initSrc(call_args_offset, decl, arg_i); }; - return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full.ast.params[arg_i])); + return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(call_full.ast.params[arg_i])); } pub fn initSrc( diff --git a/test/cases/compile_errors/builtin_call_with_invalid_param.zig b/test/cases/compile_errors/builtin_call_with_invalid_param.zig new file mode 100644 index 0000000000..b0ce2c13b2 --- /dev/null +++ b/test/cases/compile_errors/builtin_call_with_invalid_param.zig @@ -0,0 +1,12 @@ +export fn builtinCallBoolFunctionInlineWithVoid() void { + @call(.always_inline, boolFunction, .{{}}); +} + +fn boolFunction(_: bool) void {} + +// error +// backend=stage2 +// target=native +// +// :2:43: error: expected type 'bool', found 'void' +// :5:20: note: parameter type declared here