BRANCH_TODO

 * nested function decl: how to refer to params?
 * look for cached zir code
 * save zir code to cache
 * keep track of file dependencies/dependants
 * unload files from memory when a dependency is dropped
 * implement zir error notes 

 * implement the new AstGen compile errors

 * get rid of failed_root_src_file
 * get rid of Scope.DeclRef
 * get rid of optional_type_from_ptr_elem
 * handle decl collision with usingnamespace
 * the decl doing the looking up needs to create a decl dependency
   on each usingnamespace decl
 * handle usingnamespace cycles

 * compile error for return inside defer expression

 * when block has noreturn statement
   - avoid emitting defers
   - compile error for unreachable code
 
 * detect `return error.Foo` and emit ZIR that unconditionally generates errdefers
 * `return`: check return operand and generate errdefers if necessary

 * have failed_trees and just put the file in there
   - this way we can emit all the parse errors not just the first one
   - but maybe we want just the first one?

 * need a var decl zir instruction which includes the name because we need to do the
   compile error for a local shadowing a decl with Sema looking up the decl name.
   - this means LocalVal and LocalPtr should use the string table

 * sort compile errors before presenting them to eliminate nondeterministic error reporting

 * when handling decls, catch the error and continue, so that
   AstGen can report more than one compile error.

 * AstGen: inside a nosuspend block, emit function calls as nosuspend calls
 * AstGen: add result location pointers to function calls

    const container_name_hash: Scope.NameHash = if (found_pkg) |pkg|
        pkg.namespace_hash
    else
        std.zig.hashName(cur_pkg.namespace_hash, "/", resolved_path);

    file_scope.* = .{
        .root_container = .{
            .parent = null,
            .file_scope = file_scope,
            .decls = .{},
            .ty = struct_ty,
            .parent_name_hash = container_name_hash,
        },
    };
    mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
        error.AnalysisFail => {
            assert(mod.comp.totalErrorCount() != 0);
        },
        else => |e| return e,
    };
    return file_scope;



            // Until then we simulate a full cache miss. Source files could have been loaded
            // for any reason; to force a refresh we unload now.
            module.unloadFile(module.root_scope);
            module.failed_root_src_file = null;
            module.analyzeNamespace(&module.root_scope.root_container) catch |err| switch (err) {
                error.AnalysisFail => {
                    assert(self.totalErrorCount() != 0);
                },
                error.OutOfMemory => return error.OutOfMemory,
                else => |e| {
                    module.failed_root_src_file = e;
                },
            };

            // TODO only analyze imports if they are still referenced
            for (module.import_table.items()) |entry| {
                module.unloadFile(entry.value);
                module.analyzeNamespace(&entry.value.root_container) catch |err| switch (err) {
                    error.AnalysisFail => {
                        assert(self.totalErrorCount() != 0);
                    },
                    else => |e| return e,
                };
            }


pub fn createContainerDecl(
    mod: *Module,
    scope: *Scope,
    base_token: std.zig.ast.TokenIndex,
    decl_arena: *std.heap.ArenaAllocator,
    typed_value: TypedValue,
) !*Decl {
    const scope_decl = scope.ownerDecl().?;
    const name = try mod.getAnonTypeName(scope, base_token);
    defer mod.gpa.free(name);
    const name_hash = scope.namespace().fullyQualifiedNameHash(name);
    const src_hash: std.zig.SrcHash = undefined;
    const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_node, name_hash, src_hash);
    const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);

    decl_arena_state.* = decl_arena.state;
    new_decl.typed_value = .{
        .most_recent = .{
            .typed_value = typed_value,
            .arena = decl_arena_state,
        },
    };
    new_decl.analysis = .complete;
    new_decl.generation = mod.generation;

    return new_decl;
}

fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
    // TODO add namespaces, generic function signatrues
    const tree = scope.tree();
    const token_tags = tree.tokens.items(.tag);
    const base_name = switch (token_tags[base_token]) {
        .keyword_struct => "struct",
        .keyword_enum => "enum",
        .keyword_union => "union",
        .keyword_opaque => "opaque",
        else => unreachable,
    };
    const loc = tree.tokenLocation(0, base_token);
    return std.fmt.allocPrint(mod.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column });
}


    const parent_name_hash: Scope.NameHash = if (found_pkg) |pkg|
        pkg.namespace_hash
    else
        std.zig.hashName(cur_pkg.namespace_hash, "/", resolved_path);

    // We need a Decl to pass to AstGen and collect dependencies. But ultimately we
    // want to pass them on to the Decl for the struct that represents the file.
    var tmp_namespace: Scope.Namespace = .{
        .parent = null,
        .file_scope = new_file,
        .parent_name_hash = parent_name_hash,
        .ty = Type.initTag(.type),
    };

    const tree = try mod.getAstTree(new_file);


    const top_decl = try mod.createNewDecl(
        &tmp_namespace,
        resolved_path,
        0,
        parent_name_hash,
        std.zig.hashSrc(tree.source),
    );
    defer {
        mod.decl_table.removeAssertDiscard(parent_name_hash);
        top_decl.destroy(mod);
    }

    var gen_scope_arena = std.heap.ArenaAllocator.init(gpa);
    defer gen_scope_arena.deinit();

    var astgen = try AstGen.init(mod, top_decl, &gen_scope_arena.allocator);
    defer astgen.deinit();

    var gen_scope: Scope.GenZir = .{
        .force_comptime = true,
        .parent = &new_file.base,
        .astgen = &astgen,
    };
    defer gen_scope.instructions.deinit(gpa);

    const container_decl: ast.full.ContainerDecl = .{
        .layout_token = null,
        .ast = .{
            .main_token = undefined,
            .enum_token = null,
            .members = tree.rootDecls(),
            .arg = 0,
        },
    };

    const struct_decl_ref = try AstGen.structDeclInner(
        &gen_scope,
        &gen_scope.base,
        0,
        container_decl,
        .struct_decl,
    );
    _ = try gen_scope.addBreak(.break_inline, 0, struct_decl_ref);

    var code = try gen_scope.finish();
    defer code.deinit(gpa);
    if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
        code.dump(gpa, "import", &gen_scope.base, 0) catch {};
    }

    var sema: Sema = .{
        .mod = mod,
        .gpa = gpa,
        .arena = &gen_scope_arena.allocator,
        .code = code,
        .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
        .owner_decl = top_decl,
        .namespace = top_decl.namespace,
        .func = null,
        .owner_func = null,
        .param_inst_list = &.{},
    };
    var block_scope: Scope.Block = .{
        .parent = null,
        .sema = &sema,
        .src_decl = top_decl,
        .instructions = .{},
        .inlining = null,
        .is_comptime = true,
    };
    defer block_scope.instructions.deinit(gpa);

    const init_inst_zir_ref = try sema.rootAsRef(&block_scope);
    const analyzed_struct_inst = try sema.resolveInst(init_inst_zir_ref);
    assert(analyzed_struct_inst.ty.zigTypeTag() == .Type);
    const val = analyzed_struct_inst.value().?;
    const struct_ty = try val.toType(&gen_scope_arena.allocator);
    const struct_decl = struct_ty.getOwnerDecl();

    struct_decl.contents_hash = top_decl.contents_hash;
    new_file.namespace = struct_ty.getNamespace().?;
    new_file.namespace.parent = null;
    //new_file.namespace.parent_name_hash = tmp_namespace.parent_name_hash;

    // Transfer the dependencies to `owner_decl`.
    assert(top_decl.dependants.count() == 0);
    for (top_decl.dependencies.items()) |entry| {
        const dep = entry.key;
        dep.removeDependant(top_decl);
        if (dep == struct_decl) continue;
        _ = try mod.declareDeclDependency(struct_decl, dep);
    }

    return new_file;






