ampc/
types.rs

1use crate::{
2    codemap::{Span, Spanned},
3    diag::SemaDiagnostics,
4    sema::{scope::Scope, IntermediateExpr, Unit},
5    syntax::ast,
6    value::Value,
7    Context,
8};
9
10/// The mutability of a pointer.
11#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
12#[repr(u8)]
13pub enum Mutable {
14    Yes = 1,
15    No = 0,
16}
17
18/// A thin pointer [Type] (a pointer with no runtime size information).
19#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
20pub struct ThinPtr(pub Mutable, pub Type);
21
22impl ThinPtr {
23    /// Returns `true` if this [ThinPtr] type is equivalent to the provided [ThinPtr] type.
24    ///
25    /// Argument order matters, for example, `&mut T` is equivalent to `&T`, but `&T` is not
26    /// equivalent to `&mut T`.
27    #[inline]
28    pub fn is_equivalent(&self, other: &ThinPtr) -> bool {
29        self.0 >= other.0 && self.1.is_equivalent(&other.1)
30    }
31
32    /// Returns the name of the [ThinPtr] type.
33    pub fn name(&self) -> String {
34        format!(
35            "&{}{}",
36            if self.0 == Mutable::Yes { "mut " } else { "" },
37            self.1.name()
38        )
39    }
40}
41
42/// The signature/type of a function.
43#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
44pub struct FuncSig {
45    pub params: Vec<Type>,
46    pub returns: Type,
47}
48
49impl FuncSig {
50    /// Returns `true` if this [FuncSig] type is equivalent to the provided [FuncSig] type.
51    ///
52    /// Argument order matters, for example,  `func(T, ...)` is equivalent to `func(T)`, but not
53    /// the other way around.
54    pub fn is_equivalent(&self, other: &FuncSig) -> bool {
55        if other.params.len() != self.params.len() {
56            false
57        } else {
58            self.params
59                .iter()
60                .zip(other.params.iter())
61                .all(|(left, right)| left == right)
62                && self.returns.is_equivalent(&other.returns)
63        }
64    }
65
66    /// Returns the name of this type.
67    pub fn name(&self) -> String {
68        format!(
69            "func({}): {}",
70            self.params
71                .iter()
72                .map(|item| item.name())
73                .collect::<Vec<_>>()
74                .join(", "),
75            self.returns.name()
76        )
77    }
78
79    /// Reports a diagnostic if the provided type cannot be used as a function argument.
80    fn check_arg_validity(cx: &mut Context, span: Span, arg: Type) -> Result<Type, ()> {
81        match arg {
82            Type::Type => {
83                cx.cannot_use_type_as_argument(span);
84                Err(())
85            }
86            arg => Ok(arg),
87        }
88    }
89
90    /// Reports a diagnostic if the provided type cannot be used as a return type for a function.
91    fn check_return_validity(cx: &mut Context, span: Span, arg: Type) -> Result<Type, ()> {
92        match arg {
93            Type::Type => {
94                cx.cannot_use_type_as_return(span);
95                Err(())
96            }
97            arg => Ok(arg),
98        }
99    }
100
101    /// Attempts to get the signature of a function from an AST expression.
102    pub fn from_ast(
103        cx: &mut Context,
104        unit: &mut Unit,
105        scope: &Scope,
106        expr: &ast::Func,
107    ) -> Result<Self, ()> {
108        let mut params = Vec::new();
109
110        for arg in &expr.args.items {
111            params.push(match arg {
112                ast::FuncParam::Anon(arg) => {
113                    let ty = Type::from_ast(cx, unit, scope, &arg)?;
114                    Self::check_arg_validity(cx, arg.span(), ty)?
115                }
116                ast::FuncParam::Named(ast::NamedParam { ty, .. }) => {
117                    let ty = Type::from_ast(cx, unit, scope, &ty.ty)?;
118                    Self::check_arg_validity(cx, arg.span(), ty)?
119                }
120            });
121        }
122
123        Ok(Self {
124            params,
125            returns: {
126                let ty = Type::from_ast(cx, unit, scope, &expr.returns.ty)?;
127                Self::check_return_validity(cx, expr.returns.ty.span(), ty)?
128            },
129        })
130    }
131}
132
133/// The type of a value in Amp.
134#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
135pub enum Type {
136    Type,
137    U8,
138    I32,
139    ThinPtr(Box<ThinPtr>),
140    Func(Box<FuncSig>),
141}
142
143impl Type {
144    /// Creates a new [Type::ThinPtr] with the provided configuration.  Assumes that the provided
145    /// type is sized.
146    #[inline]
147    pub fn thin_ptr(mutable: Mutable, ty: Type) -> Self {
148        Self::ThinPtr(Box::new(ThinPtr(mutable, ty)))
149    }
150
151    /// Returns the name of this [Type].
152    pub fn name(&self) -> String {
153        match self {
154            Self::Type => "type".to_string(),
155            Self::U8 => "u8".to_string(),
156            Self::I32 => "i32".to_string(),
157            Self::ThinPtr(ty) => ty.name(),
158            Self::Func(ty) => ty.name(),
159        }
160    }
161
162    /// Returns `true` if this type is equivalent to another type.
163    ///
164    /// Argument order matters, for example, `&mut T` is equivalent to `&T`, but `&T` is not
165    /// equivalent to `&mut T`.
166    pub fn is_equivalent(&self, other: &Type) -> bool {
167        match (self, other) {
168            (Type::Type, Type::Type) => true,
169            (Type::U8, Type::U8) => true,
170            (Type::I32, Type::I32) => true,
171            (Type::ThinPtr(left), Type::ThinPtr(right)) => left.is_equivalent(right),
172            (Type::Func(left), Type::Func(right)) => left.is_equivalent(right),
173            _ => false,
174        }
175    }
176
177    /// Returns `true` if this type is an integer type.
178    pub fn is_int(&self) -> bool {
179        match self {
180            Type::U8 | Type::I32 => true,
181            _ => false,
182        }
183    }
184
185    /// Attempts to resolve a constant type value from the provided expression.
186    pub fn from_ast(
187        cx: &mut Context,
188        unit: &mut Unit,
189        scope: &Scope,
190        expr: &ast::Expr,
191    ) -> Result<Self, ()> {
192        let Value::Type(final_ty) = Value::eval({
193            let value = IntermediateExpr::verify(cx, unit, scope, expr)?;
194
195            value.clone()
196            // verify that the value is a type
197            .coerce(&Type::Type)
198            .ok_or_else(|| cx.invalid_type_in_type_position(
199                &value.default_type().expect("must have a type").name(),
200                expr.span()
201            ))?
202        })
203        .expect("constant evaluation cannot fail as types are always constant")
204        else {
205            unreachable!("value should be of type `type` as verified above")
206        };
207
208        Ok(final_ty)
209    }
210}