Skip to content

feature(cairo): Support Span<T> destructuring via fixed-size array pa…#9823

Open
ilyalesokhin-starkware wants to merge 1 commit intomainfrom
ilya/span_unpack
Open

feature(cairo): Support Span<T> destructuring via fixed-size array pa…#9823
ilyalesokhin-starkware wants to merge 1 commit intomainfrom
ilya/span_unpack

Conversation

@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor

@ilyalesokhin-starkware ilyalesokhin-starkware commented Apr 6, 2026

…tterns

Add support for let [a, b] = span else { ... } syntax by introducing a SliceDestructure flow control node that calls tuple_from_span to convert Span to [T; N] at runtime.


Note

Medium Risk
Touches semantic type-checking and match/let lowering by adding a new flow-control node and runtime conversion path; bugs here could affect pattern matching correctness and generated code for matches involving Span<T> and fixed-size array patterns.

Overview
Adds support for matching/destructuring Span<T> with fixed-size array patterns (e.g. let [a, b] = span else { ... } and match span { [a,b] => ... }).

Lowering now builds a size-dispatch chain for Span<T> patterns via a new FlowControlNode::SliceDestructure, which calls corelib tuple_from_span (with FixedSizedArrayInfoImpl) and handles success/failure branching. Semantic pattern computation is updated to accept fixed-size array patterns on Span types, and diagnostics/tests are extended to cover the new behavior.

Reviewed by Cursor Bugbot for commit dbd4689. Bugbot is set up for automated code reviews on this repo. Configure here.

@reviewable-StarkWare
Copy link
Copy Markdown

This change is Reviewable

Comment thread crates/cairo-lang-semantic/src/expr/compute.rs Outdated
@ilyalesokhin-starkware ilyalesokhin-starkware marked this pull request as draft April 6, 2026 13:55
@ilyalesokhin-starkware ilyalesokhin-starkware changed the base branch from main to graphite-base/9823 April 9, 2026 08:38
@ilyalesokhin-starkware ilyalesokhin-starkware changed the base branch from graphite-base/9823 to ilya/span_pattern_semantic April 9, 2026 08:38
Copy link
Copy Markdown
Contributor Author

ilyalesokhin-starkware commented Apr 9, 2026

@ilyalesokhin-starkware ilyalesokhin-starkware changed the base branch from ilya/span_pattern_semantic to graphite-base/9823 April 9, 2026 13:19
@ilyalesokhin-starkware ilyalesokhin-starkware changed the base branch from graphite-base/9823 to main April 12, 2026 06:22
Comment thread crates/cairo-lang-semantic/src/expr/compute.rs Outdated
Copy link
Copy Markdown
Contributor Author

@ilyalesokhin-starkware ilyalesokhin-starkware left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ilyalesokhin-starkware resolved 1 discussion.
Reviewable status: 0 of 11 files reviewed, 1 unresolved discussion (waiting on orizi).

Copy link
Copy Markdown
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi made 2 comments.
Reviewable status: 0 of 11 files reviewed, 3 unresolved discussions (waiting on ilyalesokhin-starkware).


corelib/src/test/language_features/match_test.cairo line 26 at r3 (raw file):

            assert_eq!(b, @20);
            assert_eq!(c, @30);
        },

Suggestion:

        [a, b, c] => {
            assert_eq!((*a, *b, *c), (10, 20, 30));
        },

crates/cairo-lang-lowering/src/lower/flow_control/test_data/match line 2603 at r3 (raw file):

    match s {
        [a, b] => *a + *b,
        [a, b] => *a + *b,

add also:

Suggestion:

    match s {
        [a, b] | [a, b, _] => *a + *b,

@ilyalesokhin-starkware ilyalesokhin-starkware force-pushed the ilya/span_unpack branch 3 times, most recently from 9df5995 to 0aa3fa0 Compare April 14, 2026 08:41
@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor Author

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 465 at r5 (raw file):

                        group.filter.add(idx);
                        group.patterns.push(None);
                    }

Not sure if I should do that like in create_node_for_enum, or just use break here.

Code quote:

                    opt_wildcard_idx = Some(idx);
                    for group in size_groups.values_mut() {
                        group.filter.add(idx);
                        group.patterns.push(None);
                    }

@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor Author

corelib/src/test/language_features/match_test.cairo line 26 at r3 (raw file):

            assert_eq!(b, @20);
            assert_eq!(c, @30);
        },

Done.

Copy link
Copy Markdown
Contributor Author

@ilyalesokhin-starkware ilyalesokhin-starkware left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ilyalesokhin-starkware made 1 comment and resolved 1 discussion.
Reviewable status: 0 of 11 files reviewed, 3 unresolved discussions (waiting on orizi).


crates/cairo-lang-lowering/src/lower/flow_control/test_data/match line 2603 at r3 (raw file):

Previously, orizi wrote…

add also:

Done.

@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor Author

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 465 at r5 (raw file):

Previously, ilyalesokhin-starkware wrote…

