lisette_semantics/checker/registration/
convert.rs1use rustc_hash::FxHashMap as HashMap;
2
3use syntax::EcoString;
4use syntax::ast::{Annotation, Generic, Span};
5use syntax::types::{SubstitutionMap, Type, substitute};
6
7use crate::checker::Checker;
8
9impl Checker<'_, '_> {
10 pub fn convert_to_type(&mut self, annotation: &Annotation, span: &Span) -> Type {
11 match annotation {
12 Annotation::Unknown => self.new_type_var(),
13
14 Annotation::Function {
15 params,
16 return_type,
17 ..
18 } => {
19 let new_params: Vec<Type> = params
20 .iter()
21 .map(|param| self.convert_to_type(param, span))
22 .collect();
23 let new_return_type = if matches!(return_type.as_ref(), Annotation::Unknown) {
26 self.type_unit()
27 } else {
28 self.convert_to_type(return_type, span)
29 };
30
31 Type::Function {
32 param_mutability: vec![false; new_params.len()],
33 params: new_params,
34 bounds: Default::default(),
35 return_type: new_return_type.into(),
36 }
37 }
38
39 Annotation::Constructor {
40 name: type_name,
41 params,
42 span: annotation_span,
43 } => {
44 if type_name == "Unit"
48 && params.is_empty()
49 && self.resolve_type_name("Unit").is_none()
50 {
51 return Type::unit();
52 }
53
54 if self.lookup_generic_index(type_name).is_some() {
55 if !params.is_empty() {
56 self.sink.push(diagnostics::infer::type_param_with_args(
57 params.len(),
58 *annotation_span,
59 ));
60 }
61 return Type::Parameter(type_name.into());
62 }
63
64 let Some((qualified_name, ty)) =
65 self.resolve_type_with_arity(type_name, params.len())
66 else {
67 if type_name == "Self" {
68 self.sink.push(diagnostics::infer::self_type_not_supported(
69 *annotation_span,
70 ));
71 } else {
72 self.sink.push(diagnostics::infer::type_not_found(
73 type_name,
74 *annotation_span,
75 ));
76 }
77 return Type::Error;
78 };
79
80 self.track_name_usage(&qualified_name, annotation_span, type_name.len() as u32);
81
82 if qualified_name == "prelude.Unknown" && self.is_lis() {
83 self.sink.push(diagnostics::infer::unknown_outside_typedef(
84 *annotation_span,
85 ));
86 }
87
88 let (generics, body) = match &ty {
89 Type::Forall { vars, body } => (vars.clone(), body.as_ref().clone()),
90 _ => (vec![], ty.clone()),
91 };
92
93 if generics.len() != params.len() {
94 let actual_types: Vec<Type> = params
95 .iter()
96 .map(|arg| self.convert_to_type(arg, span))
97 .collect();
98 let generics_as_str: Vec<String> =
99 generics.iter().map(|s| s.to_string()).collect();
100 self.sink.push(diagnostics::infer::generics_arity_mismatch(
101 &generics_as_str,
102 params,
103 &actual_types,
104 *span,
105 ));
106 }
107
108 let resolved_ty = self.instantiate_from_annotations(&generics, &body, params, span);
109
110 if self.is_lis()
112 && qualified_name == "prelude.Ref"
113 && params.len() == 1
114 && let Some(inner) = resolved_ty.inner()
115 && let Some(inner_id) = inner.resolve().get_qualified_id()
116 && self.store.get_interface(inner_id).is_some()
117 {
118 self.sink.push(diagnostics::infer::ref_of_interface_type(
119 &inner,
120 *annotation_span,
121 ));
122 }
123
124 if qualified_name == "prelude.Map"
125 && !params.is_empty()
126 && let Some(key_ty) = resolved_ty
127 .get_type_params()
128 .and_then(|p| p.first().cloned())
129 {
130 self.check_map_key_comparable(&key_ty, *annotation_span);
131 }
132
133 resolved_ty
134 }
135
136 Annotation::Tuple { elements, .. } => {
137 let element_types = elements
138 .iter()
139 .map(|e| self.convert_to_type(e, span))
140 .collect();
141 Type::Tuple(element_types)
142 }
143
144 Annotation::Opaque { .. } => {
145 unreachable!("Annotation::Opaque should not be converted to a type")
146 }
147 }
148 }
149
150 pub(super) fn resolve_type_with_arity(
151 &mut self,
152 type_name: &str,
153 expected_arity: usize,
154 ) -> Option<(String, Type)> {
155 let arity_of = |ty: &Type| match ty {
156 Type::Forall { vars, .. } => vars.len(),
157 _ => 0,
158 };
159
160 if let Some((qname, ty)) = self.resolve_type_name(type_name) {
161 if arity_of(&ty) == expected_arity {
162 return Some((qname, ty));
163 }
164 if !type_name.contains('.')
165 && let Some((pname, pty)) = self.resolve_type_from_prelude(type_name)
166 && arity_of(&pty) == expected_arity
167 {
168 return Some((pname, pty));
169 }
170 return Some((qname, ty));
171 }
172
173 self.resolve_type_from_prelude(type_name)
174 }
175
176 pub fn instantiate_from_annotations(
177 &mut self,
178 generics: &[EcoString],
179 body: &Type,
180 type_args: &[Annotation],
181 span: &Span,
182 ) -> Type {
183 let args: Vec<Type> = type_args
184 .iter()
185 .map(|arg_ann| self.convert_to_type(arg_ann, span))
186 .collect();
187
188 let map: SubstitutionMap = generics
189 .iter()
190 .zip(args.iter())
191 .map(|(name, ty)| (name.clone(), ty.clone()))
192 .collect();
193
194 substitute(body, &map)
195 }
196
197 pub(crate) fn check_undeclared_impl_type_params(
204 &mut self,
205 annotation: &Annotation,
206 generics: &[Generic],
207 ) {
208 let Annotation::Constructor {
209 name: receiver_name,
210 params,
211 ..
212 } = annotation
213 else {
214 return;
215 };
216
217 let undeclared: Vec<_> = params
218 .iter()
219 .filter_map(|param| {
220 let Annotation::Constructor {
221 name,
222 params: sub_params,
223 span: param_span,
224 } = param
225 else {
226 return None;
227 };
228
229 if sub_params.is_empty()
232 && name.len() == 1
233 && name.chars().next().is_some_and(|c| c.is_uppercase())
234 && self.lookup_generic_index(name).is_none()
235 {
236 Some((name.to_string(), *param_span))
237 } else {
238 None
239 }
240 })
241 .collect();
242
243 for (i, (name, param_span)) in undeclared.iter().enumerate() {
244 self.scopes
245 .current_mut()
246 .type_params
247 .get_or_insert_with(HashMap::default)
248 .insert(name.clone(), generics.len() + i);
249 self.sink
250 .push(diagnostics::infer::undeclared_impl_type_param(
251 name,
252 *param_span,
253 receiver_name,
254 ));
255 }
256 }
257
258 fn check_map_key_comparable(&mut self, key_ty: &Type, span: Span) {
259 let resolved = key_ty.resolve();
260
261 let reason = match &resolved {
262 Type::Function { .. } => Some("functions"),
263 _ if resolved.has_name("Slice") => Some("slices"),
264 _ if resolved.has_name("Map") => Some("maps"),
265 _ => None,
266 };
267
268 if let Some(reason) = reason {
269 self.sink.push(diagnostics::infer::non_comparable_map_key(
270 &resolved, reason, span,
271 ));
272 }
273 }
274}