rukalang/elab/
elaborate_intrinsics.rs

1use super::prelude::*;
2
3impl Elaborator {
4    /// Elaborate one intrinsic call expression and return its resulting type.
5    pub(super) fn elaborate_intrinsic_call_expr(
6        &mut self,
7        name: &str,
8        args: &mut [CallArg],
9        locals: &mut Vec<(String, Ty)>,
10    ) -> Result<Ty, ElabError> {
11        let intrinsic = IntrinsicId::parse(name);
12        if let Some(signature) = intrinsic.signature()
13            && (args.len() != signature.type_args + signature.value_args)
14        {
15            return Err(ElabError::ArityMismatch {
16                function: format!("@{}", intrinsic.name()),
17                expected: signature.type_args + signature.value_args,
18                actual: args.len(),
19            });
20        }
21
22        match intrinsic {
23            IntrinsicId::Box => {
24                let CallArg::Expr(value) = &mut args[0] else {
25                    return Err(ElabError::UnsupportedTypeExpr);
26                };
27                let inner_ty = self.elaborate_expr(value, None, locals)?;
28                Ok(Ty::Pointer(Box::new(inner_ty)))
29            }
30            IntrinsicId::Array => {
31                let CallArg::Expr(init_expr) = &mut args[0] else {
32                    return Err(ElabError::UnsupportedTypeExpr);
33                };
34                let item_ty = self.elaborate_expr(init_expr, None, locals)?;
35                let CallArg::Expr(len_expr) = &mut args[1] else {
36                    return Err(ElabError::UnsupportedTypeExpr);
37                };
38                let len_ty = self.elaborate_expr(len_expr, None, locals)?;
39                if !len_ty.is_integer() {
40                    return Err(ElabError::TypeMismatch {
41                        expected: Ty::I64,
42                        actual: len_ty,
43                    });
44                }
45                Ok(Ty::Slice(Box::new(item_ty)))
46            }
47            IntrinsicId::As
48            | IntrinsicId::IntCast
49            | IntrinsicId::IntToFloat
50            | IntrinsicId::Trunc => {
51                let CallArg::Type(target_ty_expr) = &args[0] else {
52                    return Err(ElabError::UnsupportedTypeExpr);
53                };
54                let target_ty = self.resolve_type_expr(
55                    target_ty_expr,
56                    &BTreeMap::new(),
57                    &BTreeSet::new(),
58                    None,
59                )?;
60                let CallArg::Expr(value_expr) = &mut args[1] else {
61                    return Err(ElabError::UnsupportedTypeExpr);
62                };
63                let source_ty = self.elaborate_expr(value_expr, None, locals)?;
64                match intrinsic {
65                    IntrinsicId::As => {
66                        if !can_safely_coerce_numeric(&source_ty, &target_ty) {
67                            return Err(ElabError::TypeMismatch {
68                                expected: target_ty,
69                                actual: source_ty,
70                            });
71                        }
72                    }
73                    IntrinsicId::IntCast => {
74                        if !(source_ty.is_integer() && target_ty.is_integer()) {
75                            return Err(ElabError::TypeMismatch {
76                                expected: target_ty,
77                                actual: source_ty,
78                            });
79                        }
80                    }
81                    IntrinsicId::IntToFloat => {
82                        if !(source_ty.is_integer() && target_ty.is_float()) {
83                            return Err(ElabError::TypeMismatch {
84                                expected: target_ty,
85                                actual: source_ty,
86                            });
87                        }
88                    }
89                    IntrinsicId::Trunc => {
90                        if !is_truncation_cast(&source_ty, &target_ty) {
91                            return Err(ElabError::TypeMismatch {
92                                expected: target_ty,
93                                actual: source_ty,
94                            });
95                        }
96                    }
97                    _ => unreachable!("non-cast intrinsic matched cast branch"),
98                }
99                Ok(target_ty)
100            }
101            IntrinsicId::Unknown(name) => Err(ElabError::UnknownFunction {
102                name: format!("@{name}"),
103            }),
104        }
105    }
106}