Skip to content

Fix compiler crash when passing a comptime extern function arg to a function#24249

Merged
andrewrk merged 3 commits into
ziglang:masterfrom
antlilja:dwarf-extern-arg
Aug 18, 2025
Merged

Fix compiler crash when passing a comptime extern function arg to a function#24249
andrewrk merged 3 commits into
ziglang:masterfrom
antlilja:dwarf-extern-arg

Conversation

@antlilja

@antlilja antlilja commented Jun 22, 2025

Copy link
Copy Markdown
Contributor

This PR fixes a compiler crash that occurs when passing a comptime extern function argument to a function.

The compiler crashes in DWARF debug info code. The function getValueEntry does not handle extern functions correctly, instead handling all values with a function type as InternPool.Key.func values.

Repro

Code

extern fn func() void;

fn foo(f: fn () callconv(.C) void) void {
    _ = f;
}

pub fn main() !void {
    foo(func);
}

Stack trace

error: access of union field 'func' while field 'extern' is active

src/InternPool.zig:12142:28: in toFunc (main.zig)
    return ip.indexToKey(i).func;
                           ^
src/Zcu.zig:4013:34: in funcInfo (main.zig)
    return zcu.intern_pool.toFunc(func_index);
                                 ^
src/link/Dwarf.zig:1910:110: in getValueEntry (main.zig)
        if (ip.isFunctionType(ty.toIntern()) and !value.isUndef(zcu)) return wip_nav.getNavEntry(zcu.funcInfo(value.toIntern()).owner_nav);
                                                                                                             ^
src/link/Dwarf.zig:1921:60: in refValue (main.zig)
        const unit, const entry = try wip_nav.getValueEntry(value);
                                                           ^
src/link/Dwarf.zig:1526:53: in genLocalConstDebugInfo (main.zig)
        if (has_comptime_state) try wip_nav.refValue(val);
                                                    ^
src/arch/x86_64/Emit.zig:621:61: in emitMir (main.zig)
                            try dwarf.genLocalConstDebugInfo(
                                                            ^

Closes #24259

@jacobly0

Copy link
Copy Markdown
Member

I don't think this is correct, see:

fn foo(f: fn () callconv(.C) void) void {
    _ = f;
}

pub fn main() !void {
    foo(@extern(*const fn () callconv(.c) void, .{ .name = "func" }).*);
}

@antlilja

antlilja commented Jul 9, 2025

Copy link
Copy Markdown
Contributor Author

@jacobly0 Should now close #24259, the crash you mentioned was is triggered because the extern created with a builtin extern call shares the decl with its container causing the dwarf logic to skip adding debug info for said extern (see https://github.com/ziglang/zig/blob/7c709f920bbe8f01a25392182e4a3fd02bb95219/src/Sema.zig#L26110). It seems like this is a temporary solution, the special logic for handling externs in initWipNavInner on line 2510 can be removed if the extern gets its own decl.

@antlilja antlilja force-pushed the dwarf-extern-arg branch from 1633e8c to 3918244 Compare July 9, 2025 21:13
@jacobly0

jacobly0 commented Jul 9, 2025

Copy link
Copy Markdown
Member

DW_TAG_variable is definitely not correct for extern functions, and DW_AT_type on a function is the return type not the function type.

@jacobly0

jacobly0 commented Jul 9, 2025

Copy link
Copy Markdown
Member

Also if you look at the debug info for an_extern_function here you'll see it claims to be a threadlocal function (???). You can also see the problem by looking at the other decl attributes.

export fn entry() void {
    _ = @TypeOf(foo).ef;
}
threadlocal var foo: T: {
    const e = @extern(*const fn () callconv(.c) void, .{ .name = "an_extern_function" }).*;
    break :T struct {
        x: u32,
        const ef = e;
    };
} = .{ .x = 0 };

@antlilja

Copy link
Copy Markdown
Contributor Author

Thank you for your help and patience @jacobly0. After thinking it over I realised I was a bit confused and came at the problem from the wrong direction. I think the correct thing to do is to just not emit debug info for any extern functions or variables. This seems to be more inline with what all previous code is trying to do and how this is handled in C. Correct me if I'm wrong but we don't actually need this debug info as we get it from whatever library exported the function or variable.

master currently emits DW_TAG_variable for extern functions as well.

@jacobly0

jacobly0 commented Jul 10, 2025

Copy link
Copy Markdown
Member

Master does not implement extern functions at all, hence all of these crashes, it's just falling into the extern variable handling. As Zig has namespaced extern declarations (not @extern), those need debug info so that expressions like SomeContainer.my_extern_function can be evaluated in a debugger. Additionally, there are many more uses of comptime values than just comptime function parameters, and many more comptime values that contain externs than just function values (e.g. comptime pointer values to extern functions), so the debug info needs to be able to communicate these values in general and just disabling one use of one kind of value will not work. The correct thing to do is to fix the frontend so that the required information is available, but for now it can just emit a subset of the debug info that is currently available and correct. Oh, also it is not valid to elide parameters in dwarf because dwarf is silly and identifies them based on their position.

@antlilja antlilja force-pushed the dwarf-extern-arg branch 3 times, most recently from 40999cc to 8ce37ee Compare July 12, 2025 19:22
@jacobly0 jacobly0 force-pushed the dwarf-extern-arg branch 2 times, most recently from 702c3e7 to 351b4de Compare August 13, 2025 00:35
@andrewrk andrewrk merged commit 001ec07 into ziglang:master Aug 18, 2025
23 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Panic while building bun without LLVM: "access of union field 'func' while field 'extern' is active"

4 participants