Skip to content

Fix Julia 1.12: replace Cassette with code_typed-based branch detection#55

Merged
ChrisRackauckas merged 2 commits into
SciML:mainfrom
ChrisRackauckas-Claude:fix/julia-1-12-remove-cassette
Jun 13, 2026
Merged

Fix Julia 1.12: replace Cassette with code_typed-based branch detection#55
ChrisRackauckas merged 2 commits into
SciML:mainfrom
ChrisRackauckas-Claude:fix/julia-1-12-remove-cassette

Conversation

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Contributor

Problem

Julia 1.12 changed generated functions to require CodeInfo return values instead of Expr (see changelog). Cassette v0.3.14 does not implement this change, so every call to hasbranching fails on 1.12 with:

As of Julia 1.12, generated functions must return `CodeInfo`. See `Base.generated_body_to_codeinfo`.

This is an upstream Cassette limitation with no imminent fix.

Solution

Remove the Cassette dependency entirely and replace the IR-pass approach with a direct code_typed call:

function hasbranching(f, x...)
    is_leaf(f, x...) && return false
    results = code_typed(f, Tuple{Core.Typeof.(x)...}; optimize = false)
    isempty(results) && return false
    ci = first(results)[1]
    return any(isa(s, Core.GotoIfNot) for s in ci.code)
end

code_typed(...; optimize=false) gives type-inferred but unoptimized IR — exactly the same level Cassette's pass was inspecting. The semantics are preserved:

  • x < 0 ? -x : xGotoIfNot in IR → true
  • ifelse(x < 0, -x, x) → function call, no GotoIfNotfalse
  • true ? 1 : 0GotoIfNot in unoptimized IR → true (constant folding only happens at optimize=true)
  • x1, x2 = x destructuring → GotoIfNot stays inside iterate, invisible at the caller level → false

Breaking change: extension API

The Cassette.overdub specialization pattern is replaced by is_leaf dispatch:

# Old (broken on 1.12):
function FunctionProperties.Cassette.overdub(::FunctionProperties.HasBranchingCtx, ::typeof(f), x...)
    f(x...)
end

# New:
FunctionProperties.is_leaf(::typeof(f)) = true

Changes

  • src/FunctionProperties.jl: 170 lines → 45 lines; no deps
  • Project.toml: drop Cassette and DiffRules deps; bump to v0.1.4
  • test/runtests.jl: update extension example to use is_leaf

All tests pass on Julia 1.10 and 1.12.

Note: Please ignore until reviewed by @ChrisRackauckas.

Julia 1.12 requires generated functions to return `CodeInfo`, not `Expr`
(see `Base.generated_body_to_codeinfo`). Cassette v0.3.14 does not yet
implement this change, so all `overdub` calls fail at runtime on 1.12.

Replace the entire Cassette machinery with a direct `code_typed` call:
- `code_typed(f, argtypes; optimize=false)` gives type-inferred but
  unoptimized IR, which is exactly what the pass was inspecting before.
- Scanning the resulting `CodeInfo` for `GotoIfNot` nodes reproduces
  the original semantics: value-dependent `if`/`?:` produce `GotoIfNot`,
  `ifelse()` does not, and stdlib iteration/boundscheck branches stay
  inside their callees and are invisible at the user-function level.
- The `is_leaf(f, args...) = false` dispatch replaces the Cassette
  `overdub` extension point. Users who previously added an `overdub`
  specialization now add `FunctionProperties.is_leaf(::typeof(f)) = true`.

Remove `Cassette` and `DiffRules` from deps entirely (no deps remain).
Bump version to 0.1.4.

All tests pass on Julia 1.10 and 1.12.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas-Claude ChrisRackauckas-Claude changed the base branch from master to main June 13, 2026 07:49
Indent the body of the `if GROUP in ("All", "Core")` block, which PR SciML#53
introduced without indentation. Runic v1.7 (run in CI via FormatCheck)
requires it.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas ChrisRackauckas marked this pull request as ready for review June 13, 2026 08:00
@ChrisRackauckas ChrisRackauckas merged commit 9431084 into SciML:main Jun 13, 2026
10 of 12 checks passed
ChrisRackauckas added a commit that referenced this pull request Jun 13, 2026
PR #55 exported `is_leaf` with a docstring but did not reference it in the
manual, so Documenter's `:missing_docs` check now fails on main. Add
`is_leaf` to the API reference, and replace the outdated "uses Cassette
internally" section with a description of the `code_typed`-based branch
detection.

Verified locally: `julia --project=docs docs/make.jl` builds cleanly.

Co-authored-by: Chris Rackauckas (Claude) <accounts@chrisrackauckas.com>
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.

2 participants