Skip to content

Add Julia 1.13 support while maintaining Julia 1.12 compatibility#815

Open
PatrickHaecker wants to merge 9 commits into
aviatesk:masterfrom
PatrickHaecker:julia-1.13-compat
Open

Add Julia 1.13 support while maintaining Julia 1.12 compatibility#815
PatrickHaecker wants to merge 9 commits into
aviatesk:masterfrom
PatrickHaecker:julia-1.13-compat

Conversation

@PatrickHaecker

@PatrickHaecker PatrickHaecker commented Apr 2, 2026

Copy link
Copy Markdown

This is the culmination of an intense collaboration with Claude Opus 4.6 to get runtests.jl error free and reduce the number of warnings on 1.13. The LLM needed a lot of guidance here and we both operated at our limits. All changes are tested to really be necessary to avoid errors or warnings in runtests.jl (I hope we made no error here). I think, that all changes make sense, but I do not understand every detail and especially it might be that some things should be solved differently.

I recommend to have this reviewed by someone really knowing this stuff. @aviatesk, this might be something for you. I recommend to go commit-by-commit in the review.

The remaining warnings seem hard to fix, because they either are intended or might need a Julia started with --depwarn error, but the corresponding errors are caught by JET's error handling. So we might need to temporarily deactivate the error handling, to find and fix them, but this can be done in the future.

Mostly fixes #796. However, it would still need a changed registry constraint to really fix it.

Now Claude's summary about the commits:

Background

Julia 1.13 introduces breaking changes to the binding partition system in lowered IR:

  • :const expression heads are replaced by Core.declare_const() calls
  • :globaldecl is replaced by Core.declare_global() calls
  • Base.setglobal! becomes Core.setglobal! in lowered IR
  • Test.push_testset/pop_testset are removed in favor of Test.@with_testset
  • Core.declare_global and Core.declare_const are builtins that the compiler does not yet report as nothrow

Changes

compat: add Julia 1.13 to supported versions
Update Project.toml compat to "1.12, 1.13".

print: guard CodeTracking.whereis for Base/Core submodules
On 1.13, calling CodeTracking.whereis on Base submodule methods triggers Revise lazy evaluation of all Base source files, causing hundreds of world age advances that corrupt IOContext dispatch and crash the printing infrastructure. Add _is_basemodule() to detect and skip these methods.

virtualprocess: handle Julia 1.13 binding partition IR
Add the new Core.declare_global() and Base._eval_using()/Base._eval_import() call forms to select_direct_requirement! so they are properly concretized during toplevel analysis.

typeinfer: handle declare_const in abstract evaluation
Add is_declare_const_call() and abstract_eval_declare_const() to handle Core.declare_const(mod, :name, value) calls by delegating to the existing const_assignment_rt_exct infrastructure. Without this, JET cannot track const binding types abstractly on 1.13.

jetanalyzer: filter binding partition builtins in sound mode
Add is_binding_partition_builtin() to skip spurious UnsoundBuiltinErrorReport for Core.declare_global and Core.declare_const in the sound error checker.

test: update test expectations for Julia 1.13

  • Use Test.@with_testset on 1.13 via a run_with_testset compat helper
  • Match both Core.setglobal! (1.13) and Base.setglobal! (1.12) in statement selection tests
  • Mark tests as broken on 1.13 where binding partition changes cause false positive NonBooleanCondErrorReport from Union{Missing, Bool} inference on comparison operators

Test results

  • Julia 1.13: 1295 passed, 0 failed, 30 broken
  • Julia 1.12: no regressions (verified per-commit)

