@@ -664,8 +664,18 @@ impl<'a> FunctionContext<'a> {
664
664
tgt_type : & Type ,
665
665
max_depth : usize ,
666
666
) -> arbitrary:: Result < Option < TrackedExpression > > {
667
- // If we found our type, return it without further ado.
667
+ // If we found our type, we can return it without further ado.
668
668
if src_type == tgt_type {
669
+ // If we want a slice, we can push onto it.
670
+ if let Type :: Slice ( item_type) = src_type {
671
+ if bool:: arbitrary ( u) ? {
672
+ let ( item, item_dyn) = self . gen_expr ( u, item_type, max_depth, Flags :: TOP ) ?;
673
+ let push_expr =
674
+ self . call_slice_push ( src_expr, src_type. clone ( ) , bool:: arbitrary ( u) ?, item) ;
675
+ return Ok ( Some ( ( push_expr, src_dyn || item_dyn) ) ) ;
676
+ }
677
+ }
678
+ // Otherwise just return as-is.
669
679
return Ok ( Some ( ( src_expr, src_dyn) ) ) ;
670
680
}
671
681
@@ -710,7 +720,7 @@ impl<'a> FunctionContext<'a> {
710
720
} ;
711
721
Ok ( Some ( ( expr, src_dyn) ) )
712
722
}
713
- ( Type :: Array ( len, item_typ ) , _) if * len > 0 => {
723
+ ( Type :: Array ( len, item_type ) , _) if * len > 0 => {
714
724
// Indexing arrays that contains references with dynamic indexes was banned in #8888
715
725
// If we are already looking for an index where we can't use dynamic inputs,
716
726
// don't switch to using them again, as the result can indirectly poison the outer array.
@@ -723,7 +733,7 @@ impl<'a> FunctionContext<'a> {
723
733
// }
724
734
let ( idx_expr, idx_dyn) = {
725
735
let no_dynamic = self . in_no_dynamic
726
- || !self . unconstrained ( ) && types:: contains_reference ( item_typ ) ;
736
+ || !self . unconstrained ( ) && types:: contains_reference ( item_type ) ;
727
737
let was_in_no_dynamic = std:: mem:: replace ( & mut self . in_no_dynamic , no_dynamic) ;
728
738
729
739
// Choose a random index.
@@ -738,20 +748,36 @@ impl<'a> FunctionContext<'a> {
738
748
let item_expr = Expression :: Index ( Index {
739
749
collection : Box :: new ( src_expr) ,
740
750
index : Box :: new ( idx_expr) ,
741
- element_type : * item_typ . clone ( ) ,
751
+ element_type : * item_type . clone ( ) ,
742
752
location : Location :: dummy ( ) ,
743
753
} ) ;
744
754
// Produce the target type from the item.
745
755
self . gen_expr_from_source (
746
756
u,
747
757
( item_expr, src_dyn || idx_dyn) ,
748
- item_typ ,
758
+ item_type ,
749
759
src_mutable,
750
760
tgt_type,
751
761
max_depth,
752
762
)
753
763
}
754
- ( Type :: Slice ( item_typ) , _) => {
764
+ ( Type :: Slice ( item_type) , Type :: Tuple ( fields) )
765
+ if fields. len ( ) == 2
766
+ && & fields[ 0 ] == item_type. as_ref ( )
767
+ && & fields[ 1 ] == src_type =>
768
+ {
769
+ let pop_front = self . call_slice_pop ( src_expr, src_type. clone ( ) , true ) ;
770
+ Ok ( Some ( ( pop_front, src_dyn) ) )
771
+ }
772
+ ( Type :: Slice ( item_type) , Type :: Tuple ( fields) )
773
+ if fields. len ( ) == 2
774
+ && & fields[ 0 ] == src_type
775
+ && & fields[ 1 ] == item_type. as_ref ( ) =>
776
+ {
777
+ let pop_back = self . call_slice_pop ( src_expr, src_type. clone ( ) , false ) ;
778
+ Ok ( Some ( ( pop_back, src_dyn) ) )
779
+ }
780
+ ( Type :: Slice ( item_type) , _) => {
755
781
// We don't know the length of the slice at compile time,
756
782
// so we need to call the builtin function to get it,
757
783
// and use it for the length modulo.
@@ -762,7 +788,7 @@ impl<'a> FunctionContext<'a> {
762
788
( self . gen_literal ( u, & types:: U32 ) ?, false )
763
789
} else {
764
790
let no_dynamic = self . in_no_dynamic
765
- || !self . unconstrained ( ) && types:: contains_reference ( item_typ ) ;
791
+ || !self . unconstrained ( ) && types:: contains_reference ( item_type ) ;
766
792
let was_in_no_dynamic = std:: mem:: replace ( & mut self . in_no_dynamic , no_dynamic) ;
767
793
768
794
// Choose a random index.
@@ -794,27 +820,7 @@ impl<'a> FunctionContext<'a> {
794
820
let ident_2 = Ident { id : self . next_ident_id ( ) , ..ident_1. clone ( ) } ;
795
821
796
822
// Get the runtime length.
797
- let len_expr = {
798
- let array_len_ident = Ident {
799
- location : None ,
800
- definition : Definition :: Builtin ( "array_len" . to_string ( ) ) ,
801
- mutable : false ,
802
- name : "len" . to_string ( ) ,
803
- typ : Type :: Function (
804
- vec ! [ src_type. clone( ) ] ,
805
- Box :: new ( types:: U32 ) ,
806
- Box :: new ( Type :: Unit ) ,
807
- false ,
808
- ) ,
809
- id : self . next_ident_id ( ) ,
810
- } ;
811
- Expression :: Call ( Call {
812
- func : Box :: new ( Expression :: Ident ( array_len_ident) ) ,
813
- arguments : vec ! [ Expression :: Ident ( ident_1) ] ,
814
- return_type : types:: U32 ,
815
- location : Location :: dummy ( ) ,
816
- } )
817
- } ;
823
+ let len_expr = self . call_array_len ( Expression :: Ident ( ident_1) , src_type. clone ( ) ) ;
818
824
819
825
// Take the modulo.
820
826
let idx_expr = expr:: modulo ( idx_expr, len_expr) ;
@@ -823,15 +829,15 @@ impl<'a> FunctionContext<'a> {
823
829
let item_expr = Expression :: Index ( Index {
824
830
collection : Box :: new ( Expression :: Ident ( ident_2) ) ,
825
831
index : Box :: new ( idx_expr) ,
826
- element_type : * item_typ . clone ( ) ,
832
+ element_type : * item_type . clone ( ) ,
827
833
location : Location :: dummy ( ) ,
828
834
} ) ;
829
835
830
836
// Produce the target type from the item.
831
837
let Some ( ( expr, is_dyn) ) = self . gen_expr_from_source (
832
838
u,
833
839
( item_expr, src_dyn || idx_dyn) ,
834
- item_typ ,
840
+ item_type ,
835
841
src_mutable,
836
842
tgt_type,
837
843
max_depth,
@@ -1149,7 +1155,23 @@ impl<'a> FunctionContext<'a> {
1149
1155
// Generate a type or choose an existing one.
1150
1156
let max_depth = self . max_depth ( ) ;
1151
1157
let comptime_friendly = self . config ( ) . comptime_friendly ;
1152
- let typ = self . ctx . gen_type ( u, max_depth, false , false , true , comptime_friendly, true ) ?;
1158
+ let mut typ =
1159
+ self . ctx . gen_type ( u, max_depth, false , false , true , comptime_friendly, true ) ?;
1160
+
1161
+ // If we picked the target type to be a slice, we can consider popping from it.
1162
+ if let Type :: Slice ( ref item_type) = typ {
1163
+ if bool:: arbitrary ( u) ? {
1164
+ let fields = if bool:: arbitrary ( u) ? {
1165
+ // ([T], T) <- pop_back
1166
+ vec ! [ typ. clone( ) , item_type. as_ref( ) . clone( ) ]
1167
+ } else {
1168
+ // (T, [T]) <- pop_front
1169
+ vec ! [ item_type. as_ref( ) . clone( ) , typ. clone( ) ]
1170
+ } ;
1171
+ typ = Type :: Tuple ( fields) ;
1172
+ }
1173
+ }
1174
+
1153
1175
let ( expr, is_dyn) = self . gen_expr ( u, & typ, max_depth, Flags :: TOP ) ?;
1154
1176
let mutable = bool:: arbitrary ( u) ?;
1155
1177
Ok ( self . let_var ( mutable, typ, expr, true , is_dyn, local_name) )
@@ -2045,6 +2067,98 @@ impl<'a> FunctionContext<'a> {
2045
2067
} ;
2046
2068
( * id, name. clone ( ) , let_expr)
2047
2069
}
2070
+
2071
+ /// Construct a `Call` to the `array_len` builtin function, calling it with the
2072
+ /// identifier of a slice or an array.
2073
+ fn call_array_len ( & mut self , array_or_slice : Expression , typ : Type ) -> Expression {
2074
+ let func_ident = Ident {
2075
+ location : None ,
2076
+ definition : Definition :: Builtin ( "array_len" . to_string ( ) ) ,
2077
+ mutable : false ,
2078
+ name : "len" . to_string ( ) ,
2079
+ typ : Type :: Function ( vec ! [ typ] , Box :: new ( types:: U32 ) , Box :: new ( Type :: Unit ) , false ) ,
2080
+ id : self . next_ident_id ( ) ,
2081
+ } ;
2082
+ Expression :: Call ( Call {
2083
+ func : Box :: new ( Expression :: Ident ( func_ident) ) ,
2084
+ arguments : vec ! [ array_or_slice] ,
2085
+ return_type : types:: U32 ,
2086
+ location : Location :: dummy ( ) ,
2087
+ } )
2088
+ }
2089
+
2090
+ /// Construct a `Call` to the `slice_push_front` or `slice_push_back` builtin function.
2091
+ fn call_slice_push (
2092
+ & mut self ,
2093
+ slice : Expression ,
2094
+ slice_type : Type ,
2095
+ is_front : bool ,
2096
+ item : Expression ,
2097
+ ) -> Expression {
2098
+ let item_type = match slice_type {
2099
+ Type :: Slice ( ref item_type) => item_type. as_ref ( ) . clone ( ) ,
2100
+ other => unreachable ! ( "only called with slice type; got {other}" ) ,
2101
+ } ;
2102
+ let name = if is_front { "push_front" } else { "push_back" } ;
2103
+ let func_ident = Ident {
2104
+ location : None ,
2105
+ definition : Definition :: Builtin ( format ! ( "slice_{name}" ) ) ,
2106
+ mutable : false ,
2107
+ name : name. to_string ( ) ,
2108
+ typ : Type :: Function (
2109
+ vec ! [ slice_type. clone( ) , item_type] ,
2110
+ Box :: new ( slice_type. clone ( ) ) ,
2111
+ Box :: new ( Type :: Unit ) ,
2112
+ false ,
2113
+ ) ,
2114
+ id : self . next_ident_id ( ) ,
2115
+ } ;
2116
+ Expression :: Call ( Call {
2117
+ func : Box :: new ( Expression :: Ident ( func_ident) ) ,
2118
+ arguments : vec ! [ slice, item] ,
2119
+ return_type : slice_type,
2120
+ location : Location :: dummy ( ) ,
2121
+ } )
2122
+ }
2123
+
2124
+ /// Construct a `Call` to the `slice_pop_front` or `slice_pop_back` builtin function.
2125
+ fn call_slice_pop (
2126
+ & mut self ,
2127
+ slice : Expression ,
2128
+ slice_type : Type ,
2129
+ is_front : bool ,
2130
+ ) -> Expression {
2131
+ let item_type = match slice_type {
2132
+ Type :: Slice ( ref item_type) => item_type. as_ref ( ) . clone ( ) ,
2133
+ other => unreachable ! ( "only called with slice type; got {other}" ) ,
2134
+ } ;
2135
+ let name = if is_front { "pop_front" } else { "pop_back" } ;
2136
+ let fields = if is_front {
2137
+ vec ! [ item_type, slice_type. clone( ) ]
2138
+ } else {
2139
+ vec ! [ slice_type. clone( ) , item_type]
2140
+ } ;
2141
+ let return_type = Type :: Tuple ( fields) ;
2142
+ let func_ident = Ident {
2143
+ location : None ,
2144
+ definition : Definition :: Builtin ( format ! ( "slice_{name}" ) ) ,
2145
+ mutable : false ,
2146
+ name : name. to_string ( ) ,
2147
+ typ : Type :: Function (
2148
+ vec ! [ slice_type] ,
2149
+ Box :: new ( return_type. clone ( ) ) ,
2150
+ Box :: new ( Type :: Unit ) ,
2151
+ false ,
2152
+ ) ,
2153
+ id : self . next_ident_id ( ) ,
2154
+ } ;
2155
+ Expression :: Call ( Call {
2156
+ func : Box :: new ( Expression :: Ident ( func_ident) ) ,
2157
+ arguments : vec ! [ slice] ,
2158
+ return_type,
2159
+ location : Location :: dummy ( ) ,
2160
+ } )
2161
+ }
2048
2162
}
2049
2163
2050
2164
#[ cfg( test) ]
0 commit comments