lisette_semantics/checker/registration/
convert.rs1use rustc_hash::FxHashMap as HashMap;
2
3use crate::checker::EnvResolve;
4use syntax::EcoString;
5use syntax::ast::{Annotation, Generic, Span};
6use syntax::program::{Definition, DefinitionBody};
7use syntax::types::{SubstitutionMap, Type, substitute};
8
9use crate::checker::TaskState;
10use crate::store::Store;
11
12impl TaskState<'_> {
13 pub fn convert_bound_to_type(
17 &mut self,
18 store: &Store,
19 annotation: &Annotation,
20 span: &Span,
21 ) -> Type {
22 self.bound_position_depth += 1;
23 let result = self.convert_to_type(store, annotation, span);
24 self.bound_position_depth -= 1;
25 result
26 }
27
28 pub fn convert_to_type(&mut self, store: &Store, annotation: &Annotation, span: &Span) -> Type {
29 match annotation {
30 Annotation::Unknown => self.new_type_var(),
31
32 Annotation::Function {
33 params,
34 return_type,
35 ..
36 } => {
37 let new_params: Vec<Type> = params
38 .iter()
39 .map(|param| self.convert_to_type(store, param, span))
40 .collect();
41 let new_return_type = if matches!(return_type.as_ref(), Annotation::Unknown) {
44 self.type_unit()
45 } else {
46 self.convert_to_type(store, return_type, span)
47 };
48
49 Type::Function {
50 param_mutability: vec![false; new_params.len()],
51 params: new_params,
52 bounds: Default::default(),
53 return_type: new_return_type.into(),
54 }
55 }
56
57 Annotation::Constructor {
58 name: type_name,
59 params,
60 span: annotation_span,
61 } => {
62 if type_name == "Unit"
66 && params.is_empty()
67 && self.resolve_type_name(store, "Unit").is_none()
68 {
69 return Type::unit();
70 }
71
72 if self.lookup_generic_index(type_name).is_some() {
73 if !params.is_empty() {
74 self.sink.push(diagnostics::infer::type_param_with_args(
75 params.len(),
76 *annotation_span,
77 ));
78 }
79 return Type::Parameter(type_name.into());
80 }
81
82 let Some((qualified_name, ty)) =
83 self.resolve_type_with_arity(store, type_name, params.len())
84 else {
85 if type_name == "Self" {
86 let receiver = self.scopes.impl_receiver_type().map(|ty| ty.stringify());
87 self.sink.push(diagnostics::infer::self_type_not_supported(
88 *annotation_span,
89 receiver.as_deref(),
90 ));
91 } else {
92 self.sink.push(diagnostics::infer::type_not_found(
93 type_name,
94 *annotation_span,
95 ));
96 }
97 return Type::Error;
98 };
99
100 self.track_name_usage(
101 store,
102 &qualified_name,
103 annotation_span,
104 type_name.len() as u32,
105 );
106
107 if self.bound_position_depth == 0
108 && let Some(builtin) =
109 crate::checker::infer::BuiltinBound::from_qualified_id(&qualified_name)
110 {
111 self.sink
112 .push(diagnostics::infer::bound_only_in_value_position(
113 builtin.label(),
114 *annotation_span,
115 ));
116 return Type::Error;
117 }
118
119 let (generics, body) = match &ty {
120 Type::Forall { vars, body } => (vars.clone(), body.as_ref().clone()),
121 _ => (vec![], ty.clone()),
122 };
123
124 if generics.len() != params.len() {
125 let actual_types: Vec<Type> = params
126 .iter()
127 .map(|arg| self.convert_to_type(store, arg, span))
128 .collect();
129 let generics_as_str: Vec<String> =
130 generics.iter().map(|s| s.to_string()).collect();
131 self.sink.push(diagnostics::infer::generics_arity_mismatch(
132 &generics_as_str,
133 params,
134 &actual_types,
135 *span,
136 ));
137 }
138
139 let concrete_args: Vec<Type> = params
140 .iter()
141 .map(|arg| self.convert_to_type(store, arg, span))
142 .collect();
143 let map: SubstitutionMap = generics
144 .iter()
145 .cloned()
146 .zip(concrete_args.iter().cloned())
147 .collect();
148 let resolved_ty = substitute(&body, &map);
149
150 if self.is_lis(store)
152 && qualified_name == "prelude.Ref"
153 && params.len() == 1
154 && let Some(inner) = resolved_ty.inner()
155 {
156 let peeled_inner = store.peel_alias(&inner.resolve_in(&self.env));
157 if let Some(inner_id) = peeled_inner.get_qualified_id()
158 && store.get_interface(inner_id).is_some()
159 {
160 self.sink.push(diagnostics::infer::ref_of_interface_type(
161 &inner,
162 *annotation_span,
163 ));
164 }
165 }
166
167 if qualified_name == "prelude.Map"
168 && !params.is_empty()
169 && let Some(key_ty) = resolved_ty
170 .get_type_params()
171 .and_then(|p| p.first().cloned())
172 {
173 self.check_map_key_comparable(store, &key_ty, *annotation_span);
174 }
175
176 let body_differs = match &resolved_ty {
179 Type::Nominal { id, .. } => id.as_str() != qualified_name.as_str(),
180 Type::Simple(_) | Type::Compound { .. } => true,
181 _ => false,
182 };
183 if body_differs
184 && let Some(Definition {
185 body:
186 DefinitionBody::TypeAlias {
187 annotation: alias_ann,
188 ..
189 },
190 ..
191 }) = store.get_definition(&qualified_name)
192 && !alias_ann.is_opaque()
193 {
194 return Type::Nominal {
195 id: qualified_name.into(),
196 params: concrete_args,
197 underlying_ty: Some(Box::new(resolved_ty)),
198 };
199 }
200
201 resolved_ty
202 }
203
204 Annotation::Tuple { elements, .. } => {
205 let element_types = elements
206 .iter()
207 .map(|e| self.convert_to_type(store, e, span))
208 .collect();
209 Type::Tuple(element_types)
210 }
211
212 Annotation::Opaque { .. } => {
213 unreachable!("Annotation::Opaque should not be converted to a type")
214 }
215 }
216 }
217
218 pub(super) fn resolve_type_with_arity(
219 &mut self,
220 store: &Store,
221 type_name: &str,
222 expected_arity: usize,
223 ) -> Option<(String, Type)> {
224 let arity_of = |ty: &Type| match ty {
225 Type::Forall { vars, .. } => vars.len(),
226 _ => 0,
227 };
228
229 if let Some((qname, ty)) = self.resolve_type_name(store, type_name) {
230 if arity_of(&ty) == expected_arity {
231 return Some((qname, ty));
232 }
233 if !type_name.contains('.')
234 && let Some((pname, pty)) = self.resolve_type_from_prelude(store, type_name)
235 && arity_of(&pty) == expected_arity
236 {
237 return Some((pname, pty));
238 }
239 return Some((qname, ty));
240 }
241
242 self.resolve_type_from_prelude(store, type_name)
243 }
244
245 pub fn instantiate_from_annotations(
246 &mut self,
247 store: &Store,
248 generics: &[EcoString],
249 body: &Type,
250 type_args: &[Annotation],
251 span: &Span,
252 ) -> Type {
253 let args: Vec<Type> = type_args
254 .iter()
255 .map(|arg_ann| self.convert_to_type(store, arg_ann, span))
256 .collect();
257
258 let map: SubstitutionMap = generics
259 .iter()
260 .zip(args.iter())
261 .map(|(name, ty)| (name.clone(), ty.clone()))
262 .collect();
263
264 substitute(body, &map)
265 }
266
267 pub(crate) fn check_undeclared_impl_type_params(
274 &mut self,
275 annotation: &Annotation,
276 generics: &[Generic],
277 ) {
278 let Annotation::Constructor {
279 name: receiver_name,
280 params,
281 ..
282 } = annotation
283 else {
284 return;
285 };
286
287 let undeclared: Vec<_> = params
288 .iter()
289 .filter_map(|param| {
290 let Annotation::Constructor {
291 name,
292 params: sub_params,
293 span: param_span,
294 } = param
295 else {
296 return None;
297 };
298
299 if sub_params.is_empty()
302 && name.len() == 1
303 && name.chars().next().is_some_and(|c| c.is_uppercase())
304 && self.lookup_generic_index(name).is_none()
305 {
306 Some((name.to_string(), *param_span))
307 } else {
308 None
309 }
310 })
311 .collect();
312
313 for (i, (name, param_span)) in undeclared.iter().enumerate() {
314 self.scopes
315 .current_mut()
316 .type_params
317 .get_or_insert_with(HashMap::default)
318 .insert(name.clone(), generics.len() + i);
319 self.sink
320 .push(diagnostics::infer::undeclared_impl_type_param(
321 name,
322 *param_span,
323 receiver_name,
324 ));
325 }
326 }
327
328 fn check_map_key_comparable(&mut self, store: &Store, key_ty: &Type, span: Span) {
329 let resolved = key_ty.resolve_in(&self.env);
330
331 if self.is_lis(store) && resolved.resolves_to_unknown() {
332 self.sink.push(diagnostics::infer::unknown_as_map_key(span));
333 return;
334 }
335
336 let reason = if matches!(&resolved, Type::Function { .. }) {
337 "functions"
338 } else if resolved.has_name("Slice") {
339 "slices"
340 } else if resolved.has_name("Map") {
341 "maps"
342 } else {
343 return;
344 };
345
346 self.sink.push(diagnostics::infer::non_comparable_map_key(
347 &resolved, reason, span,
348 ));
349 }
350}