mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 22:35:24 +00:00
Sema: better function parameter source location
This commit is contained in:
parent
83b2d2cd3e
commit
79ef0cdf30
@ -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 {
|
||||
|
||||
158
src/Sema.zig
158
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);
|
||||
},
|
||||
|
||||
16
test/cases/compile_errors/array_in_c_exported_function.zig
Normal file
16
test/cases/compile_errors/array_in_c_exported_function.zig
Normal file
@ -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
|
||||
@ -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'
|
||||
10
test/cases/compile_errors/export_generic_function.zig
Normal file
10
test/cases/compile_errors/export_generic_function.zig
Normal file
@ -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'
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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'
|
||||
@ -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'
|
||||
@ -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'
|
||||
@ -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'
|
||||
Loading…
x
Reference in New Issue
Block a user