Zig 0.17.0-dev (Split by item)

Shortcut keys
?
Show these tips
S
Focus search field
Esc
Clear focus & close tips
U
Jump to source code
↑
Move up in search results
↓
Move down in search results
↵
Go to active search result
This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig standard library. Private Function

fnDecl

Private Function
std.zig.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

Params

Errors

OutOfMemory
AnalysisFail

File

zig/AstGen.zig:3967
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
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(.{});
    // We don't add the full source yet, because we also need the prototype hash!
    // The source slice is added towards the *end* of this function.
    astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));

    // missing function name already checked in scanContainer()
    const fn_name_token = fn_proto.name_token.?;

    // We insert this at the beginning so that its instruction index marks the
    // 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;

    // Set this now, since parameter types, return type, etc may be generic.
    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); // validated by parser (TODO why???)
    }

    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) {
        // We include a function *type*, not a value.
        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) {
        // We include a function *value*, not a type.
        astgen.restoreSourceCursor(saved_cursor);
        try astgen.fnDeclInner(&value_gz, &value_gz.base, saved_cursor, decl_inst, decl_node, body_node.unwrap().?, fn_proto);
    }

    // *Now* we can incorporate the full source code into the hasher.
    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,
    });
}