charon_lib/transform/
ops_to_function_calls.rs

1//! Desugar some unary/binary operations and the array repeats to function calls.
2//! For instance, we desugar ArrayToSlice from an unop to a function call.
3//! This allows a more uniform treatment later on.
4//! TODO: actually transform all the unops and binops to function calls?
5use crate::llbc_ast::*;
6use crate::transform::TransformCtx;
7
8use super::ctx::LlbcPass;
9
10fn transform_st(s: &mut Statement) {
11    match &s.content {
12        // Transform the ArrayToSlice unop
13        RawStatement::Assign(
14            p,
15            Rvalue::UnaryOp(
16                UnOp::Cast(CastKind::Unsize(src_ty, tgt_ty, UnsizingMetadata::Length(_))),
17                op,
18            ),
19        ) => {
20            if let (
21                TyKind::Ref(_, deref!(TyKind::Adt(tref1)), kind1),
22                TyKind::Ref(_, deref!(TyKind::Adt(tref2)), kind2),
23            ) = (src_ty.kind(), tgt_ty.kind())
24                && matches!(tref1.id, TypeId::Builtin(BuiltinTy::Array))
25                && matches!(tref2.id, TypeId::Builtin(BuiltinTy::Slice))
26            {
27                // In MIR terminology, we go from &[T; l] to &[T] which means we
28                // effectively "unsize" the type, as `l` no longer appears in the
29                // destination type. At runtime, the converse happens: the length
30                // materializes into the fat pointer.
31                assert!(
32                    tref1.generics.types.elem_count() == 1
33                        && tref1.generics.const_generics.elem_count() == 1
34                );
35                assert!(tref1.generics.types[0] == tref2.generics.types[0]);
36                assert!(kind1 == kind2);
37                // We could avoid the clone operations below if we take the content of
38                // the statement. In practice, this shouldn't have much impact.
39                let id = match *kind1 {
40                    RefKind::Mut => BuiltinFunId::ArrayToSliceMut,
41                    RefKind::Shared => BuiltinFunId::ArrayToSliceShared,
42                };
43                let func = FunIdOrTraitMethodRef::mk_builtin(id);
44                let generics = GenericArgs::new(
45                    [Region::Erased].into(),
46                    tref1.generics.types.clone(),
47                    tref1.generics.const_generics.clone(),
48                    [].into(),
49                );
50                let func = FnOperand::Regular(FnPtr {
51                    func: Box::new(func),
52                    generics: Box::new(generics),
53                });
54                s.content = RawStatement::Call(Call {
55                    func,
56                    args: vec![op.clone()],
57                    dest: p.clone(),
58                });
59            }
60        }
61        // Transform the array aggregates to function calls
62        RawStatement::Assign(p, Rvalue::Repeat(op, ty, cg)) => {
63            // We could avoid the clone operations below if we take the content of
64            // the statement. In practice, this shouldn't have much impact.
65            let id = BuiltinFunId::ArrayRepeat;
66            let func = FunIdOrTraitMethodRef::mk_builtin(id);
67            let generics = GenericArgs::new(
68                [Region::Erased].into(),
69                [ty.clone()].into(),
70                [cg.clone()].into(),
71                [].into(),
72            );
73            let func = FnOperand::Regular(FnPtr {
74                func: Box::new(func),
75                generics: Box::new(generics),
76            });
77            s.content = RawStatement::Call(Call {
78                func,
79                args: vec![op.clone()],
80                dest: p.clone(),
81            });
82        }
83        // Transform the raw pointer aggregate to a function call
84        RawStatement::Assign(p, Rvalue::Aggregate(AggregateKind::RawPtr(ty, is_mut), ops)) => {
85            let id = BuiltinFunId::PtrFromParts(is_mut.clone());
86            let func = FunIdOrTraitMethodRef::mk_builtin(id);
87            let generics = GenericArgs::new(
88                [Region::Erased].into(),
89                [ty.clone()].into(),
90                [].into(),
91                [].into(),
92            );
93
94            let func = FnOperand::Regular(FnPtr {
95                func: Box::new(func),
96                generics: Box::new(generics),
97            });
98            s.content = RawStatement::Call(Call {
99                func,
100                args: ops.clone(),
101                dest: p.clone(),
102            });
103        }
104        _ => {}
105    }
106}
107
108pub struct Transform;
109impl LlbcPass for Transform {
110    fn transform_body(&self, ctx: &mut TransformCtx, b: &mut ExprBody) {
111        if ctx.options.no_ops_to_function_calls {
112            return;
113        }
114        b.body.visit_statements(&mut transform_st);
115    }
116}