Skip to content

fix(data): restate dropped trait bounds on 6 structs + 32 where-clauses; bump 0.27.6-beta#493

Merged
ocots merged 4 commits into
mainfrom
fix/alias-where-bounds
Jul 12, 2026
Merged

fix(data): restate dropped trait bounds on 6 structs + 32 where-clauses; bump 0.27.6-beta#493
ocots merged 4 commits into
mainfrom
fix/alias-where-bounds

Conversation

@ocots

@ocots ocots commented Jul 12, 2026

Copy link
Copy Markdown
Member

Summary

  • Root cause fixed: Hamiltonian, PseudoHamiltonian, ControlLaw,
    PathConstraint, Multiplier, ControlledVectorField each declare <: AbstractX{TD,VD} (or {FB,TD,VD} / {K,TD,VD}) without repeating that
    bound on their own type-parameter list. <: between parametric types is a
    structural comparison of declared bounds — Julia does not propagate an
    abstract parent's bound down to a concrete subtype automatically. Verified:
    Hamiltonian <: AbstractHamiltonian was false before this fix, even
    though constructing an instance with an invalid TD/VD already correctly
    threw a TypeError (enforced indirectly via the supertype check). Fixed by
    restating the bound directly on all 6 structs — safe and non-breaking,
    since every previously-constructible value still builds identically.
  • 32 where-clauses restated the same dropped bounds downstream (mostly
    Base.show methods and the _natural_sig_* label helpers in
    Data/helpers.jl, plus Interpolation.Interpolant). Of 48 candidates
    flagged by the audit grep, 12 were docstring text (not real code), 10 were
    confirmed safe (genuinely unconstrained by design).
  • Regression guard added: test/suite/meta/test_data_subtyping.jl
    asserts Struct <: AbstractParent for all 10 trait-parametrized Data
    types.
  • Version bumped 0.27.5-beta → 0.27.6-beta, with a CHANGELOG.md entry
    and a non-breaking note in BREAKING.md flagging the downstream action
    needed for packages (CTFlows.jl in particular) that dispatch on these types
    with their own unbounded where-clauses.

Full audit and reasoning in .reports/2026-07-12_alias-where-bounds-audit.md
— companion to CTFlows.jl's .reports/2026-07-11_alias-where-bounds-audit.md
and the general rule now documented in the Handbook
(philosophy/types-traits-interfaces.md).

Test plan

  • julia --project -e 'using CTBase' — compiles cleanly at every stage
  • Full suite (Pkg.test()): 4713/4713 passed, 0 failures, 0 errors
    (including Aqua's ambiguity check), run 3 times across the pass
  • Every fixed struct/where-clause combination re-verified against a
    minimal reproduction before touching source

🤖 Generated with Claude Code

ocots and others added 3 commits July 12, 2026 10:28
…rete structs

Hamiltonian, PseudoHamiltonian, ControlLaw, PathConstraint, Multiplier, and
ControlledVectorField each declare `<: AbstractX{TD,VD}` (or `{FB,TD,VD}` /
`{K,TD,VD}`) — an abstract parent that bounds TD<:TimeDependence,
VD<:VariableDependence (etc.) — without repeating that bound on their own
type-parameter list. This is not automatically inherited: `<:` between
parametric types is a structural comparison of declared bounds, so
`Hamiltonian <: AbstractHamiltonian` was `false` even though constructing an
instance with an invalid TD/VD already correctly threw a TypeError at the
supertype-check step. The gap was purely at the static/dispatch level.

Verified with a minimal reproduction before touching source: adding the bound
directly to the struct declaration is safe and non-breaking (every value that
could be constructed before still builds identically), and restores the
formal subtype relation. VectorField and HamiltonianVectorField already had
the correct pattern and were used as the reference.

See .reports/2026-07-12_alias-where-bounds-audit.md for the full audit
(companion to CTFlows.jl's .reports/2026-07-11_alias-where-bounds-audit.md
and the Handbook rule in philosophy/types-traits-interfaces.md).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ssion guard

Extends the struct-level fix (previous commit) to every where {...} clause
that drops a bound relative to the type actually written in dispatch
position. Grep flagged 48 candidates: 12 were docstring text (not real code),
10 were confirmed safe (genuinely unconstrained by design — OptionValue's T,
is_a_parameter(::Type{T}), Val{Slot} compile-time tags), 26 needed a fix —
mostly Base.show (compact + MIME) on the 6 structs fixed in the previous
commit plus VectorField/HamiltonianVectorField/ComposedVectorField/
ComposedHamiltonian (already correctly bounded, only their Base.show where
clauses had dropped it), and the _natural_sig_* label helpers in
Data/helpers.jl, plus Interpolant{M} in Interpolation/.

Adds test/suite/meta/test_data_subtyping.jl: asserts Struct <: AbstractParent
for the 6 structs fixed in the previous commit plus the 4 already-correct
ones, as a standing regression guard.

Full suite: 4713/4713 passed (4703 + 10 new).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ropping fix

Documents the fix from the previous two commits (6 structs + 32 where-clauses
restating their abstract parent's TD/VD/FB/K bound) in CHANGELOG.md, and adds
a non-breaking note in BREAKING.md warning downstream packages (CTFlows.jl in
particular) that dispatch on these types with their own unbounded
where-clauses — their ground truth has changed and should be re-audited.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ocots
ocots merged commit 02f4d19 into main Jul 12, 2026
4 checks passed
@ocots
ocots deleted the fix/alias-where-bounds branch July 12, 2026 08:39
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