#file, #line equivalents in *generated* zig files

i’m generating a .zig source file, which will then be compiled downstream… (imagine a parser-generator as a use-case)…

is there any equivalent to the C/C++ #file and #line directives, in which the zig compiler would report errors relative to the upstream source file (which, in general, might not even be written in zig)???

Zig doesn’t have a preprocessing step, if I understand you correctly you want these #file and #line directives replaced before the code is compiled?

If that is the case you would have to do a find and replace on the source-code-string/file yourself manually.

If it is enough to use comptime zig code you can use @src() and access its fields.

Can’t the generator acquire this error report information and generate it into the created file in some structured way?

3 Likes

No, there is no way to set the filename or the line number.

1 Like

It sounds like a pain but the only way I know of implementing something like this for Zig would be to create a wrapper program that runs zig on your behalf and captures/processes errors for you. You’d keep the metadata about which lines of zig correspond to which files of the actual source file, then do the replacement yourself.

So if we got an error like this:

foo.zig:2:5: error: use of undeclared identifier 'foo'

The program might have metadata that says foo.zig line 2 corresponds to foo.mycoolmetalanguage line 123, and you could do something like this:

foo.mycoolmetalanguage:123: generated zig that failed to compile with:
foo.zig:2:5: error: use of undeclared identifier 'foo'

So…what is the thing you’re working on that spawned this question?

2 Likes

I would consider this an example of “hidden control flow”, which Zig doesn’t do. I understand the desire to have errors point to the relevant places in the master code, rather than the accidental spots in the generated code where it happens to be triggered.

But the thing is, that isn’t what’s happening. The error return trace and stack trace from errors is what the computer was actually executing when the error happened. That’s exactly what Zig should provide.

So @biosbob, let me give you some pointers on how you might solve the problem. Because yeah, there are good reasons to want errors to point somewhere more useful for your users.

I’m going to future-proof (for some values of future) this post, by explaining in terms of how things work on the master branch now, and how they will work in the 0.14 release. It isn’t that different in 0.13, and I’m sure you can fill in the small differences yourself.

Errors are printed by the panic handler, which is now std.debug.FormattedPanic. You’ll see that it receives a std.builtin.StackTrace. This is your override point, you can replace this function with a custom one.

The stacktrace gets printed with std.debug.writeStackTrace, what you’ll need is a custom version of this. Your codegen will have to generate some kind of sourcemap, something you can @embedFile to have available.

You’ll see logic in writeStackTrace which takes the instruction stack and returns SourceLocation objects, that gets acquired in std.debug.printSourceAtAddress, you can get the necessary data with std.debug.getSelfDebugInfo.

Clearly this is not a small undertaking. I think it illustrates, however, why Zig shouldn’t allow hidden control flow here. Once you write code to do this, it’s rock-solid: and it can’t be undermined by other code inducing the debug info to report false information about what happened.

So if this is important to your project, and I assume it is, you’ve got some work cut out for you. But all the pieces you need are there.

1 Like