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#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
12#[repr(u8)]
13pub enum Mutable {
14 Yes = 1,
15 No = 0,
16}
17
18#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
20pub struct ThinPtr(pub Mutable, pub Type);
21
22impl ThinPtr {
23 #[inline]
28 pub fn is_equivalent(&self, other: &ThinPtr) -> bool {
29 self.0 >= other.0 && self.1.is_equivalent(&other.1)
30 }
31
32 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#[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 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 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 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 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 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#[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 #[inline]
147 pub fn thin_ptr(mutable: Mutable, ty: Type) -> Self {
148 Self::ThinPtr(Box::new(ThinPtr(mutable, ty)))
149 }
150
151 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 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 pub fn is_int(&self) -> bool {
179 match self {
180 Type::U8 | Type::I32 => true,
181 _ => false,
182 }
183 }
184
185 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 .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}