pub fn analyzeFile(mod: *Module, file: *Scope.File) !void {
    // We call `getAstTree` here so that `analyzeFile` has the error set that includes
    // file system operations, but `analyzeNamespace` does not.
    const tree = try mod.getAstTree(file.namespace.file_scope);
    const decls = tree.rootDecls();
    return mod.analyzeNamespace(file.namespace, decls);
}

/// Returns `true` if the Decl type changed.
/// Returns `true` if this is the first time analyzing the Decl.
/// Returns `false` otherwise.
fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
    const tracy = trace(@src());
    defer tracy.end();

    const tree = try mod.getAstTree(decl.namespace.file_scope);
    const node_tags = tree.nodes.items(.tag);
    const node_datas = tree.nodes.items(.data);
    const decl_node = decl.src_node;
    switch (node_tags[decl_node]) {
        .fn_decl => {
            const fn_proto = node_datas[decl_node].lhs;
            const body = node_datas[decl_node].rhs;
            switch (node_tags[fn_proto]) {
                .fn_proto_simple => {
                    var params: [1]ast.Node.Index = undefined;
                    return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoSimple(&params, fn_proto));
                },
                .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoMulti(fn_proto)),
                .fn_proto_one => {
                    var params: [1]ast.Node.Index = undefined;
                    return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoOne(&params, fn_proto));
                },
                .fn_proto => return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProto(fn_proto)),
                else => unreachable,
            }
        },
        .fn_proto_simple => {
            var params: [1]ast.Node.Index = undefined;
            return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoSimple(&params, decl_node));
        },
        .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoMulti(decl_node)),
        .fn_proto_one => {
            var params: [1]ast.Node.Index = undefined;
            return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoOne(&params, decl_node));
        },
        .fn_proto => return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProto(decl_node)),

        .global_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.globalVarDecl(decl_node)),
        .local_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.localVarDecl(decl_node)),
        .simple_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.simpleVarDecl(decl_node)),
        .aligned_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.alignedVarDecl(decl_node)),

        .@"comptime" => {
            decl.analysis = .in_progress;

            // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
            var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa);
            defer analysis_arena.deinit();

            var code: Zir = blk: {
                var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator);
                defer astgen.deinit();

                var gen_scope: Scope.GenZir = .{
                    .force_comptime = true,
                    .parent = &decl.namespace.base,
                    .astgen = &astgen,
                };
                defer gen_scope.instructions.deinit(mod.gpa);

                const block_expr = node_datas[decl_node].lhs;
                _ = try AstGen.comptimeExpr(&gen_scope, &gen_scope.base, .none, block_expr);
                _ = try gen_scope.addBreak(.break_inline, 0, .void_value);

                const code = try gen_scope.finish();
                if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
                    code.dump(mod.gpa, "comptime_block", &gen_scope.base, 0) catch {};
                }
                break :blk code;
            };
            defer code.deinit(mod.gpa);

            var sema: Sema = .{
                .mod = mod,
                .gpa = mod.gpa,
                .arena = &analysis_arena.allocator,
                .code = code,
                .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len),
                .owner_decl = decl,
                .namespace = decl.namespace,
                .func = null,
                .owner_func = null,
                .param_inst_list = &.{},
            };
            var block_scope: Scope.Block = .{
                .parent = null,
                .sema = &sema,
                .src_decl = decl,
                .instructions = .{},
                .inlining = null,
                .is_comptime = true,
            };
            defer block_scope.instructions.deinit(mod.gpa);

            _ = try sema.root(&block_scope);

            decl.analysis = .complete;
            decl.generation = mod.generation;
            return true;
        },
        .@"usingnamespace" => {
            decl.analysis = .in_progress;

            var code: Zir = blk: {
                var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator);
                defer astgen.deinit();

                var gen_scope: Scope.GenZir = .{
                    .force_comptime = true,
                    .parent = &decl.namespace.base,
                    .astgen = &astgen,
                };
                defer gen_scope.instructions.deinit(mod.gpa);

                const ns_type = try AstGen.typeExpr(&gen_scope, &gen_scope.base, type_expr);

            };
            try decl.namespace.usingnamespace_set.put(mod.gpa, ty.getNamespace().?, is_pub);

            decl.analysis = .complete;
            decl.generation = mod.generation;
            return true;
        },
        else => unreachable,
    }
}