Not sure if I should do that like in create_node_for_enum, or just use break here.

see r5 <-> r6.

@ilyalesokhin-starkware ilyalesokhin-starkware marked this pull request as ready for review April 14, 2026 10:56
@cursor
Copy link
Copy Markdown

cursor bot commented Apr 14, 2026

PR Summary

Medium Risk
Adds new lowering/control-flow graph machinery for Span<T> fixed-size array destructuring via runtime TryInto, which changes match/let-else codegen paths and could affect pattern reachability/exhaustiveness behavior.

Overview
Adds lowering support for destructuring Span<T> using fixed-size array patterns (e.g. [a, b], []) in match/if let by introducing a new flow-control node, SliceDestructure, that tries sizes in order and falls through on length mismatch.

The lowering pipeline now builds a Span<T> size-dispatch chain when FixedSizeArray patterns are present, and lowers it by reconstructing a Span from @Array, calling the core TryInto::try_into impl to obtain a boxed fixed-size array, then unboxing/destructuring elements on success.

Updates core semantic CoreInfo to expose TryInto/try_into and box_forward_snapshot, and extends tests/fixtures to cover span matching (including empty patterns, inner-pattern mismatches, wildcard/struct catch-alls, and unreachable/non-exhaustive diagnostics).

Reviewed by Cursor Bugbot for commit 29abfa2. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread crates/cairo-lang-semantic/src/helper.rs Outdated
Copy link
Copy Markdown
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 6 files and all commit messages, made 1 comment, and resolved 2 discussions.
Reviewable status: 6 of 11 files reviewed, 3 unresolved discussions (waiting on ilyalesokhin-starkware).


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 549 at r6 (raw file):

    /// single inner pattern and feed into [create_node_for_patterns], which wants it already
    /// unwrapped, hence the asymmetry.
    patterns: Vec<PatternOption<'a, 'db>>,

doesn't the index in the filter provide this already?

@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor Author

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 549 at r6 (raw file):

Previously, orizi wrote…

doesn't the index in the filter provide this already?

It can also be None, so I would need some index to include that.
Also, FilteredPatterns doesn't provide a convenient way to access the index.

This is smpler

Comment thread crates/cairo-lang-semantic/src/helper.rs Outdated
Copy link
Copy Markdown
Contributor Author

@ilyalesokhin-starkware ilyalesokhin-starkware left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ilyalesokhin-starkware resolved 1 discussion.
Reviewable status: 6 of 11 files reviewed, 2 unresolved discussions (waiting on orizi).

Copy link
Copy Markdown
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 1 file and made 3 comments.
Reviewable status: 7 of 11 files reviewed, 4 unresolved discussions (waiting on ilyalesokhin-starkware).


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 549 at r6 (raw file):

Previously, ilyalesokhin-starkware wrote…

It can also be None, so I would need some index to include that.
Also, FilteredPatterns doesn't provide a convenient way to access the index.

This is smpler

got it - call it inner_patterns maybe like we call it in the VariantInfo struct.

even possibly you can use the exact same struct (and change its name) and share much more code


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 401 at r7 (raw file):

/// Each size is tried in order. On failure, the next size is attempted. If all sizes fail,
/// the wildcard/otherwise patterns are used.
#[allow(clippy::too_many_arguments)]

is there no good logical grouping for params to remove this?

Code quote:

#[allow(clippy::too_many_arguments)]

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 430 at r7 (raw file):

            Some(semantic::Pattern::FixedSizeArray(p)) => Some(p),
            _ => None,
        });

Suggestion:

        let first_fsa = patterns.iter().flatten().find_map(|p| try_extract_matches!(p, semantic::Pattern::FixedSizeArray));

@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor Author

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 430 at r7 (raw file):

            Some(semantic::Pattern::FixedSizeArray(p)) => Some(p),
            _ => None,
        });

decided to remove it; it shouldn't have happened anyway.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3b35bee. Configure here.

@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor Author

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 549 at r6 (raw file):

Previously, orizi wrote…

got it - call it inner_patterns maybe like we call it in the VariantInfo struct.

even possibly you can use the exact same struct (and change its name) and share much more code

/// Note: unlike [VariantInfo], which stores the inner pattern of each `EnumVariant` arm,
/// here we store the whole `FixedSizeArray` pattern. The next stage —

@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor Author

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 401 at r7 (raw file):

Previously, orizi wrote…

is there no good logical grouping for params to remove this?

Done.

Copy link
Copy Markdown
Contributor Author

@ilyalesokhin-starkware ilyalesokhin-starkware left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ilyalesokhin-starkware resolved 1 discussion.
Reviewable status: 7 of 11 files reviewed, 3 unresolved discussions (waiting on orizi).

Copy link
Copy Markdown
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 1 file and all commit messages, made 3 comments, and resolved 3 discussions.
Reviewable status: 8 of 11 files reviewed, 3 unresolved discussions (waiting on ilyalesokhin-starkware).


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 396 at r9 (raw file):

