1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/// The type of a class object
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub enum Type {
    /// Type which matches with every other type
    /// Objects of type any should not exist.
    Any,
    /// The null type, implicitly returned by functions
    /// which don't specify a return type, also the
    /// value that statements return (since everything
    /// is an expression)
    Null,
    /// Marks a value that cannot be constructed, for example
    /// The return value of `loop {}`
    Never,
    /// Compile time known 32-bit signed integer
    ComptimeInt,
    /// 32-bit signed integer known at runtime
    DynamicInt,
    /// A boolean value known at compile time
    ComptimeBool,
    /// Runtime boolean
    DynamicBool,
    /// A compile time known string
    String,
    /// A compile time known format string
    FormatString,
    /// Any function, native or api
    Function,
    /// A reference to a compiled function
    /// A compiled function is uniquely identified by its block id
    /// Basically a function after monomorphization
    FunctionRef,
    /// The type of a type
    Type,
    /// Module type
    Module,
    /// Type of a struct definition
    Struct,
    /// An instantiated struct
    StructObject,
    /// Type of a tuple
    /// TODO: Could a [`Type::Tuple`] not just be a [`Type::TupleObject`] with types as elements?
    Tuple,
    /// An instantiated tuple
    TupleObject,
}

impl Type {
    pub fn is_int(&self) -> bool {
        matches!(self, Type::DynamicInt | Type::ComptimeInt)
    }

    pub fn is_never(&self) -> bool {
        matches!(self, Type::Never)
    }

    pub fn diverges(&self) -> bool {
        self.is_never()
    }

    /// Returns whether this type can be completely encoded at runtime
    pub fn runtime_encodable(&self) -> bool {
        matches!(
            self,
            Type::DynamicBool
                | Type::DynamicInt
                | Type::Null
                | Type::Never
                | Type::StructObject
                | Type::TupleObject
        )
    }

    /// Returns whether this type can be encoded at compile time.
    pub fn comptime_encodable(&self) -> bool {
        !self.runtime_encodable()
    }

    /// Returns whether `other` matches the pattern of `self`
    #[allow(clippy::match_same_arms)]
    pub fn matches(&self, other: Type) -> bool {
        match (self, other) {
            // The never type matches always
            (Type::Never, _) | (_, Type::Never) => true,
            // `Any` matches every other type only as a pattern
            (Type::Any, _) => true,
            (a, b) => *a == b,
        }
    }

    /// Returns whether this type should be treated as a reference.
    /// This effects e.g. whether variables get copied when assigned or passed to functions
    /// TODO: implement some proper reference object, to make this less implicit and confusing.
    /// Right now, only structs are treated as references
    pub fn is_reference(&self) -> bool {
        matches!(self, Type::StructObject)
    }
}

/// Error messages can safely use the debug impl for display
impl std::fmt::Display for Type {
    #[allow(clippy::use_debug)]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{self:?}")
    }
}