aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2022-07-19 20:20:57 +0300
committerAndrew Kelley <andrew@ziglang.org>2022-07-21 12:21:30 -0700
commit79ef0cdf306c48d3cc447ce14530db4021f8c5de (patch)
treed97ae5e97c97e0f3545b0d1e80af1ecf4cbf0492 /src
parent83b2d2cd3eb701b43d17404e73933bc00f7be828 (diff)
downloadzig-79ef0cdf306c48d3cc447ce14530db4021f8c5de.tar.gz
zig-79ef0cdf306c48d3cc447ce14530db4021f8c5de.zip
Sema: better function parameter source location
Diffstat (limited to 'src')
-rw-r--r--src/Module.zig85
-rw-r--r--src/Sema.zig158
2 files changed, 189 insertions, 54 deletions
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(&params, node),
+ .fn_proto_multi => tree.fnProtoMulti(node),
+ .fn_proto_one => tree.fnProtoOne(&params, node),
+ .fn_proto => tree.fnProto(node),
+ .fn_decl => switch (node_tags[node_datas[node].lhs]) {
+ .fn_proto_simple => tree.fnProtoSimple(&params, node_datas[node].lhs),
+ .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs),
+ .fn_proto_one => tree.fnProtoOne(&params, 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);
},