Skip to main content

rl_lang/checker/
types.rs

1//! Helper methods on [`CheckType`] and [`ScopeItem`], plus private type-matching utilities.
2
3use crate::{
4    ast::statements::TypeAnnotation,
5    checker::structs::{CheckType, ScopeItem},
6};
7
8impl ScopeItem {
9    pub fn new(type_annotation: CheckType, is_const: bool) -> Self {
10        Self {
11            type_annotation,
12            is_const,
13        }
14    }
15}
16
17// helper functions for CheckType
18impl CheckType {
19    /// Constructs a [`CheckType::Known`] from a [`TypeAnnotation`].
20    pub fn known(ty: TypeAnnotation) -> Self {
21        CheckType::Known(ty)
22    }
23
24    /// Returns `true` if this type is [`TypeAnnotation::Null`].
25    pub fn is_null(&self) -> bool {
26        matches!(self, CheckType::Known(TypeAnnotation::Null))
27    }
28
29    /// Returns `true` if this type is [`CheckType::Unknown`].
30    pub fn is_unknown(&self) -> bool {
31        matches!(self, CheckType::Unknown)
32    }
33
34    /// Converts a mutable `Known` type to its `const` variant (e.g. `Int` -> `CInt`).
35    pub fn into_const(self) -> Self {
36        match self {
37            CheckType::Known(ty) => CheckType::Known(const_variant(ty)),
38            other => other,
39        }
40    }
41
42    /// Returns `true` if `self` is compatible with `expected`.
43    ///
44    /// Compatibility rules:
45    /// - Either side being `Unknown` always matches (avoids cascading errors)
46    /// - `Null` matches anything (represents the absence of a value)
47    /// - `Function { .. }` matches `Known(Fn)` and vice versa
48    /// - Two `Function` types match only if params and return type are identical
49    /// - Two `Known` types match if equal, or via [`null_array_elision`] or [`const_matches`]
50    pub fn matches(&self, expected: &CheckType) -> bool {
51        match (self, expected) {
52            // if any side is [`CheckType::Unknown`] returns true
53            (CheckType::Unknown, _) | (_, CheckType::Unknown) => true,
54
55            // if item type is [`TypeAnnotation::Null`] will return true
56            // to represent the absence of value
57            (CheckType::Known(TypeAnnotation::Null), _) => true,
58
59            // matches different functions types and returns true
60            (CheckType::Function { .. }, CheckType::Known(TypeAnnotation::Fn))
61            | (CheckType::Known(TypeAnnotation::Fn), CheckType::Function { .. }) => true,
62
63            // matches [`CheckType::Function`] fields values with each other
64            // returns true if all match
65            (
66                CheckType::Function {
67                    params: p1,
68                    return_type: r1,
69                },
70                CheckType::Function {
71                    params: p2,
72                    return_type: r2,
73                },
74            ) => p1 == p2 && r1 == r2,
75
76            // checks the TypeAnnotation and compare then returns [`bool`]
77            (CheckType::Known(a), CheckType::Known(b)) => {
78                a == b
79                    || null_array_elision(a, b)
80                    || null_map_elision(a, b)
81                    || const_matches(a, b)
82                    || record_matches(a, b)
83                    || enum_matches(a, b)
84                    || set_matches(a, b)
85            }
86
87            _ => false,
88        }
89    }
90
91    /// Returns a human-readable description of this type for error messages.
92    pub fn info(&self) -> String {
93        match self {
94            CheckType::Known(ty) => format!("{:?}", ty),
95            CheckType::Function {
96                params,
97                return_type,
98            } => format!(
99                "fn({}) -> {:?}",
100                params
101                    .iter()
102                    .map(|p| format!("{:?}", p))
103                    .collect::<Vec<_>>()
104                    .join(", "),
105                return_type
106            ),
107            CheckType::Unknown => "unknown".to_string(),
108        }
109    }
110}
111
112/// Returns `true` if two array types are compatible when either inner type is `Null`
113/// (i.e. an empty array `[]` is compatible with any typed array).
114fn null_array_elision(a: &TypeAnnotation, b: &TypeAnnotation) -> bool {
115    match (a, b) {
116        (
117            TypeAnnotation::Array(x) | TypeAnnotation::CArray(x),
118            TypeAnnotation::Array(y) | TypeAnnotation::CArray(y),
119        ) => **x == TypeAnnotation::Null || **y == TypeAnnotation::Null || null_array_elision(x, y),
120
121        (
122            TypeAnnotation::Tuple(a) | TypeAnnotation::CTuple(a),
123            TypeAnnotation::Tuple(b) | TypeAnnotation::CTuple(b),
124        ) => a.iter().zip(b.iter()).all(|(x, y)| {
125            *x == TypeAnnotation::Null || *y == TypeAnnotation::Null || null_array_elision(x, y)
126        }),
127        _ => false,
128    }
129}
130
131fn null_map_elision(a: &TypeAnnotation, b: &TypeAnnotation) -> bool {
132    match (a, b) {
133        (
134            TypeAnnotation::Map(ak, av) | TypeAnnotation::CMap(ak, av),
135            TypeAnnotation::Map(bk, bv) | TypeAnnotation::CMap(bk, bv),
136        ) => {
137            (**ak == TypeAnnotation::Null
138                || **bk == TypeAnnotation::Null
139                || null_array_elision(ak, bk)
140                || null_map_elision(ak, bk))
141                && (**av == TypeAnnotation::Null
142                    || **bv == TypeAnnotation::Null
143                    || null_array_elision(av, bv)
144                    || null_map_elision(av, bv))
145        }
146        _ => false,
147    }
148}
149
150/// Converts a mutable [`TypeAnnotation`] to its immutable (`C`-prefixed) variant.
151fn const_variant(ty: TypeAnnotation) -> TypeAnnotation {
152    match ty {
153        TypeAnnotation::Int => TypeAnnotation::CInt,
154        TypeAnnotation::Float => TypeAnnotation::CFloat,
155        TypeAnnotation::Bool => TypeAnnotation::CBool,
156        TypeAnnotation::String => TypeAnnotation::CString,
157        TypeAnnotation::Byte => TypeAnnotation::CByte,
158        TypeAnnotation::Char => TypeAnnotation::CChar,
159        TypeAnnotation::Array(inner) => TypeAnnotation::CArray(inner),
160        TypeAnnotation::Map(key, value) => TypeAnnotation::CMap(key, value),
161        TypeAnnotation::Tuple(inner) => TypeAnnotation::CTuple(inner),
162        TypeAnnotation::Error => TypeAnnotation::CError,
163        TypeAnnotation::Result(inner) => TypeAnnotation::CResult(inner),
164        TypeAnnotation::Record(name) => TypeAnnotation::CRecord(name),
165        TypeAnnotation::Enum(name) => TypeAnnotation::CEnum(name),
166        TypeAnnotation::Set(items) => TypeAnnotation::CSet(items),
167        other => other,
168    }
169}
170
171/// Returns `true` if `a` and `b` are the same named record, regardless of
172/// `Record`/`CRecord` (mutable/const) mismatch.
173fn record_matches(a: &TypeAnnotation, b: &TypeAnnotation) -> bool {
174    matches!(
175        (a, b),
176        (TypeAnnotation::Record(x) | TypeAnnotation::CRecord(x),
177         TypeAnnotation::Record(y) | TypeAnnotation::CRecord(y)) if x == y
178    )
179}
180
181/// Returns `true` if `a` and `b` are the same named tag (enum), regardless of
182/// `Enum`/`CEnum` (mutable/const) mismatch.
183fn enum_matches(a: &TypeAnnotation, b: &TypeAnnotation) -> bool {
184    matches!(
185        (a, b),
186        (TypeAnnotation::Enum(x) | TypeAnnotation::CEnum(x),
187         TypeAnnotation::Enum(y) | TypeAnnotation::CEnum(y)) if x == y
188    )
189}
190
191fn set_matches(a: &TypeAnnotation, b: &TypeAnnotation) -> bool {
192    matches!(
193        (a, b),
194        (TypeAnnotation::Set(x) | TypeAnnotation::CSet(x),
195         TypeAnnotation::Set(y) | TypeAnnotation::CSet(y)) if x == y
196    )
197}
198
199/// Returns `true` if `a` is the const variant of `b` or vice versa (e.g. `CInt` <-> `Int`).
200fn const_matches(a: &TypeAnnotation, b: &TypeAnnotation) -> bool {
201    matches!(
202        (a, b),
203        (TypeAnnotation::CString, TypeAnnotation::String)
204            | (TypeAnnotation::CInt, TypeAnnotation::Int)
205            | (TypeAnnotation::CFloat, TypeAnnotation::Float)
206            | (TypeAnnotation::CBool, TypeAnnotation::Bool)
207            | (TypeAnnotation::CByte, TypeAnnotation::Byte)
208            | (TypeAnnotation::CChar, TypeAnnotation::Char)
209            | (TypeAnnotation::CTuple(_), TypeAnnotation::Tuple(_))
210            | (TypeAnnotation::Tuple(_), TypeAnnotation::CTuple(_))
211            | (TypeAnnotation::CError, TypeAnnotation::Error)
212            | (TypeAnnotation::Error, TypeAnnotation::CError)
213            | (TypeAnnotation::CResult(_), TypeAnnotation::Result(_))
214            | (TypeAnnotation::Result(_), TypeAnnotation::CResult(_))
215            | (TypeAnnotation::CMap(_, _), TypeAnnotation::Map(_, _))
216            | (TypeAnnotation::Map(_, _), TypeAnnotation::CMap(_, _))
217            | (TypeAnnotation::Set(_), TypeAnnotation::CSet(_))
218            | (TypeAnnotation::CSet(_), TypeAnnotation::Set(_))
219    )
220}