fn astgenAndSemaFn(
    mod: *Module,
    decl: *Decl,
    tree: ast.Tree,
    body_node: ast.Node.Index,
    fn_proto: ast.full.FnProto,
) !bool {
    const is_inline = fn_type.fnCallingConvention() == .Inline;
    const anal_state: Fn.Analysis = if (is_inline) .inline_only else .queued;

    new_func.* = .{
        .state = anal_state,
        .zir = fn_zir,
        .body = undefined,
        .owner_decl = decl,
    };
    fn_payload.* = .{
        .base = .{ .tag = .function },
        .data = new_func,
    };

    var prev_type_has_bits = false;
    var prev_is_inline = false;
    var type_changed = true;

    if (decl.typedValueManaged()) |tvm| {
        prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
        type_changed = !tvm.typed_value.ty.eql(fn_type);
        if (tvm.typed_value.val.castTag(.function)) |payload| {
            const prev_func = payload.data;
            prev_is_inline = prev_func.state == .inline_only;
            prev_func.deinit(mod.gpa);
        }

        tvm.deinit(mod.gpa);
    }

    decl_arena_state.* = decl_arena.state;
    decl.typed_value = .{
        .most_recent = .{
            .typed_value = .{
                .ty = fn_type,
                .val = Value.initPayload(&fn_payload.base),
            },
            .arena = decl_arena_state,
        },
    };
    decl.analysis = .complete;
    decl.generation = mod.generation;

    if (!is_inline and fn_type.hasCodeGenBits()) {
        // We don't fully codegen the decl until later, but we do need to reserve a global
        // offset table index for it. This allows us to codegen decls out of dependency order,
        // increasing how many computations can be done in parallel.
        try mod.comp.bin_file.allocateDeclIndexes(decl);
        try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
        if (type_changed and mod.emit_h != null) {
            try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
        }
    } else if (!prev_is_inline and prev_type_has_bits) {
        mod.comp.bin_file.freeDecl(decl);
    }

    if (fn_proto.extern_export_token) |maybe_export_token| {
        if (token_tags[maybe_export_token] == .keyword_export) {
            if (is_inline) {
                return mod.failTok(
                    &block_scope.base,
                    maybe_export_token,
                    "export of inline function",
                    .{},
                );
            }
            const export_src = decl.tokSrcLoc(maybe_export_token);
            const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString
            // The scope needs to have the decl in it.
            try mod.analyzeExport(&block_scope.base, export_src, name, decl);
        }
    }
    return type_changed or is_inline != prev_is_inline;
}

