Defer to native tracking on 1.11+, make @public expansion pure - #15
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #15 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 1 1
Lines 274 277 +3
=========================================
+ Hits 274 277 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- Skip the per-module fallback storage entirely on Julia 1.11+, where the native publicp binding flag is the only tracking; the hidden const is now injected on pre-1.11 only. Drop the invokelatest workaround, no longer needed with storage confined to pre-world-partitioned Julia. - Make @public expansion side-effect-free: emit the conflict check and tracking as top-level calls instead of mutating during expansion, so @macroexpand leaves no trace and the export-conflict error fires at statement execution like the native keyword (now ErrorException, not LoadError). - Emit the conflict check on every version: the native error for public-after-export is a 1.12 addition; 1.11's native keyword silently tolerates it. @public now gives uniform 1.12+ semantics on all versions. - Add has_native_public(), a public reflection helper that is the single home of the v"1.11.0-DEV.469" version gate. Tested on 1.6, 1.10, 1.11, and 1.12. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@static isn't needed here — both branches lower on every supported version (Expr(:public) is constructed, never parsed, and Base.ispublic is only an error if executed). It also broke coverage: pre-1.12 lowering attributes an instrumentation point to the @static-if line that can never fire (the condition is consumed at expansion time), so the line reports 0 hits on 1.11 and shows as missed after the Codecov merge. With a plain if, the condition line executes at runtime and each CI matrix version covers its branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AntonOresten
force-pushed
the
native-public-tracking
branch
from
August 10, 2026 12:15
3883afa to
a7dc16b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Symbol("#Republic_public_names")const was being written on every version but only ever read pre-1.11 (ispublic/public_namesdefer toBaseon 1.11+). It is now injected on pre-1.11 only — on modern Julia the nativepublicpbinding flag is the sole tracking and Republic adds no binding to consumer modules. TheBase.invokelatestworkaround in_get_storageis dropped: storage access is now confined to pre-world-partitioned-bindings Julia, where a plaingetfieldon a just-eval'd const is safe.@publicexpansion is pure. The macro previously ranCore.evaland mutated storage during expansion, so@macroexpand @public foo(or any tool that expands without evaluating) mutated the module, and the export-conflict check fired at expansion time. It now emits a_check_not_exportedcall followed by either the nativeExpr(:public, ...)(1.11+) or a_track_publiccall (pre-1.11). The conflict error consequently fires at statement execution and is a plainErrorExceptionrather than aLoadError.cannot declare X public; it is already declared exportederror is a 1.12 addition — 1.11's native keyword silently tolerates public-after-export (the old test only passed there because the expansion-time check threw first).@publicnow emits its own check on every version, so 1.6–1.11 match 1.12+ behavior.has_native_public()— the single home of thev"1.11.0-DEV.469"gate, replacing the literal scattered across src and tests. Exported via@publicand documented in the README.Test plan
@macroexpand @public fooleaves no trace (no storage, nothing marked public).ErrorException(unwrappingLoadErrorwhere applicable).🤖 Generated with Claude Code