fix(data): convert self-referential container fields and defaults to declared types - #85
Merged
Merged
Conversation
Variant storage structs use `Any` for a field whenever its declared type
collapses -- a `Vector{Any}` field or a self-referential `Vector{Self}`
(which cannot reference the not-yet-defined wrapper `Type`). Julia's inner
constructor then performs no conversion, so the stored value keeps its
original type while `getproperty`/`variant_getfield` type-assert against the
declared annotation, raising a `TypeError`.
Convert each argument to its declared annotation type in the positional and
keyword constructors, restoring normal struct-construction semantics. The
conversion is a no-op when the value already matches, so construction stays
type stable. This also lets the explicit-brace form of self-referential
constructors promote a parametric singleton bottom (e.g. `Empty()`).
Fixes #32
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inside the generated module, the bare ADT name resolves to the module
itself, so a self-referential default like `arguments::Vector{SExpr} =
SExpr[]` expanded to `getindex(::Module)` and errored on construction.
Add `eval_default`, which processes a field's default value like
`eval_global_ref` but additionally rewrites the ADT name (and its
explicit `SExpr.Type` / `SExpr{...}` forms) to the module-local `Type`
alias -- so the name means `SExpr.Type` in defaults just as it already
does in field types. Qualified access such as `SExpr.Add` is preserved
so variant constructors stay reachable.
Fixes #33
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #85 +/- ##
==========================================
+ Coverage 91.69% 91.74% +0.04%
==========================================
Files 43 43
Lines 1661 1683 +22
==========================================
+ Hits 1523 1544 +21
- Misses 138 139 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
Benchmark Results (Julia v1)Time benchmarks
Memory benchmarks
|
github-actions Bot
referenced
this pull request
Jul 12, 2026
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes two closely-related bugs in how
@datahandles self-referential /container-collapsing field types. Both stem from the fact that such fields are
stored as
Anyin the variant storage struct (the wrapperTypedoesn't existyet, and self-references can't be named), so Julia's inner constructor performs
no conversion.
#32 — convert constructor arguments to declared field types
When a declared field type collapses to
Anyin storage (aVector{Any}fieldor a self-referential
Vector{Self}), the inner constructor stored the valueunchanged while
getproperty/variant_getfieldtype-asserted against thedeclared annotation, raising a
TypeError.Both the positional and keyword constructors now
Base.converteach argument toits declared annotation type, restoring normal struct-construction semantics. The
conversion is a no-op when the value already matches, so construction stays type
stable, and it lets the explicit-brace form of self-referential constructors
promote a parametric singleton bottom (e.g.
Empty()).#33 — resolve the ADT name to
.Typein default valuesInside the generated module the bare ADT name resolves to the module, so a
self-referential default like:
expanded
SExpr[]togetindex(::Module)and errored. The declared field typealready treated
SExprasSExpr.Type; the default value did not.New
eval_defaultprocesses a field's default likeeval_global_refbutadditionally rewrites the ADT name (and its explicit
SExpr.Type/SExpr{...}forms) to the module-local
Typealias, so the name meansSExpr.Typeindefaults just as in field types. Qualified access such as
SExpr.Addispreserved so variant constructors stay reachable.
Tests
test/data/emit/container.jl— regression tests for TypeError in getproperty on container types #32 (Vector{Any}andself-referential
Vector{Self}fields across anonymous/named/keywordconstructors; explicit-brace self-ref promotion for Singleton with parameters #34).
test/data/emit/selfref.jl— regression tests for Clarify typing of default values in@data#33 (bare name, explicit.Type, and parametricSExpr{T}[]defaults).data 310,match 121,derive 30,perf 5.Fixes #32
Fixes #33
🤖 Generated with Claude Code