1
+ use cairo_lang_defs:: ids:: NamedLanguageElementId ;
1
2
use cairo_lang_diagnostics:: Maybe ;
2
3
use cairo_lang_semantic:: corelib:: validate_literal;
3
4
use cairo_lang_semantic:: db:: SemanticGroup ;
@@ -13,6 +14,7 @@ use cairo_lang_utils::iterators::zip_eq3;
13
14
use cairo_lang_utils:: ordered_hash_map:: OrderedHashMap ;
14
15
use itertools:: Itertools ;
15
16
use num_bigint:: BigInt ;
17
+ use salsa:: Database ;
16
18
17
19
use super :: super :: graph:: {
18
20
Deconstruct , EnumMatch , FlowControlGraphBuilder , FlowControlNode , FlowControlVar , NodeId ,
@@ -48,8 +50,10 @@ use crate::lower::lower_match::numeric_match_optimization_threshold;
48
50
/// returns `0` if `y=C` and `1` otherwise.
49
51
/// Finally, the inner pattern-matching function (for `x`) will construct a [EnumMatch] node
50
52
/// that leads to the two nodes returned by the callback.
53
+ ///
54
+ /// The `path: String` parameter is an example for a pattern leading to this callback.
51
55
type BuildNodeCallback < ' db , ' a > =
52
- & ' a mut dyn FnMut ( & mut FlowControlGraphBuilder < ' db > , FilteredPatterns ) -> NodeId ;
56
+ & ' a mut dyn FnMut ( & mut FlowControlGraphBuilder < ' db > , FilteredPatterns , String ) -> NodeId ;
53
57
54
58
/// A thin wrapper around [semantic::Pattern], where `None` represents the `_` pattern.
55
59
type PatternOption < ' a , ' db > = Option < & ' a semantic:: Pattern < ' db > > ;
@@ -95,14 +99,16 @@ pub fn create_node_for_patterns<'db>(
95
99
let mut cache = Cache :: default ( ) ;
96
100
97
101
// Wrap `build_node_callback` to add the bindings to the patterns and cache the result.
98
- let mut build_node_callback =
99
- |graph : & mut FlowControlGraphBuilder < ' db > , pattern_indices : FilteredPatterns | {
100
- cache. get_or_compute (
101
- build_node_callback,
102
- graph,
103
- pattern_indices. add_bindings ( bindings. clone ( ) ) ,
104
- )
105
- } ;
102
+ let mut build_node_callback = |graph : & mut FlowControlGraphBuilder < ' db > ,
103
+ pattern_indices : FilteredPatterns ,
104
+ path : String | {
105
+ cache. get_or_compute (
106
+ build_node_callback,
107
+ graph,
108
+ pattern_indices. add_bindings ( bindings. clone ( ) ) ,
109
+ path,
110
+ )
111
+ } ;
106
112
107
113
let var_ty = graph. var_ty ( input_var) ;
108
114
let ( n_snapshots, long_ty) = peel_snapshots ( ctx. db , var_ty) ;
@@ -130,7 +136,7 @@ pub fn create_node_for_patterns<'db>(
130
136
else {
131
137
// If all the patterns are catch-all, we do not need to look into `input_var`.
132
138
// Call the callback with all patterns accepted.
133
- return build_node_callback ( graph, FilteredPatterns :: all ( patterns. len ( ) ) ) ;
139
+ return build_node_callback ( graph, FilteredPatterns :: all ( patterns. len ( ) ) , "_" . into ( ) ) ;
134
140
} ;
135
141
136
142
// Check if this is a numeric type that should use value matching.
@@ -225,8 +231,12 @@ fn create_node_for_enum<'db>(
225
231
ctx,
226
232
graph,
227
233
patterns : & inner_patterns,
228
- build_node_callback : & mut |graph, pattern_indices_inner| {
229
- build_node_callback ( graph, pattern_indices_inner. lift ( & pattern_indices) )
234
+ build_node_callback : & mut |graph, pattern_indices_inner, path| {
235
+ build_node_callback (
236
+ graph,
237
+ pattern_indices_inner. lift ( & pattern_indices) ,
238
+ format ! ( "{}({path})" , concrete_variant. id. name( ctx. db) ) ,
239
+ )
230
240
} ,
231
241
location,
232
242
} ,
@@ -269,7 +279,15 @@ fn create_node_for_tuple<'db>(
269
279
. collect_vec ( ) ;
270
280
271
281
let node = create_node_for_tuple_inner (
272
- CreateNodeParams { ctx, graph, patterns, build_node_callback, location } ,
282
+ CreateNodeParams {
283
+ ctx,
284
+ graph,
285
+ patterns,
286
+ build_node_callback : & mut |graph, pattern_indices, path| {
287
+ build_node_callback ( graph, pattern_indices, format ! ( "({path})" ) )
288
+ } ,
289
+ location,
290
+ } ,
273
291
& inner_vars,
274
292
types,
275
293
0 ,
@@ -305,7 +323,16 @@ fn create_node_for_struct<'db>(
305
323
. collect_vec ( ) ;
306
324
307
325
let node = create_node_for_tuple_inner (
308
- CreateNodeParams { ctx, graph, patterns, build_node_callback, location } ,
326
+ CreateNodeParams {
327
+ ctx,
328
+ graph,
329
+ patterns,
330
+ build_node_callback : & mut |graph, pattern_indices, path| {
331
+ let struct_name = concrete_struct_id. struct_id ( ctx. db ) . name ( ctx. db ) ;
332
+ build_node_callback ( graph, pattern_indices, format ! ( "{struct_name}{{{path}}}" ) )
333
+ } ,
334
+ location,
335
+ } ,
309
336
& inner_vars,
310
337
& types,
311
338
0 ,
@@ -334,7 +361,7 @@ fn create_node_for_tuple_inner<'db>(
334
361
let CreateNodeParams { ctx, graph, patterns, build_node_callback, location } = params;
335
362
336
363
if item_idx == types. len ( ) {
337
- return build_node_callback ( graph, FilteredPatterns :: all ( patterns. len ( ) ) ) ;
364
+ return build_node_callback ( graph, FilteredPatterns :: all ( patterns. len ( ) ) , "" . into ( ) ) ;
338
365
}
339
366
340
367
// If the type is a struct, get the current member.
@@ -399,15 +426,19 @@ fn create_node_for_tuple_inner<'db>(
399
426
ctx,
400
427
graph,
401
428
patterns : & patterns_ref,
402
- build_node_callback : & mut |graph, pattern_indices| {
429
+ build_node_callback : & mut |graph, pattern_indices, path_head | {
403
430
// Call `create_node_for_tuple_inner` recursively to handle the rest of the tuple.
404
431
create_node_for_tuple_inner (
405
432
CreateNodeParams {
406
433
ctx,
407
434
graph,
408
435
patterns : & pattern_indices. indices ( ) . map ( |idx| patterns[ idx] ) . collect_vec ( ) ,
409
- build_node_callback : & mut |graph, pattern_indices_inner| {
410
- build_node_callback ( graph, pattern_indices_inner. lift ( & pattern_indices) )
436
+ build_node_callback : & mut |graph, pattern_indices_inner, path_tail| {
437
+ build_node_callback (
438
+ graph,
439
+ pattern_indices_inner. lift ( & pattern_indices) ,
440
+ add_item_to_path ( ctx. db , & path_head, & path_tail, current_member) ,
441
+ )
411
442
} ,
412
443
location,
413
444
} ,
@@ -423,6 +454,34 @@ fn create_node_for_tuple_inner<'db>(
423
454
)
424
455
}
425
456
457
+ /// Concatenates the given `item` to the given `path_tail`.
458
+ ///
459
+ /// Adds the member name if the current item is a struct, and adds a comma if necessary.
460
+ ///
461
+ /// `current_member` is `None` for tuples, and `Some` for structs.
462
+ ///
463
+ /// For example, for tuples (`current_member` is `None`):
464
+ /// if `item` is `A` and `path_tail` is `B, C`, the result is `A, B, C`.
465
+ /// For structs (`current_member` is `Some`):
466
+ /// if `item` is `A` and `path_tail` is `b: B, c: C`, the result will be of the form:
467
+ /// `a: A, b: B, c: C`.
468
+ fn add_item_to_path < ' db > (
469
+ db : & dyn Database ,
470
+ item : & String ,
471
+ path_tail : & String ,
472
+ current_member : Option < & semantic:: Member < ' db > > ,
473
+ ) -> String {
474
+ // If it's a struct, add the member name.
475
+ let item_str = if let Some ( current_member) = current_member {
476
+ format ! ( "{}: {}" , current_member. id. name( db) , item)
477
+ } else {
478
+ item. clone ( )
479
+ } ;
480
+
481
+ // If it's not the only item, add a comma.
482
+ if path_tail. is_empty ( ) { item_str } else { format ! ( "{item_str}, {path_tail}" ) }
483
+ }
484
+
426
485
/// Handles the u256 literal as if it was a pattern of the form `u256 { low: ..., high: ... }`.
427
486
///
428
487
/// According to `item_idx`, returns the low or the high part of the value as a u128 literal
@@ -528,7 +587,7 @@ fn create_node_for_value<'db>(
528
587
} ;
529
588
530
589
// First, construct a node that handles the otherwise patterns.
531
- let mut current_node = build_node_callback ( graph, otherwise_filter. clone ( ) ) ;
590
+ let mut current_node = build_node_callback ( graph, otherwise_filter. clone ( ) , "_" . into ( ) ) ;
532
591
533
592
// Go over the literals (in reverse order), and construct a chain of [BooleanIf] nodes that
534
593
// handle each literal.
@@ -537,7 +596,7 @@ fn create_node_for_value<'db>(
537
596
if * literal < value_match_size_bigint {
538
597
continue ;
539
598
}
540
- let node_if_literal = build_node_callback ( graph, filter. clone ( ) ) ;
599
+ let node_if_literal = build_node_callback ( graph, filter. clone ( ) , literal . to_string ( ) ) ;
541
600
542
601
// Don't add an [EqualsLiteral] node if both branches lead to the same node.
543
602
if node_if_literal == current_node {
@@ -567,7 +626,9 @@ fn create_node_for_value<'db>(
567
626
let in_range_var = graph. new_var ( bounded_int_ty, graph. var_location ( input_var) ) ;
568
627
569
628
let nodes = ( 0 ..value_match_size)
570
- . map ( |i| build_node_callback ( graph, literals_map[ & BigInt :: from ( i) ] . 0 . clone ( ) ) )
629
+ . map ( |i| {
630
+ build_node_callback ( graph, literals_map[ & BigInt :: from ( i) ] . 0 . clone ( ) , i. to_string ( ) )
631
+ } )
571
632
. collect ( ) ;
572
633
let value_match_node = graph
573
634
. add_node ( FlowControlNode :: ValueMatch ( ValueMatch { matched_var : in_range_var, nodes } ) ) ;
0 commit comments