@PatrickHaecker PatrickHaecker changed the title This PR adds Julia 1.13 support to JET while maintaining Julia 1.12 compatibility. Add Julia 1.13 support while maintaining Julia 1.12 compatibility Apr 2, 2026
PatrickHaecker pushed a commit to PatrickHaecker/JET.jl that referenced this pull request Apr 20, 2026
Add a new analyzer that detects `if @generated` functions where the
inferred effects of the generated branch (for concrete types) differ from
the fallback branch (for abstract types). Such mismatches can lead to
[unsound optimizations](JuliaLang/julia#61601 (comment)) where the compiler removes code from one branch
based on stronger effects inferred from the other.

The analyzer hooks into `finishinfer!` to inspect each inferred method.
For methods with a generator, it extracts the method signature bounds to
build an abstract type tuple that triggers the fallback branch, then
compares effects field-by-field (excluding `nonoverlayed`, which
legitimately differs).

Entry points: `report_generated_effects`, `@report_generated_effects`,
`test_generated_effects`, `@test_generated_effects`.

This change was generated by Claude Opus 4.6. I only quickly went
through the code as a quick plausibility check. However, the test cases
look good and are passed, so it seems to be useful.

This builds on top of aviatesk#815, so aviatesk#815 should be handled first.
@PatrickHaecker

Copy link
Copy Markdown
Author

Any chance that you can soon look at this @aviatesk?
Ignoring the tests, these are around 60 modified lines of code, so it should take a limited amount of time. However, I think it would help Julia 1.13 testing enormously to have a working JET.jl.

@kbarros

kbarros commented Apr 30, 2026

Copy link
Copy Markdown

With 1.13 RC1 now available, this compatibility update becomes especially important.

Our package requires JET for its unit tests, so the lack of JET compatibility effectively blocks us from testing on 1.13.

(As I understand it, there is no clean way to set up the test/Project.toml so that the JET version is controlled by the Julia version?)

@PatrickHaecker

PatrickHaecker commented May 2, 2026

Copy link
Copy Markdown
Author

(As I understand it, there is no clean way to set up the test/Project.toml so that the JET version is controlled by the Julia version?)

You can use different manifests for different Julia versions. Create the Julia 1.13 manifest from a copy of the generic manifest, modify the Julia 1.13 manifest by

(@v1.13) pkg> add https://github.com/PatrickHaecker/JET.jl#julia-1.13-compat

and then resolve or update it, which is the way we currently do it. However, this needs manual setup for each package and you will have a nasty surprise when I'll delete the merged branch in the future, as your tests will no longer work. However, you can at least use this as a trigger to set the used JET back to the general registry with

(@v1.13) pkg> free JET

(if I don't forget to delete the branch in time, otherwise you'd need to set yourself a trigger).

In addition, you now need to maintain two manifests.

So this can only be a workaround, especially for such an important package as JET.jl.

@kbarros

kbarros commented May 12, 2026

Copy link
Copy Markdown

Thanks for the tips.

I tried running the Sunny.jl tests using 1.13-RC1 and this JET branch, but get lots of errors of the form:

  TypeError: in typeassert, expected Compiler.InferenceState, got a value of type Compiler.IRInterpretationState
  Stacktrace:
     [1] typeinf
       @ ~/.julia/packages/JET/ZldSa/src/abstractinterpret/typeinfer.jl:321 [inlined]
     [2] const_prop_call(interp::JET.OptAnalyzer{Returns{Bool}}, mi::Core.MethodInstance, result::Compiler.MethodCallResult, arginfo::Compiler.ArgInfo, sv::Compiler.IRInterpretationState, concrete_eval_result::Nothing)
       @ Compiler ./../usr/share/julia/Compiler/src/abstractinterpretation.jl:1354
     [3] const_prop_call
       @ ~/.julia/packages/JET/ZldSa/src/analyzers/optanalyzer.jl:190 [inlined]
     ...

Does this ring a bell? Full trace: JET-output-Sunny.txt

@PatrickHaecker

Copy link
Copy Markdown
Author

@kbarros:
I hadn't touched the relevant lines in this PR before. However, I think filter_lineages! only makes sense when having an InferenceState. So I modified JET in my PR to not filter otherwise. Can you please give it a try?
I quickly tried to test Sunny and JET seems to be doing something useful, but overall I get so many errors and failures that I don't know whether this is actual progress.

@kbarros

kbarros commented Jun 16, 2026

Copy link
Copy Markdown

I quickly tried to test Sunny and JET seems to be doing something useful, but overall I get so many errors and failures that I don't know whether this is actual progress.

Prior to 1.13, all Sunny tests pass without any errors or warnings, and JET tests are part of our CI. So if you get many errors, that means JET is not working yet, unfortunately. Are you asking me to check if the delta from your two recent commits is going in the right direction?

Thanks for working on this.

@PatrickHaecker

PatrickHaecker commented Jun 17, 2026

Copy link
Copy Markdown
Author

Yes, @kbarros, please rerun the Sunny tests from an environment where you have added https://github.com/PatrickHaecker/JET.jl#julia-1.13-compat.

I think your reasoning is incomplete. If there are errors in the 1.13 JET tests, at least the following things are possible

  • Sunny is not yet fully compatible with Julia 1.13
  • Sunny's dependencies are not yet fully compatible with Julia 1.13
  • This JET branch is not yet fully compatible with Julia 1.13
  • JET's dependencies are not yet fully compatible with Julia 1.13

I think after running the tests you can judge best where the problems are located.

@kbarros

kbarros commented Jun 17, 2026

Copy link
Copy Markdown

Thanks for the clarification.

  • I verified that Sunny is Julia 1.13 compatible. Commenting out this one JET testing file makes all the remaining tests pass.
  • However, something very weird: When the JET tests are enabled, the testing output includes additional failures that should be unrelated to JET. I don't know what to make of this. Is it possible that JET failures are somehow changing the test harness state in a way that breaks things in downstream tests?

Here is a minimal script that fails on Julia 1.13 (with your JET#julia-1.13-compat branch) but passes on Julia 1.12 (JET v0.11.4):

using Sunny
using JET

latvecs = lattice_vectors(1, 1, 2, 90, 90, 90)
crystal = Crystal(latvecs, [[0, 0, 0]])
sys = System(crystal, [1 => Moment(s=1, g=2)], :dipole; dims=(2, 2, 1))

@test_opt energy(sys)

@PatrickHaecker

Copy link
Copy Markdown
Author

Your example fails for me without JET, too:

julia> using Sunny

julia> latvecs = lattice_vectors(1, 1, 2, 90, 90, 90)
3×3 StaticArraysCore.SMatrix{3, 3, Float64, 9} with indices SOneTo(3)×SOneTo(3):
 1.0  0.0  0.0
 0.0  1.0  0.0
 0.0  0.0  2.0

julia> crystal = Crystal(latvecs, [[0, 0, 0]])
Crystal
HM symbol 'P 4/m m m' (123)
Lattice params a=1, b=1, c=2, α=90°, β=90°, γ=90°
Cell volume 2
Wyckoff 1a (point group '4/mmm'):
   1. [0, 0, 0]

julia> sys = System(crystal, [1 => Moment(s=1, g=2)], :dipole; dims=(2, 2, 1))
ERROR: UndefVarError: `Moment` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
 [1] top-level scope
   @ REPL[3]:1
 [2] top-level scope
   @ REPL:1

(@v1.13) pkg> status Sunny
Status `~/.julia/environments/v1.13/Project.toml`
⌃ [2b4a2ac8] Sunny v0.2.0

@kbarros

kbarros commented Jun 18, 2026

Copy link
Copy Markdown

Looks like you’re on Sunny 0.2.0. The latest version is 0.9.1.

Patrick Häcker added 9 commits June 23, 2026 18:45
Update the Julia compat entry in Project.toml from "1.12"
to "1.12, 1.13" to declare support for Julia 1.13.

Written by Claude Opus 4.6
On Julia 1.13, calling CodeTracking.whereis on methods from
Base submodules (e.g. Base.Threads) triggers Revise lazy
evaluation of ALL Base source files. This causes hundreds of
world age advances which corrupts IOContext dispatch, leading
to crashes in the printing infrastructure.

Add _is_basemodule() helper that walks parent modules to
detect Base/Core submodule methods, and skip the
CodeTracking.whereis call for those methods in
fixed_line_number(). Base methods do not need line number
revision since they are not user-editable.

Written by Claude Opus 4.6
Julia 1.13 replaces several expression heads with function
calls in the binding partition system:
- :globaldecl -> Core.declare_global()
- :using/:import -> Base._eval_using()/Base._eval_import()

Add these new call forms to select_direct_requirement! so
they are properly concretized during toplevel analysis.

Also intercept module usage statements (using/import) before
lowering, since on 1.13 they lower to _eval_using/_eval_import
calls that are not recognized by ismoduleusage in the lowered
form. By handling them pre-lowering, we ensure module usage
inside begin blocks is properly processed.

Note: Core.declare_const() is intentionally NOT added to
direct requirements, as that would over-concretize and
execute side effects (e.g. Downloads.download). Instead,
declare_const is pulled in as a dependency when struct or
method definitions need it.

Written by Claude Opus 4.6
Julia 1.13 replaces :const expression heads with
Core.declare_const(mod, :name, value) calls. Without
handling these in the abstract interpreter, JET cannot
track const binding types abstractly, which breaks:
- Type alias resolution (const T = SomeType)
- Conditional const tracking
- Struct definitions depending on const bindings
- Overall type inference accuracy for toplevel code

Add is_declare_const_call() to detect these calls, and
abstract_eval_declare_const() to handle them by extracting
the module, name, and value arguments and delegating to
the existing const_assignment_rt_exct infrastructure.

Written by Claude Opus 4.6
On Julia 1.13, Core.declare_global and Core.declare_const
are builtins that can appear in lowered toplevel code. The
compiler does not yet report them as nothrow, which causes
the sound mode error checker to emit spurious
UnsoundBuiltinErrorReport for these binding partition
operations.

Add is_binding_partition_builtin() predicate (guarded with
@static if for 1.12 compatibility) and use it to skip
reporting for these builtins in _report_builtin_error_sound!.

Written by Claude Opus 4.6
Update test infrastructure and expectations for Julia 1.13
compatibility:

- with_isolated_testset: Use Test.@with_testset on 1.13
  since Test.push_testset/Test.pop_testset were removed

- @capture patterns: Match both Core.setglobal! (1.13) and
  Base.setglobal! (1.12) in statement selection tests

- Mark tests as broken on 1.13 where the binding partition
  changes cause false positive toplevel error reports that
  are not yet fully resolved:
  - Include chain tests with NonBooleanCondErrorReport
  - World age @testset test
  - @test macros integration tests
  - File target tests (error.jl, dict.jl)

- Guard test_sum_over_string call with version check since
  it depends on inference results not available on 1.13

Written by Claude Opus 4.6
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.

Importing JET fails on 1.13.0-alpha2

2 participants