Fix recursion limit for enums with many variants#99
Draft
antiguru wants to merge 5 commits intofrankmcsherry:masterfrom
Draft
Fix recursion limit for enums with many variants#99antiguru wants to merge 5 commits intofrankmcsherry:masterfrom
antiguru wants to merge 5 commits intofrankmcsherry:masterfrom
Conversation
The linear chain pattern `chain(chain(chain(a, b), c), d)` creates O(N)-deep nested types that hit the compiler's recursion limit for enums with many variants (e.g. 80+). Replace with a balanced binary tree that produces O(log N) depth, supporting up to 256 variants. Also add an asm inspection example for verifying inlining. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Mark Chain::fold as #[inline(always)] so the balanced chain tree is fully inlined for large enums (256 variants: count compiles to a constant, encode has zero residual fold calls). - Restructure encode's Read 3 loop to eliminate bounds-check panics: use split_at instead of manual slicing, chunks_exact instead of resize+index for misaligned copies, and bytemuck::cast_mut for the remainder. This removes both slice_index_fail and copy_from_slice::len_mismatch_fail from the generated assembly, reducing big_encode from 43K to 36K instructions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These propagate exact sizes through the balanced chain tree, making count() O(1) instead of O(N). For a 256-variant enum, as_bytes().count() compiles to a constant without needing fold. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The encode loop bodies are identical for every byte slice, but the fully-inlined Chain fold duplicates them at each of the N leaves. Route the closures through `&mut dyn FnMut` so the iterator tree inlines (for efficient traversal) while the per-slice work exists only once. For a 256-variant enum: big_encode shrinks from 37K to ~12.5K instructions, small_encode from 562 to 167, with no measurable runtime difference (workload is memcpy-bound). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
Summary
Fixes compilation failures for enums with many variants (80+) that hit Rust's type recursion limit due to deeply nested
Chain<Chain<Chain<...>>>types in derivedas_bytes()implementations.Changes
Balanced chain tree in derive macro: Replace linear
chain(chain(chain(a, b), c), d)nesting (O(N) depth) with a balanced binary tree (O(log N) depth). Supports up to 256 variants.#[inline(always)]onChain::fold: Ensures the balanced chain tree is fully inlined —as_bytes().count()compiles to a single constant for any size enum.size_hintandcountonChain/ChainOne: Propagates exact sizes through the tree, makingcount()O(1) without needing fold. Eliminates an entire iteration pass inencode.Eliminate panic paths in
encode: Restructure the byte-copying loop to usesplit_at,chunks_exact, andbytemuck::cast_mut. Removesslice_index_failandcopy_from_slice::len_mismatch_failfrom generated assembly.Dyn dispatch in
encodeloop bodies: Route the per-slice closures through&mut dyn FnMutso the inlined iterator tree emits one indirect call per leaf instead of duplicating the closure body at each of the N fold sites. 3× code size reduction with no measurable runtime cost (workload is memcpy-bound).Assembly verification
Includes
examples/enum_as_bytes_asm.rswith a 256-variant enum forcargo rustc --example enum_as_bytes_asm --release -- --emit asminspection.as_bytes().count()mov w0, #258; retencodetotal code sizeencodepanic pathssmall_encode(3 variants)Test plan
cargo test)cargo run --release)🤖 Generated with Claude Code