Skip to content

Pragma, Preprocessor: add "beforeInclude" hook, extend "once"#1010

Merged
Vexu merged 3 commits into
Vexu:masterfrom
vancluever:pragma-once-whole-include-skip
May 26, 2026
Merged

Pragma, Preprocessor: add "beforeInclude" hook, extend "once"#1010
Vexu merged 3 commits into
Vexu:masterfrom
vancluever:pragma-once-whole-include-skip

Conversation

@vancluever

@vancluever vancluever commented May 18, 2026

Copy link
Copy Markdown
Contributor

This adds the beforeInclude pragma hook, which fires before a file is included. Its current and only implementation is in the "once" pragma, which has been extended to use it to ensure that #pragma once causes future inclusions of a file to be skipped outright, versus processed until the pragma is hit again.

This prevents scenarios where the directive has not been placed at the top of the file. This is a practice that is carried out in at least the GTK headers, and the current control flow causes inner-inclusion guards
to be hit when they should not be.

Note that since this is a deep-preprocessor event, it has not been added to the compilation-scoped PragmaEvent set, and just implemented as a hook local in the include function. If more event hooks get added in the future, it might be a good idea to review the event system to allow for scope-agnostic event types and payloads.

This logs when macros get #undef'ed, useful when debugging guards, etc.
@vancluever

vancluever commented May 18, 2026

Copy link
Copy Markdown
Contributor Author

This was discovered during our migration of Ghostty to Zig 0.16.0 (current PR is ghostty-org/ghostty#12726). It appears that the #pragma once in gdk/version/gdkversionmacros.h has been placed after the guards in the file, which causes some deeper includes to fail, particularly the include path that starts at gtk/gtkaboutdialog.h in gtk/gtk.h, by which point the GDK includes have been walked enough to undef __GDK_H_INSIDE__ (in addition to __GTK_CSS_H_INSIDE__ through the gtk/css/gtkcss.h path).

Managed to isolate the case to what is seen in the test here. A reproduction in clang seems to be fine, so I'm guessing this is generally the expected behavior.

Let me know if this needs anything!

@ehaas

ehaas commented May 19, 2026

Copy link
Copy Markdown
Collaborator

The test looks good. The fix works as a targeted solution to the problem; it would be nice if the pragma system itself could express this sort of thing (as it stands, a consumer of aro can provide their own pragmas, but they would not be able to provide a custom pragma that behaved the same as #pragma once since its behavior is now hardcoded into the preprocessor itself.)

I'm imagining something like another PragmaEvent like "before_include" or "should_include" or something like that; the default implementation would just return true but then custom pragmas could override it to allow deciding whether the file should be included.

I don't think implementing that is a blocker, especially since this issue is preventing use on a popular real-world library, just a thought for how we could improve the pragma system.

@vancluever

vancluever commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

@ehaas I think this would be a trivial thing (or at least not a complicated thing) to implement to start out. I'll update the PR in a bit!

@vancluever vancluever force-pushed the pragma-once-whole-include-skip branch from 6792610 to 76e397e Compare May 19, 2026 18:04
@vancluever vancluever changed the title Preprocessor: whole include skip w/"pragma once" Pragma, Preprocessor: add "beforeInclude" hook, extend "once" May 19, 2026
@vancluever vancluever force-pushed the pragma-once-whole-include-skip branch from 76e397e to 53c59c7 Compare May 19, 2026 18:07
@vancluever

Copy link
Copy Markdown
Contributor Author

@ehaas PR updated - commentary in the commit and PR description!

@vancluever vancluever force-pushed the pragma-once-whole-include-skip branch from 53c59c7 to 0a310f1 Compare May 19, 2026 18:14
This adds the "beforeInclude" pragma hook, which fires before a file is
included. Its current and only implementation is in the "once" pragma,
which has been extended to use it to ensure that "#pragma once" causes
future inclusions of a file to be skipped outright, versus processed
until the pragma is hit again.

This prevents scenarios where the directive has not been placed at the
top of the file. This is a practice that is carried out in at least the
GTK headers, and the current control flow causes inner-inclusion guards
to be hit when they should not be.

Note that since this is a deep-preprocessor event, it has not been added
to the compilation-scoped PragmaEvent set, and just implemented as a
hook local in the include function. If more event hooks get added in the
future, it might be a good idea to review the event system to allow for
scope-agnostic event types and payloads.
@vancluever vancluever force-pushed the pragma-once-whole-include-skip branch from 0a310f1 to e24416e Compare May 19, 2026 18:15
Comment thread src/aro/Preprocessor.zig Outdated
Co-authored-by: Veikka Tuominen <git@vexu.eu>
@Vexu Vexu merged commit 121472d into Vexu:master May 26, 2026
3 checks passed
@vancluever

Copy link
Copy Markdown
Contributor Author

Thanks to the both of you!

@vancluever vancluever deleted the pragma-once-whole-include-skip branch June 3, 2026 16:59
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.

3 participants