fn astgenAndSemaVarDecl(
    mod: *Module,
    decl: *Decl,
    tree: ast.Tree,
    var_decl: ast.full.VarDecl,
) !bool {
    const token_tags = tree.tokens.items(.tag);

}


    /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
    pub fn tree(scope: *Scope) *const ast.Tree {
        switch (scope.tag) {
            .file => return &scope.cast(File).?.tree,
            .block => return &scope.cast(Block).?.src_decl.namespace.file_scope.tree,
            .gen_zir => return scope.cast(GenZir).?.tree(),
            .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.namespace.file_scope.tree,
            .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.namespace.file_scope.tree,
            .namespace => return &scope.cast(Namespace).?.file_scope.tree,
            .decl_ref => return &scope.cast(DeclRef).?.decl.namespace.file_scope.tree,
        }
    }


        error.FileNotFound => {
            return mod.fail(&block.base, src, "unable to find '{s}'", .{operand});
        },



        log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str});
        mod.comp.stage1AddLinkLib(lib_name_str) catch |err| {
            return mod.failTok(
                &fn_type_scope.base,
                lib_name_token,
                "unable to add link lib '{s}': {s}",
                .{ lib_name_str, @errorName(err) },
            );
        };
        const target = mod.comp.getTarget();
        if (target_util.is_libc_lib_name(target, lib_name_str)) {
            if (!mod.comp.bin_file.options.link_libc) {
                return mod.failTok(
                    &fn_type_scope.base,
                    lib_name_token,
                    "dependency on libc must be explicitly specified in the build command",
                    .{},
                );
            }
            break :blk;
        }
        if (target_util.is_libcpp_lib_name(target, lib_name_str)) {
            if (!mod.comp.bin_file.options.link_libcpp) {
                return mod.failTok(
                    &fn_type_scope.base,
                    lib_name_token,
                    "dependency on libc++ must be explicitly specified in the build command",
                    .{},
                );
            }
            break :blk;
        }
        if (!target.isWasm() and !mod.comp.bin_file.options.pic) {
            return mod.failTok(
                &fn_type_scope.base,
                lib_name_token,
                "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
                .{ lib_name_str, lib_name_str },
            );
        }

            if (counts.values == 0 and counts.decls == 0 and arg_inst == .none) {
                // No explicitly provided tag values and no top level declarations! In this case,
                // we can construct the enum type in AstGen and it will be correctly shared by all
                // generic function instantiations and comptime function calls.
                var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
                errdefer new_decl_arena.deinit();
                const arena = &new_decl_arena.allocator;

                var fields_map: std.StringArrayHashMapUnmanaged(void) = .{};
                try fields_map.ensureCapacity(arena, counts.total_fields);
                for (container_decl.ast.members) |member_node| {
                    if (member_node == counts.nonexhaustive_node)
                        continue;
                    const member = switch (node_tags[member_node]) {
                        .container_field_init => tree.containerFieldInit(member_node),
                        .container_field_align => tree.containerFieldAlign(member_node),
                        .container_field => tree.containerField(member_node),
                        else => unreachable, // We checked earlier.
                    };
                    const name_token = member.ast.name_token;
                    const tag_name = try mod.identifierTokenStringTreeArena(
                        scope,
                        name_token,
                        tree,
                        arena,
                    );
                    const gop = fields_map.getOrPutAssumeCapacity(tag_name);
                    if (gop.found_existing) {
                        const msg = msg: {
                            const msg = try mod.errMsg(
                                scope,
                                gz.tokSrcLoc(name_token),
                                "duplicate enum tag",
                                .{},
                            );
                            errdefer msg.destroy(gpa);
                            // Iterate to find the other tag. We don't eagerly store it in a hash
                            // map because in the hot path there will be no compile error and we
                            // don't need to waste time with a hash map.
                            const bad_node = for (container_decl.ast.members) |other_member_node| {
                                const other_member = switch (node_tags[other_member_node]) {
                                    .container_field_init => tree.containerFieldInit(other_member_node),
                                    .container_field_align => tree.containerFieldAlign(other_member_node),
                                    .container_field => tree.containerField(other_member_node),
                                    else => unreachable, // We checked earlier.
                                };
                                const other_tag_name = try mod.identifierTokenStringTreeArena(
                                    scope,
                                    other_member.ast.name_token,
                                    tree,
                                    arena,
                                );
                                if (mem.eql(u8, tag_name, other_tag_name))
                                    break other_member_node;
                            } else unreachable;
                            const other_src = gz.nodeSrcLoc(bad_node);
                            try mod.errNote(scope, other_src, msg, "other tag here", .{});
                            break :msg msg;
                        };
                        return mod.failWithOwnedErrorMsg(scope, msg);
                    }
                }
                const enum_simple = try arena.create(Module.EnumSimple);
                enum_simple.* = .{
                    .owner_decl = astgen.decl,
                    .node_offset = astgen.decl.nodeIndexToRelative(node),
                    .fields = fields_map,
                };
                const enum_ty = try Type.Tag.enum_simple.create(arena, enum_simple);
                const enum_val = try Value.Tag.ty.create(arena, enum_ty);
                const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
                    .ty = Type.initTag(.type),
                    .val = enum_val,
                });
                const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);
                const result = try gz.addDecl(.decl_val, decl_index, node);
                return rvalue(gz, scope, rl, result, node);
            }


    if (mod.lookupIdentifier(scope, ident_name)) |decl| {
        const msg = msg: {
            const msg = try mod.errMsg(
                scope,
                name_src,
                "redeclaration of '{s}'",
                .{ident_name},
            );
            errdefer msg.destroy(gpa);
            try mod.errNoteNonLazy(decl.srcLoc(), msg, "previously declared here", .{});
            break :msg msg;
        };
        return mod.failWithOwnedErrorMsg(scope, msg);
    }


    var type_changed = true;
    if (decl.typedValueManaged()) |tvm| {
        type_changed = !tvm.typed_value.ty.eql(var_info.ty);

        tvm.deinit(mod.gpa);
    }

    const new_variable = try decl_arena.allocator.create(Var);
    new_variable.* = .{
        .owner_decl = decl,
        .init = var_info.val orelse undefined,
        .is_extern = is_extern,
        .is_mutable = is_mutable,
        .is_threadlocal = is_threadlocal,
    };
    const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable);

    decl_arena_state.* = decl_arena.state;
    decl.typed_value = .{
        .most_recent = .{
            .typed_value = .{
                .ty = var_info.ty,
                .val = var_val,
            },
            .arena = decl_arena_state,
        },
    };
    decl.analysis = .complete;
    decl.generation = mod.generation;



    if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
        return mod.failTok(
            &decl_scope.base,
            var_decl.ast.mut_token,
            "variable of type '{}' must be const",
            .{var_info.ty},
        );
    }

    if (var_decl.extern_export_token) |maybe_export_token| {
        if (token_tags[maybe_export_token] == .keyword_export) {
            const export_src = decl.tokSrcLoc(maybe_export_token);
            const name_token = var_decl.ast.mut_token + 1;
            const name = tree.tokenSlice(name_token); // TODO identifierTokenString
            // The scope needs to have the decl in it.
            try mod.analyzeExport(&decl_scope.base, export_src, name, decl);
        }
    }

    fn writeFuncExtra(
        self: *Writer,
        stream: anytype,
        inst: Inst.Index,
        var_args: bool,
    ) !void {
        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
        const src = inst_data.src();
        const extra = self.code.extraData(Inst.FuncExtra, inst_data.payload_index);
        const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
        const cc = extra.data.cc;
        const body = self.code.extra[extra.end + param_types.len ..][0..extra.data.body_len];
        return self.writeFuncCommon(
            stream,
            param_types,
            extra.data.return_type,
            var_args,
            cc,
            body,
            src,
        );
    }


    const error_set = try arena.create(Module.ErrorSet);
    error_set.* = .{
        .owner_decl = astgen.decl,
        .node_offset = astgen.decl.nodeIndexToRelative(node),
        .names_ptr = fields.ptr,
        .names_len = @intCast(u32, fields.len),
    };
    const error_set_ty = try Type.Tag.error_set.create(arena, error_set);
    const error_set_val = try Value.Tag.ty.create(arena, error_set_ty);
    const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
        .ty = Type.initTag(.type),
        .val = error_set_val,
    });
    const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);
    const result = try gz.addDecl(.decl_val, decl_index, node);
    return rvalue(gz, scope, rl, result, node);



        // when implementing this be sure to add test coverage for the asm return type
        // not resolving into a type (the node_offset_asm_ret_ty  field of LazySrcLoc)