rue_compiler/compile/expr/
function_call.rs

1use std::collections::HashMap;
2
3use log::debug;
4use rue_ast::{AstFunctionCallExpr, AstNode};
5use rue_diagnostic::DiagnosticKind;
6use rue_hir::{BinaryOp, Builtin, FunctionCall, Hir, Symbol, UnaryOp, Value};
7use rue_lir::ClvmOp;
8use rue_types::{Pair, Type, TypeId, Union, substitute_with_mappings};
9
10use crate::{Compiler, compile_expr};
11
12pub fn compile_function_call_expr(ctx: &mut Compiler, call: &AstFunctionCallExpr) -> Value {
13    let Some(expr) = call.expr() else {
14        debug!("Unresolved function call expr");
15        return ctx.builtins().unresolved.clone();
16    };
17
18    let expr = compile_expr(ctx, &expr, None);
19
20    if let Hir::Reference(symbol) = ctx.hir(expr.hir).clone()
21        && let Symbol::Builtin(builtin) = ctx.symbol(symbol).clone()
22    {
23        return compile_builtin(ctx, call, builtin);
24    }
25
26    let is_unresolved = ctx.is_unresolved(expr.ty);
27
28    let expected_functions = rue_types::extract_functions(ctx.types_mut(), expr.ty);
29
30    if expected_functions.len() > 1 {
31        let name = ctx.type_name(expr.ty);
32        ctx.diagnostic(
33            call.syntax(),
34            DiagnosticKind::CannotDisambiguateFunctionTypes(name),
35        );
36    } else if expected_functions.is_empty() && !is_unresolved {
37        let name = ctx.type_name(expr.ty);
38        ctx.diagnostic(call.syntax(), DiagnosticKind::InvalidFunctionCall(name));
39    }
40
41    let expected_function = expected_functions.first();
42
43    let len = call.args().count();
44
45    let mut nil_terminated = true;
46    let mut args = Vec::new();
47
48    for (i, arg) in call.args().enumerate() {
49        if let Some(spread) = arg.spread() {
50            if i == len - 1 {
51                nil_terminated = false;
52            } else {
53                ctx.diagnostic(&spread, DiagnosticKind::NonFinalSpread);
54            }
55        }
56
57        args.extend(arg.expr());
58    }
59
60    let (args, mappings) = if let Some(function) = expected_function {
61        if function.nil_terminated != nil_terminated {
62            ctx.diagnostic(call.syntax(), DiagnosticKind::InvalidSpread);
63        }
64
65        if function.params.len() != args.len() {
66            ctx.diagnostic(
67                call.syntax(),
68                DiagnosticKind::ExpectedArguments(function.params.len(), args.len()),
69            );
70        }
71
72        let mut mappings: HashMap<TypeId, Vec<TypeId>> = HashMap::new();
73        let mut results = Vec::new();
74
75        for (i, (_, param)) in function.params.iter().enumerate() {
76            let substitute_mappings = mappings
77                .iter()
78                .map(|(k, v)| {
79                    (
80                        *k,
81                        if v.is_empty() {
82                            ctx.builtins().types.never
83                        } else if v.len() == 1 {
84                            v[0]
85                        } else {
86                            ctx.alloc_type(Type::Union(Union::new(v.clone())))
87                        },
88                    )
89                })
90                .collect();
91
92            let param = substitute_with_mappings(ctx.types_mut(), *param, &substitute_mappings);
93
94            if let Some(expr) = args.get(i) {
95                let value = compile_expr(ctx, expr, Some(param));
96                results.push(value.hir);
97                ctx.infer_type(expr.syntax(), value.ty, param, &mut mappings);
98            } else {
99                debug!("Unresolved function call argument");
100                results.push(ctx.builtins().unresolved.hir);
101            }
102        }
103
104        (results, mappings)
105    } else {
106        (vec![], HashMap::new())
107    };
108
109    let ty = if expected_functions.is_empty() {
110        debug!("Unresolved function call return type due to unresolved function type");
111        ctx.alloc_type(Type::Unresolved)
112    } else if expected_functions.len() == 1 {
113        expected_functions[0].ret
114    } else {
115        ctx.alloc_type(Type::Union(Union::new(
116            expected_functions
117                .iter()
118                .map(|function| function.ret)
119                .collect(),
120        )))
121    };
122
123    let mappings = mappings
124        .into_iter()
125        .map(|(k, v)| {
126            (
127                k,
128                if v.is_empty() {
129                    ctx.builtins().types.never
130                } else if v.len() == 1 {
131                    v[0]
132                } else {
133                    ctx.alloc_type(Type::Union(Union::new(v)))
134                },
135            )
136        })
137        .collect();
138
139    let ty = substitute_with_mappings(ctx.types_mut(), ty, &mappings);
140
141    let hir = ctx.alloc_hir(Hir::FunctionCall(FunctionCall {
142        function: expr.hir,
143        args,
144        nil_terminated,
145    }));
146
147    Value::new(hir, ty)
148}
149
150fn compile_builtin(ctx: &mut Compiler, call: &AstFunctionCallExpr, builtin: Builtin) -> Value {
151    let mut args = Vec::new();
152    let mut spread = None;
153
154    let len = call.args().count();
155
156    for (i, arg) in call.args().enumerate() {
157        let Some(expr) = arg.expr() else {
158            debug!("Unresolved clvm op argument");
159            continue;
160        };
161
162        if let Some(op) = arg.spread() {
163            if i == len - 1 {
164                spread = Some(op);
165            } else {
166                ctx.diagnostic(arg.syntax(), DiagnosticKind::NonFinalSpread);
167            }
168        }
169
170        let value = compile_expr(ctx, &expr, None);
171
172        args.push((value, expr));
173    }
174
175    match builtin {
176        Builtin::Sha256 { inline } | Builtin::Keccak256 { inline } => {
177            if args.len() != 1 {
178                ctx.diagnostic(
179                    call.syntax(),
180                    DiagnosticKind::ExpectedArguments(1, args.len()),
181                );
182            }
183
184            let value = if let Some((value, expr)) = args.first() {
185                let ty = if let Some(spread) = &spread {
186                    if inline {
187                        ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
188                    }
189
190                    let list = ctx.builtins().types.list;
191
192                    let mappings = HashMap::from_iter([(
193                        ctx.builtins().types.list_generic,
194                        ctx.builtins().types.bytes,
195                    )]);
196
197                    rue_types::substitute_with_mappings(ctx.types_mut(), list, &mappings)
198                } else {
199                    ctx.builtins().types.bytes
200                };
201                ctx.assign_type(expr.syntax(), value.ty, ty);
202                value.hir
203            } else {
204                ctx.builtins().unresolved.hir
205            };
206
207            let hir = match (builtin, spread) {
208                (Builtin::Sha256 { inline }, None) => {
209                    if inline {
210                        ctx.alloc_hir(Hir::Unary(UnaryOp::Sha256Inline, value))
211                    } else {
212                        ctx.alloc_hir(Hir::Unary(UnaryOp::Sha256, value))
213                    }
214                }
215                (Builtin::Keccak256 { inline }, None) => {
216                    if inline {
217                        ctx.alloc_hir(Hir::Unary(UnaryOp::Keccak256Inline, value))
218                    } else {
219                        ctx.alloc_hir(Hir::Unary(UnaryOp::Keccak256, value))
220                    }
221                }
222                (Builtin::Sha256 { .. }, Some(_)) => {
223                    ctx.alloc_hir(Hir::ClvmOp(ClvmOp::Sha256, value))
224                }
225                (Builtin::Keccak256 { .. }, Some(_)) => {
226                    ctx.alloc_hir(Hir::ClvmOp(ClvmOp::Keccak256, value))
227                }
228                _ => unreachable!(),
229            };
230
231            Value::new(hir, ctx.builtins().types.bytes32)
232        }
233        Builtin::Concat => {
234            if args.len() != 1 {
235                ctx.diagnostic(
236                    call.syntax(),
237                    DiagnosticKind::ExpectedArguments(1, args.len()),
238                );
239            }
240
241            if let Some(spread) = &spread {
242                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
243            }
244
245            let value = if let Some((value, expr)) = args.first() {
246                let list = ctx.builtins().types.list;
247
248                let mappings = HashMap::from_iter([(
249                    ctx.builtins().types.list_generic,
250                    ctx.builtins().types.bytes,
251                )]);
252
253                let ty = rue_types::substitute_with_mappings(ctx.types_mut(), list, &mappings);
254                ctx.assign_type(expr.syntax(), value.ty, ty);
255                value.hir
256            } else {
257                ctx.builtins().unresolved.hir
258            };
259
260            let hir = ctx.alloc_hir(Hir::ClvmOp(ClvmOp::Concat, value));
261
262            Value::new(hir, ctx.builtins().types.bytes)
263        }
264        Builtin::CoinId => {
265            if args.len() != 3 {
266                ctx.diagnostic(
267                    call.syntax(),
268                    DiagnosticKind::ExpectedArguments(3, args.len()),
269                );
270            }
271
272            if let Some(spread) = &spread {
273                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
274            }
275
276            let hir = if args.len() == 3 {
277                ctx.assign_type(args[0].1.syntax(), args[0].0.ty, ctx.builtins().types.bytes);
278                ctx.assign_type(args[1].1.syntax(), args[1].0.ty, ctx.builtins().types.bytes);
279                ctx.assign_type(args[2].1.syntax(), args[2].0.ty, ctx.builtins().types.int);
280                ctx.alloc_hir(Hir::CoinId(args[0].0.hir, args[1].0.hir, args[2].0.hir))
281            } else {
282                ctx.builtins().unresolved.hir
283            };
284
285            Value::new(hir, ctx.builtins().types.bytes32)
286        }
287        Builtin::Substr => {
288            if args.len() != 2 && args.len() != 3 {
289                ctx.diagnostic(
290                    call.syntax(),
291                    DiagnosticKind::ExpectedArgumentsBetween(2, 3, args.len()),
292                );
293            }
294
295            if let Some(spread) = &spread {
296                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
297            }
298
299            let hir = if args.len() >= 2 {
300                ctx.assign_type(args[0].1.syntax(), args[0].0.ty, ctx.builtins().types.bytes);
301                ctx.assign_type(args[1].1.syntax(), args[1].0.ty, ctx.builtins().types.int);
302
303                if args.len() == 3 {
304                    ctx.assign_type(args[2].1.syntax(), args[2].0.ty, ctx.builtins().types.int);
305                }
306
307                ctx.alloc_hir(Hir::Substr(
308                    args[0].0.hir,
309                    args[1].0.hir,
310                    args.get(2).map(|arg| arg.0.hir),
311                ))
312            } else {
313                ctx.builtins().unresolved.hir
314            };
315
316            Value::new(hir, ctx.builtins().types.bytes)
317        }
318        Builtin::Sum | Builtin::Difference | Builtin::Product => {
319            if args.len() != 1 {
320                ctx.diagnostic(
321                    call.syntax(),
322                    DiagnosticKind::ExpectedArguments(1, args.len()),
323                );
324            }
325
326            if let Some(spread) = &spread {
327                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
328            }
329
330            let value = if let Some((value, expr)) = args.first() {
331                let list = ctx.builtins().types.list;
332
333                let mappings = HashMap::from_iter([(
334                    ctx.builtins().types.list_generic,
335                    ctx.builtins().types.int,
336                )]);
337
338                let ty = rue_types::substitute_with_mappings(ctx.types_mut(), list, &mappings);
339                ctx.assign_type(expr.syntax(), value.ty, ty);
340                value.hir
341            } else {
342                ctx.builtins().unresolved.hir
343            };
344
345            let hir = ctx.alloc_hir(Hir::ClvmOp(
346                match builtin {
347                    Builtin::Sum => ClvmOp::Add,
348                    Builtin::Difference => ClvmOp::Sub,
349                    Builtin::Product => ClvmOp::Mul,
350                    _ => unreachable!(),
351                },
352                value,
353            ));
354
355            Value::new(hir, ctx.builtins().types.int)
356        }
357        Builtin::Divmod => {
358            if args.len() != 2 {
359                ctx.diagnostic(
360                    call.syntax(),
361                    DiagnosticKind::ExpectedArguments(2, args.len()),
362                );
363            }
364
365            if let Some(spread) = &spread {
366                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
367            }
368
369            let hir = if args.len() == 2 {
370                ctx.assign_type(args[0].1.syntax(), args[0].0.ty, ctx.builtins().types.int);
371                ctx.assign_type(args[1].1.syntax(), args[1].0.ty, ctx.builtins().types.int);
372                ctx.alloc_hir(Hir::Binary(BinaryOp::Divmod, args[0].0.hir, args[1].0.hir))
373            } else {
374                ctx.builtins().unresolved.hir
375            };
376
377            let int = ctx.builtins().types.int;
378            let pair = ctx.alloc_type(Type::Pair(Pair::new(int, int)));
379
380            Value::new(hir, pair)
381        }
382        Builtin::Modpow => {
383            if args.len() != 3 {
384                ctx.diagnostic(
385                    call.syntax(),
386                    DiagnosticKind::ExpectedArguments(3, args.len()),
387                );
388            }
389
390            if let Some(spread) = &spread {
391                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
392            }
393
394            let hir = if args.len() == 3 {
395                ctx.assign_type(args[0].1.syntax(), args[0].0.ty, ctx.builtins().types.int);
396                ctx.assign_type(args[1].1.syntax(), args[1].0.ty, ctx.builtins().types.int);
397                ctx.assign_type(args[2].1.syntax(), args[2].0.ty, ctx.builtins().types.int);
398                ctx.alloc_hir(Hir::Modpow(args[0].0.hir, args[1].0.hir, args[2].0.hir))
399            } else {
400                ctx.builtins().unresolved.hir
401            };
402
403            Value::new(hir, ctx.builtins().types.int)
404        }
405        Builtin::Any | Builtin::All => {
406            if args.len() != 1 {
407                ctx.diagnostic(
408                    call.syntax(),
409                    DiagnosticKind::ExpectedArguments(1, args.len()),
410                );
411            }
412
413            if let Some(spread) = &spread {
414                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
415            }
416
417            let value = if let Some((value, expr)) = args.first() {
418                let list = ctx.builtins().types.list;
419
420                let mappings = HashMap::from_iter([(
421                    ctx.builtins().types.list_generic,
422                    ctx.builtins().types.bool,
423                )]);
424
425                let ty = rue_types::substitute_with_mappings(ctx.types_mut(), list, &mappings);
426                ctx.assign_type(expr.syntax(), value.ty, ty);
427                value.hir
428            } else {
429                ctx.builtins().unresolved.hir
430            };
431
432            let hir = ctx.alloc_hir(Hir::ClvmOp(
433                match builtin {
434                    Builtin::Any => ClvmOp::Any,
435                    Builtin::All => ClvmOp::All,
436                    _ => unreachable!(),
437                },
438                value,
439            ));
440
441            Value::new(hir, ctx.builtins().types.bool)
442        }
443        Builtin::PubkeyForExp => {
444            if args.len() != 1 {
445                ctx.diagnostic(
446                    call.syntax(),
447                    DiagnosticKind::ExpectedArguments(1, args.len()),
448                );
449            }
450
451            if let Some(spread) = &spread {
452                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
453            }
454
455            let hir = if args.len() == 1 {
456                ctx.assign_type(
457                    args[0].1.syntax(),
458                    args[0].0.ty,
459                    ctx.builtins().types.bytes32,
460                );
461                ctx.alloc_hir(Hir::Unary(UnaryOp::PubkeyForExp, args[0].0.hir))
462            } else {
463                ctx.builtins().unresolved.hir
464            };
465
466            Value::new(hir, ctx.builtins().types.public_key)
467        }
468        Builtin::G1Sum | Builtin::G1Difference => {
469            if args.len() != 1 {
470                ctx.diagnostic(
471                    call.syntax(),
472                    DiagnosticKind::ExpectedArguments(1, args.len()),
473                );
474            }
475
476            if let Some(spread) = &spread {
477                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
478            }
479
480            let value = if let Some((value, expr)) = args.first() {
481                let list = ctx.builtins().types.list;
482
483                let mappings = HashMap::from_iter([(
484                    ctx.builtins().types.list_generic,
485                    ctx.builtins().types.public_key,
486                )]);
487
488                let ty = rue_types::substitute_with_mappings(ctx.types_mut(), list, &mappings);
489                ctx.assign_type(expr.syntax(), value.ty, ty);
490                value.hir
491            } else {
492                ctx.builtins().unresolved.hir
493            };
494
495            let hir = ctx.alloc_hir(Hir::ClvmOp(
496                match builtin {
497                    Builtin::G1Sum => ClvmOp::G1Add,
498                    Builtin::G1Difference => ClvmOp::G1Subtract,
499                    _ => unreachable!(),
500                },
501                value,
502            ));
503
504            Value::new(hir, ctx.builtins().types.public_key)
505        }
506        Builtin::G2Sum | Builtin::G2Difference => {
507            if args.len() != 1 {
508                ctx.diagnostic(
509                    call.syntax(),
510                    DiagnosticKind::ExpectedArguments(1, args.len()),
511                );
512            }
513
514            if let Some(spread) = &spread {
515                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
516            }
517
518            let value = if let Some((value, expr)) = args.first() {
519                let list = ctx.builtins().types.list;
520
521                let mappings = HashMap::from_iter([(
522                    ctx.builtins().types.list_generic,
523                    ctx.builtins().types.signature,
524                )]);
525
526                let ty = rue_types::substitute_with_mappings(ctx.types_mut(), list, &mappings);
527                ctx.assign_type(expr.syntax(), value.ty, ty);
528                value.hir
529            } else {
530                ctx.builtins().unresolved.hir
531            };
532
533            let hir = ctx.alloc_hir(Hir::ClvmOp(
534                match builtin {
535                    Builtin::G2Sum => ClvmOp::G2Add,
536                    Builtin::G2Difference => ClvmOp::G2Subtract,
537                    _ => unreachable!(),
538                },
539                value,
540            ));
541
542            Value::new(hir, ctx.builtins().types.signature)
543        }
544        Builtin::G1Map | Builtin::G2Map => {
545            if args.len() != 1 && args.len() != 2 {
546                ctx.diagnostic(
547                    call.syntax(),
548                    DiagnosticKind::ExpectedArgumentsBetween(1, 2, args.len()),
549                );
550            }
551
552            if let Some(spread) = &spread {
553                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
554            }
555
556            let data = if let Some((value, expr)) = args.first() {
557                ctx.assign_type(expr.syntax(), value.ty, ctx.builtins().types.bytes);
558                value.hir
559            } else {
560                ctx.builtins().unresolved.hir
561            };
562
563            let dst = args.get(1).map(|(value, expr)| {
564                ctx.assign_type(expr.syntax(), value.ty, ctx.builtins().types.bytes);
565                value.hir
566            });
567
568            let hir = match builtin {
569                Builtin::G1Map => ctx.alloc_hir(Hir::G1Map(data, dst)),
570                Builtin::G2Map => ctx.alloc_hir(Hir::G2Map(data, dst)),
571                _ => unreachable!(),
572            };
573
574            Value::new(
575                hir,
576                match builtin {
577                    Builtin::G1Map => ctx.builtins().types.public_key,
578                    Builtin::G2Map => ctx.builtins().types.signature,
579                    _ => unreachable!(),
580                },
581            )
582        }
583        Builtin::BlsPairingIdentity => {
584            let hir = if spread.is_some() {
585                if args.len() != 1 {
586                    ctx.diagnostic(
587                        call.syntax(),
588                        DiagnosticKind::ExpectedArguments(1, args.len()),
589                    );
590                }
591
592                let list = ctx.builtins().types.alternating_list;
593
594                let mappings = HashMap::from_iter([
595                    (
596                        ctx.builtins().types.alternating_list_generic_a,
597                        ctx.builtins().types.public_key,
598                    ),
599                    (
600                        ctx.builtins().types.alternating_list_generic_b,
601                        ctx.builtins().types.signature,
602                    ),
603                ]);
604
605                let ty = rue_types::substitute_with_mappings(ctx.types_mut(), list, &mappings);
606
607                if let Some((value, expr)) = args.first() {
608                    ctx.assign_type(expr.syntax(), value.ty, ty);
609                    ctx.alloc_hir(Hir::ClvmOp(ClvmOp::BlsPairingIdentity, value.hir))
610                } else {
611                    ctx.builtins().unresolved.hir
612                }
613            } else {
614                if args.len() % 2 != 0 {
615                    ctx.diagnostic(call.syntax(), DiagnosticKind::ExpectedEvenArguments);
616                }
617
618                for (i, (value, expr)) in args.iter().enumerate() {
619                    if i % 2 == 0 {
620                        ctx.assign_type(expr.syntax(), value.ty, ctx.builtins().types.public_key);
621                    } else {
622                        ctx.assign_type(expr.syntax(), value.ty, ctx.builtins().types.signature);
623                    }
624                }
625
626                ctx.alloc_hir(Hir::BlsPairingIdentity(
627                    args.iter().map(|arg| arg.0.hir).collect(),
628                ))
629            };
630
631            Value::new(hir, ctx.builtins().types.nil)
632        }
633        Builtin::BlsVerify => {
634            let hir = if spread.is_some() {
635                if args.len() != 2 {
636                    ctx.diagnostic(
637                        call.syntax(),
638                        DiagnosticKind::ExpectedArguments(2, args.len()),
639                    );
640                }
641
642                let list = ctx.builtins().types.alternating_list;
643
644                let mappings = HashMap::from_iter([
645                    (
646                        ctx.builtins().types.alternating_list_generic_a,
647                        ctx.builtins().types.public_key,
648                    ),
649                    (
650                        ctx.builtins().types.alternating_list_generic_b,
651                        ctx.builtins().types.bytes,
652                    ),
653                ]);
654
655                let ty = rue_types::substitute_with_mappings(ctx.types_mut(), list, &mappings);
656
657                if args.len() == 2 {
658                    ctx.assign_type(
659                        args[0].1.syntax(),
660                        args[0].0.ty,
661                        ctx.builtins().types.signature,
662                    );
663                    ctx.assign_type(args[1].1.syntax(), args[1].0.ty, ty);
664
665                    let pair = ctx.alloc_hir(Hir::Pair(args[0].0.hir, args[1].0.hir));
666                    ctx.alloc_hir(Hir::ClvmOp(ClvmOp::BlsVerify, pair))
667                } else {
668                    ctx.builtins().unresolved.hir
669                }
670            } else {
671                if args.is_empty() || args.len() % 2 != 1 {
672                    ctx.diagnostic(
673                        call.syntax(),
674                        DiagnosticKind::ExpectedOneArgumentEvenAdditional,
675                    );
676                }
677
678                for (i, (value, expr)) in args.iter().enumerate() {
679                    if i == 0 {
680                        ctx.assign_type(expr.syntax(), value.ty, ctx.builtins().types.signature);
681                    } else if i % 2 == 1 {
682                        ctx.assign_type(expr.syntax(), value.ty, ctx.builtins().types.public_key);
683                    } else {
684                        ctx.assign_type(expr.syntax(), value.ty, ctx.builtins().types.bytes);
685                    }
686                }
687
688                if args.is_empty() {
689                    ctx.builtins().unresolved.hir
690                } else {
691                    ctx.alloc_hir(Hir::BlsVerify(
692                        args[0].0.hir,
693                        args.iter().skip(1).map(|arg| arg.0.hir).collect(),
694                    ))
695                }
696            };
697
698            Value::new(hir, ctx.builtins().types.nil)
699        }
700        Builtin::Secp256K1Verify | Builtin::Secp256R1Verify => {
701            if args.len() != 3 {
702                ctx.diagnostic(
703                    call.syntax(),
704                    DiagnosticKind::ExpectedArguments(3, args.len()),
705                );
706            }
707
708            if let Some(spread) = &spread {
709                ctx.diagnostic(spread, DiagnosticKind::InvalidSpreadBuiltin);
710            }
711
712            let hir = if args.len() == 3 {
713                ctx.assign_type(
714                    args[0].1.syntax(),
715                    args[0].0.ty,
716                    match builtin {
717                        Builtin::Secp256K1Verify => ctx.builtins().types.k1_public_key,
718                        Builtin::Secp256R1Verify => ctx.builtins().types.r1_public_key,
719                        _ => unreachable!(),
720                    },
721                );
722                ctx.assign_type(
723                    args[1].1.syntax(),
724                    args[1].0.ty,
725                    ctx.builtins().types.bytes32,
726                );
727                ctx.assign_type(
728                    args[2].1.syntax(),
729                    args[2].0.ty,
730                    match builtin {
731                        Builtin::Secp256K1Verify => ctx.builtins().types.k1_signature,
732                        Builtin::Secp256R1Verify => ctx.builtins().types.r1_signature,
733                        _ => unreachable!(),
734                    },
735                );
736
737                match builtin {
738                    Builtin::Secp256K1Verify => ctx.alloc_hir(Hir::Secp256K1Verify(
739                        args[0].0.hir,
740                        args[1].0.hir,
741                        args[2].0.hir,
742                    )),
743                    Builtin::Secp256R1Verify => ctx.alloc_hir(Hir::Secp256R1Verify(
744                        args[0].0.hir,
745                        args[1].0.hir,
746                        args[2].0.hir,
747                    )),
748                    _ => unreachable!(),
749                }
750            } else {
751                ctx.builtins().unresolved.hir
752            };
753
754            Value::new(hir, ctx.builtins().types.nil)
755        }
756    }
757}