1
1
use clarity:: vm:: clarity_wasm:: get_type_size;
2
2
use clarity:: vm:: types:: {
3
- FunctionType , ListTypeData , SequenceSubtype , StringSubtype , TypeSignature ,
3
+ FixedFunction , FunctionType , ListTypeData , SequenceSubtype , StringSubtype , TypeSignature ,
4
4
} ;
5
5
use clarity:: vm:: { ClarityName , SymbolicExpression } ;
6
6
use walrus:: ir:: { self , BinaryOp , IfElse , InstrSeqType , Loop , UnaryOp } ;
@@ -90,7 +90,7 @@ impl ComplexWord for Fold {
90
90
& self ,
91
91
generator : & mut WasmGenerator ,
92
92
builder : & mut walrus:: InstrSeqBuilder ,
93
- _expr : & SymbolicExpression ,
93
+ expr : & SymbolicExpression ,
94
94
args : & [ SymbolicExpression ] ,
95
95
) -> Result < ( ) , GeneratorError > {
96
96
check_args ! ( generator, builder, 3 , args. len( ) , ArgumentCountCheck :: Exact ) ;
@@ -112,21 +112,60 @@ impl ComplexWord for Fold {
112
112
// (- 6 (- 4 (- 2 0)))
113
113
// ```
114
114
115
- // WORKAROUND: Get the type of the function being called, and set the
116
- // type of the initial value to match the functions parameter type.
117
- // This is a workaround for the typechecker not being able to infer
118
- // the complete type of initial value.
119
- if let Some ( FunctionType :: Fixed ( fixed) ) = generator. get_function_type ( func) {
120
- let initial_ty = fixed
121
- . args
122
- . get ( 1 )
115
+ // To make sure that the initial value will reserve enough space in memory, we reassign its type to the type of the expression.
116
+ generator. set_expr_type (
117
+ initial,
118
+ generator
119
+ . get_expr_type ( expr)
123
120
. ok_or_else ( || {
124
- GeneratorError :: TypeError ( "expected function with 2 arguments" . into ( ) )
121
+ GeneratorError :: TypeError ( "fold expression should be typed" . to_owned ( ) )
125
122
} ) ?
126
- . signature
127
- . clone ( ) ;
128
- generator. set_expr_type ( initial, initial_ty) ?;
123
+ . clone ( ) ,
124
+ ) ?;
125
+
126
+ // We need to find the correct types expected by the function `func` and the result type of the fold expression
127
+ // to make sure everything will be coherent in the end.
128
+ // This is only needed if we are folding a list and the function is user-defined.
129
+ struct FoldFuncTy {
130
+ elem_ty : TypeSignature ,
131
+ acc_ty : TypeSignature ,
132
+ return_ty : TypeSignature ,
129
133
}
134
+ let fold_func_ty = {
135
+ match generator. get_expr_type ( sequence) . ok_or_else ( || {
136
+ GeneratorError :: TypeError ( "Folded sequence should be typed" . to_owned ( ) )
137
+ } ) ? {
138
+ TypeSignature :: SequenceType ( SequenceSubtype :: ListType ( ltd) ) => {
139
+ match generator. get_function_type ( func) {
140
+ Some ( FunctionType :: Fixed ( FixedFunction { args, returns } ) )
141
+ if args. len ( ) == 2 =>
142
+ {
143
+ let fold_func_ty = FoldFuncTy {
144
+ elem_ty : args[ 0 ] . signature . clone ( ) ,
145
+ acc_ty : args[ 1 ] . signature . clone ( ) ,
146
+ return_ty : returns. clone ( ) ,
147
+ } ;
148
+ // Set the type of the list elements
149
+ generator. set_expr_type (
150
+ sequence,
151
+ TypeSignature :: SequenceType ( SequenceSubtype :: ListType (
152
+ ListTypeData :: new_list (
153
+ fold_func_ty. elem_ty . clone ( ) ,
154
+ ltd. get_max_len ( ) ,
155
+ )
156
+ . map_err ( |e| GeneratorError :: TypeError ( e. to_string ( ) ) ) ?,
157
+ ) ) ,
158
+ ) ?;
159
+ // set the accumulator type
160
+ generator. set_expr_type ( initial, fold_func_ty. acc_ty . clone ( ) ) ?;
161
+ Some ( fold_func_ty)
162
+ }
163
+ _ => None ,
164
+ }
165
+ }
166
+ _ => None ,
167
+ }
168
+ } ;
130
169
131
170
// The result type must match the type of the initial value
132
171
let result_clar_ty = generator
@@ -227,6 +266,10 @@ impl ComplexWord for Fold {
227
266
} else {
228
267
// Call user defined function
229
268
generator. visit_call_user_defined ( & mut loop_, & result_clar_ty, func) ?;
269
+ // since the accumulator and the return type of the function could have different types, we need to duck-type.
270
+ if let Some ( tys) = & fold_func_ty {
271
+ generator. duck_type ( & mut loop_, & tys. return_ty , & tys. acc_ty ) ?;
272
+ }
230
273
}
231
274
// Save the result into the locals (in reverse order as we pop)
232
275
for result_local in result_locals. iter ( ) . rev ( ) {
@@ -262,6 +305,11 @@ impl ComplexWord for Fold {
262
305
alternative : else_id,
263
306
} ) ;
264
307
308
+ // since the return type of the function and the accumulator could have different types, we need to duck-type.
309
+ if let Some ( tys) = & fold_func_ty {
310
+ generator. duck_type ( builder, & tys. acc_ty , & tys. return_ty ) ?;
311
+ }
312
+
265
313
Ok ( ( ) )
266
314
}
267
315
}
@@ -2354,6 +2402,34 @@ mod tests {
2354
2402
crosscheck ( snippet, Ok ( Some ( expected) ) )
2355
2403
}
2356
2404
2405
+ #[ test]
2406
+ fn fold_with_response_partial_acc ( ) {
2407
+ let snippet = "
2408
+ (define-private (foo (a (response int bool)) (b (response int uint)))
2409
+ (match b
2410
+ bok (ok (+ (unwrap-panic a) bok))
2411
+ berr (ok (+ (unwrap-panic a) (to-int berr)))
2412
+ )
2413
+ )
2414
+ (fold foo (list (ok 1)) (ok 2))
2415
+ " ;
2416
+ crosscheck ( snippet, Ok ( Some ( Value :: okay ( Value :: Int ( 3 ) ) . unwrap ( ) ) ) ) ;
2417
+ }
2418
+
2419
+ #[ test]
2420
+ fn fold_with_response_full_acc ( ) {
2421
+ let snippet = "
2422
+ (define-private (foo (a (response int bool)) (b (response int uint)))
2423
+ (match b
2424
+ bok (ok (+ (unwrap-panic a) bok))
2425
+ berr (err (+ (to-uint (unwrap-panic a)) berr))
2426
+ )
2427
+ )
2428
+ (fold foo (list (ok 1)) (ok 2))
2429
+ " ;
2430
+ crosscheck ( snippet, Ok ( Some ( Value :: okay ( Value :: Int ( 3 ) ) . unwrap ( ) ) ) ) ;
2431
+ }
2432
+
2357
2433
#[ test]
2358
2434
fn unit_fold_repsonses_full_type ( ) {
2359
2435
let snippet = "
0 commit comments