/// Each size is tried in order. On failure, the next size is attempted. If all sizes fail,
/// the wildcard/otherwise patterns are used.
fn try_create_slice_destructure_chain<'db>(

can you separate this function into 2:

  1. check if you want to try to match using the [] pattern.
  2. actually return a node id.

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 411 at r9 (raw file):

    // the `StructConstruct(Span<T>)` it emits rely on this: `snapshot_array_var` below must be
    // exactly `@Array<T>`, not a wrapped variant.
    if wrapping_info.n_outer_snapshots != 0 || wrapping_info.n_boxed_inner_snapshots.is_some() {

can you add a test with:

match arr.span() {
      [v0] => {...},
      [v1] => {...},
      Span { snapshot: inner } =>{...}
}

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 511 at r9 (raw file):

    }));

    Some(chain)

Suggestion:

    Some(graph.add_node(FlowControlNode::Deconstruct(Deconstruct {
        input: input_var,
        outputs: vec![snapshot_array_var],
        next: failure_node,
    })))

@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor Author

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 411 at r9 (raw file):

Previously, orizi wrote…

can you add a test with:

match arr.span() {
      [v0] => {...},
      [v1] => {...},
      Span { snapshot: inner } =>{...}
}

Do we want to support don't catch everything?

…tterns

Add support for `let [a, b] = span else { ... }` syntax by introducing
a SliceDestructure flow control node that calls tuple_from_span to
convert Span<T> to [T; N] at runtime.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Copy link
Copy Markdown
Contributor Author

@ilyalesokhin-starkware ilyalesokhin-starkware left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ilyalesokhin-starkware made 3 comments.
Reviewable status: 7 of 11 files reviewed, 4 unresolved discussions (waiting on orizi).


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 396 at r9 (raw file):

Previously, orizi wrote…

can you separate this function into 2:

  1. check if you want to try to match using the [] pattern.
  2. actually return a node id.

Done.


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 511 at r9 (raw file):

    }));

    Some(chain)

Done.


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 503 at r10 (raw file):

                // A catch-all struct pattern makes every subsequent arm unreachable, so we stop
                // adding to `size_groups`. The match-completeness pass will flag the dead arms.
                break;

we can break here for now, right?

Code quote:

                // A catch-all struct pattern makes every subsequent arm unreachable, so we stop
                // adding to `size_groups`. The match-completeness pass will flag the dead arms.
                break;

Copy link
Copy Markdown
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 2 files and all commit messages, made 4 comments, and resolved 2 discussions.
Reviewable status: 9 of 11 files reviewed, 4 unresolved discussions (waiting on ilyalesokhin-starkware).


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 411 at r9 (raw file):

Previously, ilyalesokhin-starkware wrote…

Do we want to support don't catch everything?

this just checks that can open the type of "catch everything" object.


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 503 at r10 (raw file):

Previously, ilyalesokhin-starkware wrote…

we can break here for now, right?

assuming they are still considered merged and supply diagnostics - this make sense.


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 354 at r10 (raw file):

            elem_ty,
        );
    }

Suggestion:

    if let Some(elem_ty) =
        should_create_slice_destructure_chain(params.ctx.db, params.patterns, concrete_struct_id, wrapping_info)
        // or change signature to `should_create_slice_destructure_chain(&params, concrete_struct_id, wrapping_info)`
    {
        return create_slice_destructure_chain(
            params,
            input_var,
            concrete_struct_id,
            elem_ty,
        );
    }
    let CreateNodeParams { ctx, graph, patterns, build_node_callback, location } = params;

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 443 at r10 (raw file):

    let snapshot_member_id = members.iter().next().unwrap().1.id;
    let snapshot_array_ty = members.iter().next().unwrap().1.ty;
    let snapshot_array_var = graph.new_var(snapshot_array_ty, location);

Suggestion:

    let members = match ctx.db.concrete_struct_members(concrete_struct_id) {
        Ok(members) => members.iter().exactly_one(),
        Err(diag_added) => return graph.add_node(FlowControlNode::Missing(diag_added)),
    };
    let (snapshot_member_id, snapshot_array_ty) = match members.iter().exactly_one() {
        Ok((_, member)) => (member.id, member.ty),
        Err(e) => panic!("Got wrong number of `Span` members: `{e:?}`."),
    };
    let snapshot_array_var = graph.new_var(snapshot_array_ty, location);

@ilyalesokhin-starkware
Copy link
Copy Markdown
Contributor Author

crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 503 at r10 (raw file):

Previously, orizi wrote…

assuming they are still considered merged and supply diagnostics - this make sense.

what do you mean by considered merged?

Copy link
Copy Markdown
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi made 1 comment.
Reviewable status: 9 of 11 files reviewed, 4 unresolved discussions (waiting on ilyalesokhin-starkware).


crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs line 503 at r10 (raw file):

Previously, ilyalesokhin-starkware wrote…

what do you mean by considered merged?

just the goto at the end the arm to the continuation of the flow.

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.

3 participants