Skip to content

Remove IfElse.jl dependency; use Base.ifelse#473

Merged
ChrisRackauckas merged 1 commit into
SciML:mainfrom
ChrisRackauckas-Claude:fix-red-lane
Jul 5, 2026
Merged

Remove IfElse.jl dependency; use Base.ifelse#473
ChrisRackauckas merged 1 commit into
SciML:mainfrom
ChrisRackauckas-Claude:fix-red-lane

Conversation

@ChrisRackauckas-Claude

@ChrisRackauckas-Claude ChrisRackauckas-Claude commented Jul 5, 2026

Copy link
Copy Markdown
Member

Please ignore until reviewed by @ChrisRackauckas.

Problem

The strict Downgrade lane (allow_reresolve=false) fails because the resolver was allowed to pick IfElse 0.1.0, whose greedy varargs method

ifelse(args...) = Core.ifelse(args...)

intercepts symbolic Num conditions and forwards to Core.ifelse (which requires a Bool), throwing TypeError: non-boolean (Num) used in boolean context at symbolic source constructors such as Sine (src/Blocks/sources.jl).

Fix

Remove the IfElse.jl dependency entirely and rely on Base.ifelse. On modern Julia, IfElse.ifelse is const ifelse = Base.ifelse, and Symbolics registers a symbolic method on Base.ifelse, so ifelse(sym_cond, a, b) still builds a symbolic ifelse(...) expression rather than eagerly evaluating.

  • Replaced all IfElse.ifelse(...) call sites with bare ifelse(...).
  • Removed every using IfElse / import IfElse line.
  • Removed IfElse from [deps] and [compat].
  • src/Blocks/Blocks.jl: the removed line was import IfElse: ifelse, which brought ifelse into module scope so the ifelse(::Bool, ::Parameter, ...) overloads in sources.jl extended it. A bare removal makes those method definitions create a new Blocks.ifelse binding that shadows Base.ifelse for the whole module, so the symbolic Base.ifelse method is no longer visible and Sine/Step/Ramp/… fail to build with MethodError: no method matching ifelse(::Num, …). Replaced with import Base: ifelse so the Parameter overloads extend Base.ifelse and the symbolic method stays in scope.
  • Dropped the now-stale Downgrade.yml comment referencing the fntype regression.

Validation (local, Julia 1.12)

  • The package precompiles cleanly after the change (both the normal env and the floored downgrade manifest). ModelingToolkitStandardLibrary.Blocks.ifelse === Base.ifelse is true.
  • Symbolic tracing confirmed: ifelse(t % T > duty_cycle*T, 1, 0) (Digital Pulse / DigitalPin comparator patterns) build SymbolicUtils.BasicSymbolic ifelse(...) nodes — no eager evaluation.
  • Sine (and Square/Step/Ramp/Triangular/ExpSine) build successfully after the import Base: ifelse fix (they fail with a bare removal — confirmed).
  • Ran the affected test files (test/analog.jl full pass — exercises Square/Sine/Triangular plus NMOS/PMOS/NPN/PNP transistors and mosfets which use ifelse; test/digital.jl; test/sources.jl; test/isothermal_compressible.jl; test/thermal.jl). No new failures. The pre-existing errors (fntype_X_Y interpolation errors in sources.jl, Valve DomainError in isothermal_compressible.jl, ExtraVariablesSystemException in thermal.jl) reproduce byte-identically on unmodified main and are unrelated to this change (existing MTK 11 / DataInterpolations incompatibilities in the dev depot).
  • Strict downgrade harness (julia-downgrade-compat v2.6.1 downgrade.jl) now reports "Successfully resolved minimal versions for . (with extras)". IfElse remains only as a transitive dependency of Static/CPUSummary/etc.; MTKStandardLibrary no longer depends on it directly, so the resolver is no longer exposed to IfElse's ifelse method for the package's own symbolic code.

🤖 Generated with Claude Code

IfElse.jl's `ifelse` is `const ifelse = Base.ifelse` on modern Julia (0.1.1),
so the dependency is redundant: Symbolics registers a symbolic method on
`Base.ifelse`, so `ifelse(sym_cond, a, b)` traces to a symbolic expression
without IfElse. Removing IfElse also unblocks the strict Downgrade lane, which
previously could resolve IfElse 0.1.0 (whose greedy `ifelse(args...) =
Core.ifelse(args...)` broke symbolic conditions).

- Replace `IfElse.ifelse(...)` call sites with bare `ifelse(...)`.
- Drop every `using IfElse` / `import IfElse` line.
- Remove IfElse from [deps] and [compat].
- In Blocks, the removed `import IfElse: ifelse` had brought `ifelse` into
  scope so the `Parameter` overloads in sources.jl extended it. Replace with
  `import Base: ifelse` so those overloads extend `Base.ifelse` instead of
  creating a new `Blocks.ifelse` binding that would shadow the symbolic method.
- Drop the now-stale Downgrade.yml comment referencing the fntype regression.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas-Claude ChrisRackauckas-Claude changed the title Downgrade: raise IfElse compat floor to 0.1.1 Remove IfElse.jl dependency; use Base.ifelse Jul 5, 2026
@ChrisRackauckas
ChrisRackauckas marked this pull request as ready for review July 5, 2026 07:02
@ChrisRackauckas
ChrisRackauckas merged commit dca3a23 into SciML:main Jul 5, 2026
5 of 11 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.

2 participants