feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.fnDecl
fn fnDecl(
astgen: *AstGen,
gz: *GenZir,
scope: *Scope,
wip_decls: *WipDecls,
decl_node: Ast.Node.Index,
body_node: Ast.Node.OptionalIndex,
fn_proto: Ast.full.FnProto,
) InnerError!void
File
Code
fn fnDecl(
astgen: *AstGen,
gz: *GenZir,
scope: *Scope,
wip_decls: *WipDecls,
decl_node: Ast.Node.Index,
body_node: Ast.Node.OptionalIndex,
fn_proto: Ast.full.FnProto,
) InnerError!void {
const tree = astgen.tree;
const old_hasher = astgen.src_hasher;
defer astgen.src_hasher = old_hasher;
astgen.src_hasher = std.zig.SrcHasher.init(.{});
// The source slice is added towards the *end* of this function.
astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
const fn_name_token = fn_proto.name_token.?;
// start of the top level declaration.
const decl_inst = try gz.makeDeclaration(fn_proto.ast.proto_node);
astgen.advanceSourceCursorToNode(decl_node);
const saved_cursor = astgen.saveSourceCursor();
const decl_column = astgen.source_column;
const prev_within_fn = astgen.within_fn;
defer astgen.within_fn = prev_within_fn;
astgen.within_fn = true;
const is_pub = fn_proto.visib_token != null;
const is_export = blk: {
const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
break :blk tree.tokenTag(maybe_export_token) == .keyword_export;
};
const is_extern = blk: {
const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
break :blk tree.tokenTag(maybe_extern_token) == .keyword_extern;
};
const has_inline_keyword = blk: {
const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
break :blk tree.tokenTag(maybe_inline_token) == .keyword_inline;
};
const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: {
const lib_name_str = try astgen.strLitAsString(lib_name_token);
const lib_name_slice = astgen.string_bytes.items[@backingInt(lib_name_str.index)..][0..lib_name_str.len];
if (mem.findScalar(u8, lib_name_slice, 0) != null) {
return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
} else if (lib_name_str.len == 0) {
return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
}
break :blk lib_name_str.index;
} else .empty;
if (fn_proto.ast.callconv_expr != .none and has_inline_keyword) {
return astgen.failNode(
fn_proto.ast.callconv_expr.unwrap().?,
"explicit callconv incompatible with inline keyword",
.{},
);
}
const return_type = fn_proto.ast.return_type.unwrap().?;
const maybe_bang = tree.firstToken(return_type) - 1;
const is_inferred_error = tree.tokenTag(maybe_bang) == .bang;
if (body_node == .none) {
if (!is_extern) {
return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
}
if (is_inferred_error) {
return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
}
} else {
assert(!is_extern);
}
wip_decls.nextDecl(decl_inst);
var type_gz: GenZir = .{
.is_comptime = true,
.decl_node_index = fn_proto.ast.proto_node,
.decl_line = astgen.source_line,
.parent = scope,
.astgen = astgen,
.instructions = gz.instructions,
.instructions_top = gz.instructions.items.len,
};
defer type_gz.unstack();
if (is_extern) {
const type_inst = try fnProtoExprInner(&type_gz, &type_gz.base, .{ .rl = .none }, decl_node, fn_proto, true);
_ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, decl_node);
}
var align_gz = type_gz.makeSubBlock(scope);
defer align_gz.unstack();
if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
astgen.restoreSourceCursor(saved_cursor);
const inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, align_expr);
_ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
}
var linksection_gz = align_gz.makeSubBlock(scope);
defer linksection_gz.unstack();
if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
astgen.restoreSourceCursor(saved_cursor);
const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_expr);
_ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
}
var addrspace_gz = linksection_gz.makeSubBlock(scope);
defer addrspace_gz.unstack();
if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
astgen.restoreSourceCursor(saved_cursor);
const addrspace_ty = try addrspace_gz.addStdLangValue(addrspace_expr, .address_space);
const inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_expr);
_ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
}
var value_gz = addrspace_gz.makeSubBlock(scope);
defer value_gz.unstack();
if (!is_extern) {
astgen.restoreSourceCursor(saved_cursor);
try astgen.fnDeclInner(&value_gz, &value_gz.base, saved_cursor, decl_inst, decl_node, body_node.unwrap().?, fn_proto);
}
astgen.src_hasher.update(tree.getNodeSource(decl_node));
var hash: std.zig.SrcHash = undefined;
astgen.src_hasher.final(&hash);
try setDeclaration(decl_inst, .{
.src_hash = hash,
.src_line = type_gz.decl_line,
.src_column = decl_column,
.kind = .@"const",
.name = try astgen.identAsString(fn_name_token),
.is_pub = is_pub,
.is_threadlocal = false,
.linkage = if (is_extern) .@"extern" else if (is_export) .@"export" else .normal,
.lib_name = lib_name,
.type_gz = &type_gz,
.align_gz = &align_gz,
.linksection_gz = &linksection_gz,
.addrspace_gz = &addrspace_gz,
.value_gz = &value_gz,
});
}