Skip to content

Defer to native tracking on 1.11+, make @public expansion pure - #15

Merged
AntonOresten merged 3 commits into
mainfrom
native-public-tracking
Aug 10, 2026
Merged

Defer to native tracking on 1.11+, make @public expansion pure#15
AntonOresten merged 3 commits into
mainfrom
native-public-tracking

Conversation

@AntonOresten

Copy link
Copy Markdown
Member

Summary

  • No fallback storage on 1.11+. The hidden per-module Symbol("#Republic_public_names") const was being written on every version but only ever read pre-1.11 (ispublic/public_names defer to Base on 1.11+). It is now injected on pre-1.11 only — on modern Julia the native publicp binding flag is the sole tracking and Republic adds no binding to consumer modules. The Base.invokelatest workaround in _get_storage is dropped: storage access is now confined to pre-world-partitioned-bindings Julia, where a plain getfield on a just-eval'd const is safe.
  • @public expansion is pure. The macro previously ran Core.eval and 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_exported call followed by either the native Expr(:public, ...) (1.11+) or a _track_public call (pre-1.11). The conflict error consequently fires at statement execution and is a plain ErrorException rather than a LoadError.
  • Uniform 1.12 conflict semantics. Running the suite on 1.11 revealed that the native cannot declare X public; it is already declared exported error 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). @public now emits its own check on every version, so 1.6–1.11 match 1.12+ behavior.
  • New public reflection helper has_native_public() — the single home of the v"1.11.0-DEV.469" gate, replacing the literal scattered across src and tests. Exported via @public and documented in the README.

Test plan

  • Storage testsets now assert the hidden binding is absent on 1.11+ and present pre-1.11.
  • New testset: @macroexpand @public foo leaves no trace (no storage, nothing marked public).
  • Conflict-error testset updated to accept the execution-time ErrorException (unwrapping LoadError where applicable).
  • Full suite run locally on 1.6.7, 1.10.11, 1.11.9, and 1.12.6 — all pass.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (96c541f) to head (2dec196).
⚠️ Report is 1 commits behind head on main.

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

AntonOresten and others added 2 commits August 10, 2026 14:15
- 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
AntonOresten force-pushed the native-public-tracking branch from 3883afa to a7dc16b Compare August 10, 2026 12:15
@AntonOresten
AntonOresten merged commit cdf916c into main Aug 10, 2026
5 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.

1 participant