Skip to content

refactor: distinguish unloaded extensions from dispatch errors - #190

Merged
MilesCranmer merged 4 commits into
SymbolicML:masterfrom
MilesCranmerBot:bot/189-extension-loaded-fixes
Aug 8, 2026
Merged

refactor: distinguish unloaded extensions from dispatch errors#190
MilesCranmer merged 4 commits into
SymbolicML:masterfrom
MilesCranmerBot:bot/189-extension-loaded-fixes

Conversation

@MilesCranmerBot

Copy link
Copy Markdown
Contributor

Based on #189 by @mvanhorn, with the review fixes from #189 (comment):

  • The node_to_symbolic docstring now lives on the public entry point in ExtensionInterface.jl, so ?node_to_symbolic keeps working at the REPL
  • _is_extension_loaded renamed to is_extension_loaded since it is imported by other code
  • is_extension_loaded(::Val{...}) markers moved to the bottom of each extension module, keeping consts ahead of method definitions
  • Fixed the added tests: the varargs dispatch wrappers are inherently unstable, so successful extension-dispatched calls are wrapped in allow_unstable (matching this file's existing pattern), and the bumper check uses the node-call return value directly (call syntax returns only the result vector, not a tuple)

Original summary (from #189)

Add a shared, Val-dispatched extension-presence predicate in src/ExtensionInterface.jl, and make each affected public/core entry point check its extension state before delegating to a separate internal generic. Update the SymbolicUtils, Zygote, and Bumper extension modules to mark themselves loaded and attach their existing implementations to those internal generics, preserving current successful behavior and unloaded-package guidance. When an extension is loaded but no implementation matches, allow normal Julia dispatch to surface a MethodError rather than emitting the false "please load" message.

Verification

  • test/test_initial_errors.jl passes locally (covers unloaded guidance, loaded happy paths, and post-load MethodError dispatch for SymbolicUtils, Zygote, and Bumper)

Fixes #68

@MilesCranmer MilesCranmer changed the title fix: Distinguish unloaded extensions from dispatch errors refactor: distinguish unloaded extensions from dispatch errors Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1)

Time benchmarks
master 973feb0... master / 973feb0...
eval/ComplexF32/evaluation 7.43 ± 0.53 ms 7.45 ± 0.52 ms 0.997 ± 0.1
eval/ComplexF64/evaluation 10.8 ± 0.98 ms 10.8 ± 0.86 ms 1 ± 0.12
eval/Float32/derivative 11.5 ± 1.5 ms 11.5 ± 1.7 ms 0.999 ± 0.2
eval/Float32/derivative_turbo 11.5 ± 1.6 ms 11.5 ± 1.9 ms 1 ± 0.22
eval/Float32/evaluation 2.52 ± 0.25 ms 2.51 ± 0.25 ms 1.01 ± 0.14
eval/Float32/evaluation_bumper 0.598 ± 0.026 ms 0.618 ± 0.018 ms 0.967 ± 0.051
eval/Float32/evaluation_turbo 0.548 ± 0.037 ms 0.542 ± 0.038 ms 1.01 ± 0.097
eval/Float32/evaluation_turbo_bumper 0.594 ± 0.019 ms 0.615 ± 0.017 ms 0.965 ± 0.04
eval/Float64/derivative 15.8 ± 3.4 ms 15.8 ± 3.9 ms 1.01 ± 0.33
eval/Float64/derivative_turbo 15.7 ± 3.3 ms 15.2 ± 3.5 ms 1.03 ± 0.32
eval/Float64/evaluation 2.95 ± 0.3 ms 2.93 ± 0.3 ms 1 ± 0.15
eval/Float64/evaluation_bumper 1.22 ± 0.048 ms 1.29 ± 0.043 ms 0.946 ± 0.049
eval/Float64/evaluation_turbo 1.05 ± 0.071 ms 1.04 ± 0.068 ms 1.01 ± 0.095
eval/Float64/evaluation_turbo_bumper 1.21 ± 0.046 ms 1.28 ± 0.049 ms 0.946 ± 0.051
utils/combine_operators/break_sharing 0.0418 ± 0.0013 ms 0.042 ± 0.0015 ms 0.995 ± 0.048
utils/convert/break_sharing 27.3 ± 3.3 μs 26.8 ± 3 μs 1.02 ± 0.17
utils/convert/preserve_sharing 0.101 ± 0.01 ms 0.0988 ± 0.0091 ms 1.02 ± 0.14
utils/copy/break_sharing 27.6 ± 4.1 μs 27.2 ± 2.9 μs 1.01 ± 0.18
utils/copy/preserve_sharing 0.101 ± 0.0098 ms 0.0974 ± 0.0078 ms 1.04 ± 0.13
utils/count_constant_nodes/break_sharing 12.7 ± 1 μs 12.7 ± 0.66 μs 1 ± 0.097
utils/count_constant_nodes/preserve_sharing 0.0859 ± 0.0081 ms 0.0837 ± 0.005 ms 1.03 ± 0.11
utils/count_depth/break_sharing 14 ± 1.3 μs 12.8 ± 0.74 μs 1.09 ± 0.12
utils/count_nodes/break_sharing 12.3 ± 0.98 μs 13.1 ± 0.63 μs 0.937 ± 0.087
utils/count_nodes/preserve_sharing 0.0876 ± 0.008 ms 0.0858 ± 0.0062 ms 1.02 ± 0.12
utils/get_set_constants!/break_sharing 0.0326 ± 0.0038 ms 0.0323 ± 0.003 ms 1.01 ± 0.15
utils/get_set_constants!/preserve_sharing 0.175 ± 0.013 ms 0.173 ± 0.011 ms 1.01 ± 0.097
utils/get_set_constants_parametric 0.0486 ± 0.0074 ms 0.0456 ± 0.0042 ms 1.07 ± 0.19
utils/has_constants/break_sharing 7.64 ± 1.4 μs 7.12 ± 0.81 μs 1.07 ± 0.23
utils/has_operators/break_sharing 2.62 ± 0.24 μs 2.44 ± 0.14 μs 1.07 ± 0.12
utils/hash/break_sharing 23.4 ± 1.4 μs 24.3 ± 1.4 μs 0.962 ± 0.082
utils/hash/preserve_sharing 0.101 ± 0.0094 ms 0.0983 ± 0.0054 ms 1.03 ± 0.11
utils/index_constant_nodes/break_sharing 28.5 ± 2.5 μs 28.5 ± 2.1 μs 1 ± 0.12
utils/index_constant_nodes/preserve_sharing 0.102 ± 0.009 ms 0.099 ± 0.0067 ms 1.03 ± 0.11
utils/is_constant/break_sharing 8.2 ± 1.3 μs 7.73 ± 0.79 μs 1.06 ± 0.2
utils/simplify_tree/break_sharing 31.6 ± 1.9 μs 0.0317 ± 0.0012 ms 0.996 ± 0.072
utils/simplify_tree/preserve_sharing 0.112 ± 0.0084 ms 0.111 ± 0.0058 ms 1 ± 0.092
utils/string_tree/break_sharing 0.44 ± 0.02 ms 0.442 ± 0.018 ms 0.996 ± 0.06
utils/string_tree/preserve_sharing 0.539 ± 0.023 ms 0.535 ± 0.018 ms 1.01 ± 0.054
time_to_load 0.169 ± 0.003 s 0.176 ± 0.003 s 0.956 ± 0.024
Memory benchmarks
master 973feb0... master / 973feb0...
eval/ComplexF32/evaluation 1.09 k allocs: 2.54 MB 1.07 k allocs: 2.5 MB 1.02
eval/ComplexF64/evaluation 1.1 k allocs: 5.11 MB 1.09 k allocs: 5.06 MB 1.01
eval/Float32/derivative 4.68 k allocs: 17.6 MB 4.65 k allocs: 17.5 MB 1.01
eval/Float32/derivative_turbo 4.62 k allocs: 17.4 MB 4.71 k allocs: 17.7 MB 0.98
eval/Float32/evaluation 1.07 k allocs: 1.27 MB 1.09 k allocs: 1.29 MB 0.982
eval/Float32/evaluation_bumper 0.403 k allocs: 0.395 MB 0.403 k allocs: 0.395 MB 1
eval/Float32/evaluation_turbo 1.07 k allocs: 1.26 MB 1.07 k allocs: 1.26 MB 1
eval/Float32/evaluation_turbo_bumper 0.403 k allocs: 0.395 MB 0.403 k allocs: 0.395 MB 1
eval/Float64/derivative 4.81 k allocs: 0.0352 GB 4.8 k allocs: 0.0351 GB 1
eval/Float64/derivative_turbo 4.79 k allocs: 0.035 GB 4.73 k allocs: 0.0346 GB 1.01
eval/Float64/evaluation 1.1 k allocs: 2.56 MB 1.1 k allocs: 2.56 MB 1
eval/Float64/evaluation_bumper 0.403 k allocs: 0.773 MB 0.403 k allocs: 0.773 MB 1
eval/Float64/evaluation_turbo 1.09 k allocs: 2.54 MB 1.1 k allocs: 2.55 MB 0.997
eval/Float64/evaluation_turbo_bumper 0.403 k allocs: 0.773 MB 0.403 k allocs: 0.773 MB 1
utils/combine_operators/break_sharing 4 allocs: 0.953 kB 4 allocs: 0.953 kB 1
utils/convert/break_sharing 2 k allocs: 0.123 MB 2 k allocs: 0.123 MB 1
utils/convert/preserve_sharing 2.4 k allocs: 0.192 MB 2.4 k allocs: 0.192 MB 1
utils/copy/break_sharing 2 k allocs: 0.123 MB 2 k allocs: 0.123 MB 1
utils/copy/preserve_sharing 2.4 k allocs: 0.192 MB 2.4 k allocs: 0.192 MB 1
utils/count_constant_nodes/break_sharing 4 allocs: 0.953 kB 4 allocs: 0.953 kB 1
utils/count_constant_nodes/preserve_sharing 0.404 k allocs: 0.0696 MB 0.404 k allocs: 0.0696 MB 1
utils/count_depth/break_sharing 4 allocs: 0.953 kB 4 allocs: 0.953 kB 1
utils/count_nodes/break_sharing 4 allocs: 0.953 kB 4 allocs: 0.953 kB 1
utils/count_nodes/preserve_sharing 0.404 k allocs: 0.0696 MB 0.404 k allocs: 0.0696 MB 1
utils/get_set_constants!/break_sharing 0.898 k allocs: 25.2 kB 0.898 k allocs: 25.2 kB 1
utils/get_set_constants!/preserve_sharing 1.7 k allocs: 0.138 MB 1.7 k allocs: 0.138 MB 1
utils/get_set_constants_parametric 1.42 k allocs: 0.0663 MB 1.42 k allocs: 0.0663 MB 1
utils/has_constants/break_sharing 4 allocs: 0.203 kB 4 allocs: 0.203 kB 1
utils/has_operators/break_sharing 4 allocs: 0.203 kB 4 allocs: 0.203 kB 1
utils/hash/break_sharing 0.104 k allocs: 2.52 kB 0.104 k allocs: 2.52 kB 1
utils/hash/preserve_sharing 0.504 k allocs: 0.0711 MB 0.504 k allocs: 0.0711 MB 1
utils/index_constant_nodes/break_sharing 2.1 k allocs: 0.094 MB 2.1 k allocs: 0.094 MB 1
utils/index_constant_nodes/preserve_sharing 2.5 k allocs: 0.163 MB 2.5 k allocs: 0.163 MB 1
utils/is_constant/break_sharing 4 allocs: 0.203 kB 4 allocs: 0.203 kB 1
utils/simplify_tree/break_sharing 0.104 k allocs: 2.52 kB 0.104 k allocs: 2.52 kB 1
utils/simplify_tree/preserve_sharing 0.504 k allocs: 0.0711 MB 0.504 k allocs: 0.0711 MB 1
utils/string_tree/break_sharing 11.9 k allocs: 0.999 MB 11.9 k allocs: 0.999 MB 1
utils/string_tree/preserve_sharing 12.3 k allocs: 1.07 MB 12.3 k allocs: 1.07 MB 1
time_to_load 0.145 k allocs: 11 kB 0.145 k allocs: 11 kB 1

@MilesCranmer

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: d687e6efb4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@MilesCranmer

Copy link
Copy Markdown
Member

@MilesCranmerBot See the CI errors. I think this function node_to_symbolic can be marked @unstable because the extensions are not checked for stability. So for this to be bit identical, we should explicitly mark it. Please fix.

@MilesCranmer

Copy link
Copy Markdown
Member

@MilesCranmerBot CI is still failing... Please fix. Also the branch is out-of-date with master.

mvanhorn and others added 4 commits August 8, 2026 19:55
- Move node_to_symbolic docstring back to the public entry point in
  ExtensionInterface.jl so `?node_to_symbolic` keeps working
- Rename _is_extension_loaded to is_extension_loaded (imported by other code)
- Move is_extension_loaded(::Val{...}) markers to the bottom of each
  extension module so consts stay ahead of method definitions
- Fix added tests: wrap extension-dispatched calls in allow_unstable
  (DispatchDoctor errors on the varargs wrappers) and use the node-call
  return value directly for the bumper check

Co-authored-by: Miles Cranmer <miles.cranmer@gmail.com>
Co-authored-by: Miles Cranmer <miles.cranmer@gmail.com>
@MilesCranmerBot
MilesCranmerBot force-pushed the bot/189-extension-loaded-fixes branch from be0b0b0 to 973feb0 Compare August 8, 2026 20:07
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.73684% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 72.88%. Comparing base (a5999b8) to head (973feb0).

Files with missing lines Patch % Lines
src/ExtensionInterface.jl 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #190      +/-   ##
==========================================
+ Coverage   72.56%   72.88%   +0.32%     
==========================================
  Files          30       30              
  Lines        2810     2818       +8     
==========================================
+ Hits         2039     2054      +15     
+ Misses        771      764       -7     

☔ 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.

@MilesCranmer
MilesCranmer merged commit 30276c8 into SymbolicML:master Aug 8, 2026
13 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.

Consider a different way to error on extension not loaded

3 participants