@@ -2,17 +2,23 @@ use cairo_lang_debug::DebugWithDb;
22use cairo_lang_defs:: ids:: NamedLanguageElementId ;
33use cairo_lang_diagnostics:: { DiagnosticNote , Maybe } ;
44use cairo_lang_filesystem:: flag:: FlagsGroup ;
5- use cairo_lang_semantic:: corelib:: { CorelibSemantic , validate_literal} ;
5+ use cairo_lang_filesystem:: ids:: SmolStrId ;
6+ use cairo_lang_semantic:: corelib:: {
7+ CorelibSemantic , get_usize_ty, try_get_core_ty_by_name, validate_literal,
8+ } ;
69use cairo_lang_semantic:: expr:: compute:: unwrap_pattern_type;
10+ use cairo_lang_semantic:: items:: constant:: ConstValue ;
711use cairo_lang_semantic:: items:: enm:: SemanticEnumEx ;
812use cairo_lang_semantic:: items:: structure:: StructSemantic ;
13+ use cairo_lang_semantic:: types:: wrap_in_snapshots;
914use cairo_lang_semantic:: {
1015 self as semantic, ConcreteEnumId , ConcreteStructId , ConcreteTypeId , ExprNumericLiteral ,
11- PatternEnumVariant , PatternLiteral , PatternStruct , PatternTuple , PatternWrappingInfo , TypeId ,
12- TypeLongId , corelib,
16+ GenericArgumentId , PatternEnumVariant , PatternLiteral , PatternStruct , PatternTuple ,
17+ PatternWrappingInfo , TypeId , TypeLongId , corelib,
1318} ;
1419use cairo_lang_syntax:: node:: TypedStablePtr ;
1520use cairo_lang_syntax:: node:: ast:: ExprPtr ;
21+ use cairo_lang_utils:: Intern ;
1622use cairo_lang_utils:: ordered_hash_map:: OrderedHashMap ;
1723use itertools:: { Itertools , zip_eq} ;
1824use num_bigint:: BigInt ;
@@ -26,7 +32,9 @@ use super::filtered_patterns::{Bindings, FilteredPatterns};
2632use crate :: diagnostic:: { LoweringDiagnosticKind , MatchDiagnostic , MatchError } ;
2733use crate :: ids:: LocationId ;
2834use crate :: lower:: context:: LoweringContext ;
29- use crate :: lower:: flow_control:: graph:: { Downcast , EqualsLiteral , Upcast , ValueMatch } ;
35+ use crate :: lower:: flow_control:: graph:: {
36+ Downcast , EqualsLiteral , SliceDestructure , Upcast , ValueMatch ,
37+ } ;
3038
3139/// A callback that gets a [FilteredPatterns] and constructs a node that continues the pattern
3240/// matching restricted to the filtered patterns.
@@ -339,6 +347,19 @@ fn create_node_for_struct<'db>(
339347) -> NodeId {
340348 let CreateNodeParams { ctx, graph, patterns, build_node_callback, location } = params;
341349
350+ if let Some ( node) = try_create_slice_destructure_chain (
351+ ctx,
352+ graph,
353+ patterns,
354+ build_node_callback,
355+ location,
356+ input_var,
357+ concrete_struct_id,
358+ wrapping_info,
359+ ) {
360+ return node;
361+ }
362+
342363 let members = match ctx. db . concrete_struct_members ( concrete_struct_id) {
343364 Ok ( members) => members,
344365 Err ( diag_added) => return graph. add_node ( FlowControlNode :: Missing ( diag_added) ) ,
@@ -375,6 +396,144 @@ fn create_node_for_struct<'db>(
375396 } ) )
376397}
377398
399+ /// Tries to create a chain of [`SliceDestructure`] nodes for matching a `Span<T>` against
400+ /// fixed-size array patterns with different sizes.
401+ ///
402+ /// Returns `None` if no `FixedSizeArray` patterns are present or the struct is not a `Span`.
403+ /// Each size is tried in order. On failure, the next size is attempted. If all sizes fail,
404+ /// the wildcard/otherwise patterns are used.
405+ #[ allow( clippy:: too_many_arguments) ]
406+ fn try_create_slice_destructure_chain < ' db > (
407+ ctx : & LoweringContext < ' db , ' _ > ,
408+ graph : & mut FlowControlGraphBuilder < ' db > ,
409+ patterns : & [ PatternOption < ' _ , ' db > ] ,
410+ build_node_callback : BuildNodeCallback < ' db , ' _ > ,
411+ location : LocationId < ' db > ,
412+ input_var : FlowControlVar ,
413+ concrete_struct_id : ConcreteStructId < ' db > ,
414+ wrapping_info : PatternWrappingInfo ,
415+ ) -> Option < NodeId > {
416+ if !patterns. iter ( ) . any ( |p| matches ! ( p, Some ( semantic:: Pattern :: FixedSizeArray ( ..) ) ) ) {
417+ return None ;
418+ }
419+ let [ GenericArgumentId :: Type ( elem_ty) ] = concrete_struct_id. long ( ctx. db ) . generic_args [ ..]
420+ else {
421+ return None ;
422+ } ;
423+ if try_get_core_ty_by_name (
424+ ctx. db ,
425+ SmolStrId :: from ( ctx. db , "Span" ) ,
426+ vec ! [ GenericArgumentId :: Type ( elem_ty) ] ,
427+ )
428+ . is_err ( )
429+ {
430+ // Not a Span - report error on the first FixedSizeArray pattern.
431+ let first_fsa = patterns. iter ( ) . find_map ( |p| match p {
432+ Some ( semantic:: Pattern :: FixedSizeArray ( p) ) => Some ( p) ,
433+ _ => None ,
434+ } ) ;
435+ return Some ( graph. report_with_missing_node (
436+ first_fsa. unwrap ( ) . stable_ptr . untyped ( ) ,
437+ LoweringDiagnosticKind :: UnexpectedError ,
438+ ) ) ;
439+ }
440+ // Deconstruct Span<T> to get its single member @Array<T>.
441+ let members = ctx. db . concrete_struct_members ( concrete_struct_id) . ok ( ) ?;
442+ let snapshot_array_ty = members. iter ( ) . next ( ) . unwrap ( ) . 1 . ty ;
443+ let snapshot_array_var = graph. new_var ( snapshot_array_ty, location) ;
444+
445+ // Group patterns by array size. Wildcards/otherwise are added to all groups.
446+ // Use an OrderedHashMap to preserve insertion order (first-seen size first).
447+ let mut size_groups: OrderedHashMap < usize , SizeGroupInfo > = OrderedHashMap :: default ( ) ;
448+ let mut wildcard_filter = FilteredPatterns :: default ( ) ;
449+
450+ for ( idx, pattern) in patterns. iter ( ) . enumerate ( ) {
451+ match pattern {
452+ Some ( semantic:: Pattern :: FixedSizeArray ( p) ) => {
453+ let n = p. elements_patterns . len ( ) ;
454+ let group = size_groups. entry ( n) . or_default ( ) ;
455+ group. filter . add ( idx) ;
456+ group. patterns . push ( * pattern) ;
457+ }
458+ Some ( semantic:: Pattern :: Otherwise ( ..) ) | None => {
459+ wildcard_filter. add ( idx) ;
460+ for group in size_groups. values_mut ( ) {
461+ group. filter . add ( idx) ;
462+ group. patterns . push ( None ) ;
463+ }
464+ }
465+ _ => unreachable ! ( "Non-FixedSizeArray/Otherwise pattern in slice destructure chain" ) ,
466+ }
467+ }
468+
469+ let sizes: Vec < usize > = size_groups. keys ( ) . copied ( ) . collect ( ) ;
470+
471+ // Build the chain from back to front. The final fallback is the wildcard-only callback.
472+ let mut failure_node = build_node_callback ( graph, wildcard_filter, "[slice_no_match]" . into ( ) ) ;
473+
474+ for & size in sizes. iter ( ) . rev ( ) {
475+ let group = size_groups. swap_remove ( & size) . unwrap ( ) ;
476+ let n = size;
477+ let types = vec ! [ wrap_in_snapshots( ctx. db, elem_ty, 1 ) ; n] ;
478+ let inner_vars = types
479+ . iter ( )
480+ . map ( |ty| graph. new_var ( wrapping_info. wrap ( ctx. db , * ty) , location) )
481+ . collect_vec ( ) ;
482+
483+ // Build the success path: process element patterns within this size group.
484+ let group_filter = group. filter ;
485+ let group_patterns: Vec < PatternOption < ' _ , ' db > > = group. patterns ;
486+ let success = create_node_for_tuple_inner (
487+ CreateNodeParams {
488+ ctx,
489+ graph,
490+ patterns : & group_patterns,
491+ build_node_callback : & mut |graph, pattern_indices, path| {
492+ build_node_callback (
493+ graph,
494+ pattern_indices. lift ( & group_filter) ,
495+ format ! ( "[{path}]" ) ,
496+ )
497+ } ,
498+ location,
499+ } ,
500+ & inner_vars,
501+ & types,
502+ 0 ,
503+ None ,
504+ ) ;
505+
506+ let fixed_array_ty = TypeLongId :: FixedSizeArray {
507+ type_id : elem_ty,
508+ size : ConstValue :: Int ( n. into ( ) , get_usize_ty ( ctx. db ) ) . intern ( ctx. db ) ,
509+ }
510+ . intern ( ctx. db ) ;
511+
512+ failure_node = graph. add_node ( FlowControlNode :: SliceDestructure ( SliceDestructure {
513+ input : snapshot_array_var,
514+ fixed_array_ty,
515+ outputs : inner_vars,
516+ success,
517+ failure : failure_node,
518+ } ) ) ;
519+ }
520+
521+ // Wrap in a Deconstruct to extract @Array<T> from Span<T> once.
522+ let chain = graph. add_node ( FlowControlNode :: Deconstruct ( Deconstruct {
523+ input : input_var,
524+ outputs : vec ! [ snapshot_array_var] ,
525+ next : failure_node,
526+ } ) ) ;
527+
528+ Some ( chain)
529+ }
530+
531+ #[ derive( Default ) ]
532+ struct SizeGroupInfo < ' a , ' db > {
533+ filter : FilteredPatterns ,
534+ patterns : Vec < PatternOption < ' a , ' db > > ,
535+ }
536+
378537/// Helper function for [create_node_for_tuple].
379538///
380539/// `item_idx` is the index of the current member that is being processed in the tuple.
@@ -429,6 +588,13 @@ fn create_node_for_tuple_inner<'db>(
429588 patterns_on_current_item. push ( Some ( inner_pattern) )
430589 }
431590 }
591+ Some ( semantic:: Pattern :: FixedSizeArray ( semantic:: PatternFixedSizeArray {
592+ elements_patterns,
593+ ..
594+ } ) ) if current_member. is_none ( ) => {
595+ patterns_on_current_item
596+ . push ( Some ( get_pattern ( ctx, elements_patterns[ item_idx] ) . clone ( ) ) ) ;
597+ }
432598 Some (
433599 pattern @ ( semantic:: Pattern :: StringLiteral ( ..)
434600 | semantic:: Pattern :: EnumVariant ( ..)
0 commit comments