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, unqualified_name};
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 let param_mutability = vec![false; new_params.len()];
50 Type::function(
51 new_params,
52 param_mutability,
53 Default::default(),
54 new_return_type.into(),
55 )
56 }
57
58 Annotation::Constructor {
59 name: type_name,
60 params,
61 span: annotation_span,
62 } => {
63 if type_name == "Unit"
67 && params.is_empty()
68 && self.resolve_type_name(store, "Unit").is_none()
69 {
70 return Type::unit();
71 }
72
73 if self.lookup_generic_index(type_name).is_some() {
74 if !params.is_empty() {
75 self.sink.push(diagnostics::infer::type_param_with_args(
76 params.len(),
77 *annotation_span,
78 ));
79 }
80 return Type::Parameter(type_name.into());
81 }
82
83 let Some((qualified_name, ty)) =
84 self.resolve_type_with_arity(store, type_name, params.len())
85 else {
86 if type_name == "Self" {
87 let receiver = self.scopes.impl_receiver_type().map(|ty| ty.stringify());
88 self.sink.push(diagnostics::infer::self_type_not_supported(
89 *annotation_span,
90 receiver.as_deref(),
91 ));
92 } else {
93 self.sink.push(diagnostics::infer::type_not_found(
94 type_name,
95 *annotation_span,
96 ));
97 }
98 return Type::Error;
99 };
100
101 if let Some((kind, help)) =
102 self.classify_non_type_name(store, &qualified_name, type_name)
103 {
104 self.sink.push(diagnostics::infer::value_in_type_position(
105 type_name,
106 kind,
107 *annotation_span,
108 help,
109 ));
110 return Type::Error;
111 }
112
113 self.track_name_usage(
114 store,
115 &qualified_name,
116 annotation_span,
117 type_name.len() as u32,
118 );
119
120 if self.bound_position_depth == 0
121 && let Some(builtin) =
122 crate::checker::infer::BuiltinBound::from_qualified_id(&qualified_name)
123 {
124 self.sink
125 .push(diagnostics::infer::bound_only_in_value_position(
126 builtin.label(),
127 *annotation_span,
128 ));
129 return Type::Error;
130 }
131
132 let (generics, body) = match &ty {
133 Type::Forall { vars, body } => (vars.clone(), body.as_ref().clone()),
134 _ => (vec![], ty.clone()),
135 };
136
137 if generics.len() != params.len() {
138 let actual_types: Vec<Type> = params
139 .iter()
140 .map(|arg| self.convert_to_type(store, arg, span))
141 .collect();
142 let generics_as_str: Vec<String> =
143 generics.iter().map(|s| s.to_string()).collect();
144 self.sink.push(diagnostics::infer::generics_arity_mismatch(
145 &generics_as_str,
146 params,
147 &actual_types,
148 *span,
149 ));
150 }
151
152 let concrete_args: Vec<Type> = params
153 .iter()
154 .map(|arg| self.convert_to_type(store, arg, span))
155 .collect();
156 let map: SubstitutionMap = generics
157 .iter()
158 .cloned()
159 .zip(concrete_args.iter().cloned())
160 .collect();
161 let resolved_ty = substitute(&body, &map);
162
163 if self.is_lis(store)
165 && qualified_name == "prelude.Ref"
166 && params.len() == 1
167 && let Some(inner) = resolved_ty.inner()
168 {
169 let peeled_inner = store.peel_alias(&inner.resolve_in(&self.env));
170 if let Some(inner_id) = peeled_inner.get_qualified_id()
171 && store.get_interface(inner_id).is_some()
172 {
173 self.sink.push(diagnostics::infer::ref_of_interface_type(
174 &inner,
175 *annotation_span,
176 ));
177 }
178 }
179
180 if qualified_name == "prelude.Map"
181 && !params.is_empty()
182 && let Some(key_ty) = resolved_ty
183 .get_type_params()
184 .and_then(|p| p.first().cloned())
185 {
186 self.check_map_key_comparable(store, &key_ty, *annotation_span);
187 }
188
189 let body_differs = match &resolved_ty {
192 Type::Nominal { id, .. } => id.as_str() != qualified_name.as_str(),
193 Type::Simple(_) | Type::Compound { .. } => true,
194 _ => false,
195 };
196 if body_differs
197 && let Some(Definition {
198 body:
199 DefinitionBody::TypeAlias {
200 annotation: alias_ann,
201 ..
202 },
203 ..
204 }) = store.get_definition(&qualified_name)
205 && !alias_ann.is_opaque()
206 {
207 return Type::Nominal {
208 id: qualified_name.into(),
209 params: concrete_args,
210 underlying_ty: Some(Box::new(resolved_ty)),
211 };
212 }
213
214 resolved_ty
215 }
216
217 Annotation::Tuple { elements, .. } => {
218 let element_types = elements
219 .iter()
220 .map(|e| self.convert_to_type(store, e, span))
221 .collect();
222 Type::Tuple(element_types)
223 }
224
225 Annotation::Opaque { .. } => {
226 unreachable!("Annotation::Opaque should not be converted to a type")
227 }
228 }
229 }
230
231 pub(super) fn classify_non_type_name(
232 &self,
233 store: &Store,
234 qualified_name: &str,
235 type_name: &str,
236 ) -> Option<(&'static str, Option<String>)> {
237 let definition = store.get_definition(qualified_name)?;
238 if !matches!(definition.body, DefinitionBody::Value { .. }) {
239 return None;
240 }
241 let body = definition.ty.unwrap_forall();
242
243 if body
244 .get_qualified_id()
245 .is_some_and(|id| id == qualified_name)
246 {
247 return None;
248 }
249
250 let is_function = matches!(body, Type::Function(_));
251 let enum_id = match body {
252 Type::Function(f) => f.return_type.get_qualified_id(),
253 other => other.get_qualified_id(),
254 };
255 let variant_name = unqualified_name(qualified_name);
256 let parent_enum = enum_id.filter(|id| {
257 store.get_definition(id).is_some_and(|d| match &d.body {
258 DefinitionBody::Enum { variants, .. } => {
259 variants.iter().any(|v| v.name == variant_name)
260 }
261 DefinitionBody::ValueEnum { variants, .. } => {
262 variants.iter().any(|v| v.name == variant_name)
263 }
264 _ => false,
265 })
266 });
267
268 if let Some(enum_id) = parent_enum {
269 let enum_name = unqualified_name(enum_id);
270 let help = if is_function {
271 format!(
272 "Use `{}` for the enum type, or call `{}(...)` to construct a value",
273 enum_name, type_name
274 )
275 } else {
276 format!("Use `{}` for the enum type", enum_name)
277 };
278 return Some(("enum variant", Some(help)));
279 }
280
281 if is_function {
282 return Some((
283 "function",
284 Some("Use a function type alias or write the function type directly".to_string()),
285 ));
286 }
287
288 Some(("value", Some("Only a type is allowed here".to_string())))
289 }
290
291 pub(super) fn resolve_type_with_arity(
292 &mut self,
293 store: &Store,
294 type_name: &str,
295 expected_arity: usize,
296 ) -> Option<(String, Type)> {
297 let arity_of = |ty: &Type| match ty {
298 Type::Forall { vars, .. } => vars.len(),
299 _ => 0,
300 };
301
302 if let Some((qname, ty)) = self.resolve_type_name(store, type_name) {
303 if arity_of(&ty) == expected_arity {
304 return Some((qname, ty));
305 }
306 if !type_name.contains('.')
307 && let Some((pname, pty)) = self.resolve_type_from_prelude(store, type_name)
308 && arity_of(&pty) == expected_arity
309 {
310 return Some((pname, pty));
311 }
312 return Some((qname, ty));
313 }
314
315 self.resolve_type_from_prelude(store, type_name)
316 }
317
318 pub fn instantiate_from_annotations(
319 &mut self,
320 store: &Store,
321 generics: &[EcoString],
322 body: &Type,
323 type_args: &[Annotation],
324 span: &Span,
325 ) -> Type {
326 let args: Vec<Type> = type_args
327 .iter()
328 .map(|arg_ann| self.convert_to_type(store, arg_ann, span))
329 .collect();
330
331 let map: SubstitutionMap = generics
332 .iter()
333 .zip(args.iter())
334 .map(|(name, ty)| (name.clone(), ty.clone()))
335 .collect();
336
337 substitute(body, &map)
338 }
339
340 pub(crate) fn check_undeclared_impl_type_params(
347 &mut self,
348 annotation: &Annotation,
349 generics: &[Generic],
350 ) {
351 let Annotation::Constructor {
352 name: receiver_name,
353 params,
354 ..
355 } = annotation
356 else {
357 return;
358 };
359
360 let undeclared: Vec<_> = params
361 .iter()
362 .filter_map(|param| {
363 let Annotation::Constructor {
364 name,
365 params: sub_params,
366 span: param_span,
367 } = param
368 else {
369 return None;
370 };
371
372 if sub_params.is_empty()
375 && name.len() == 1
376 && name.chars().next().is_some_and(|c| c.is_uppercase())
377 && self.lookup_generic_index(name).is_none()
378 {
379 Some((name.to_string(), *param_span))
380 } else {
381 None
382 }
383 })
384 .collect();
385
386 for (i, (name, param_span)) in undeclared.iter().enumerate() {
387 self.scopes
388 .current_mut()
389 .type_params
390 .get_or_insert_with(HashMap::default)
391 .insert(name.clone(), generics.len() + i);
392 self.sink
393 .push(diagnostics::infer::undeclared_impl_type_param(
394 name,
395 *param_span,
396 receiver_name,
397 ));
398 }
399 }
400
401 fn check_map_key_comparable(&mut self, store: &Store, key_ty: &Type, span: Span) {
402 let resolved = key_ty.resolve_in(&self.env);
403
404 if self.is_lis(store) && resolved.resolves_to_unknown() {
405 self.sink.push(diagnostics::infer::unknown_as_map_key(span));
406 return;
407 }
408
409 let reason = if matches!(&resolved, Type::Function(_)) {
410 "functions"
411 } else if resolved.has_name("Slice") {
412 "slices"
413 } else if resolved.has_name("Map") {
414 "maps"
415 } else {
416 return;
417 };
418
419 self.sink.push(diagnostics::infer::non_comparable_map_key(
420 &resolved, reason, span,
421 ));
422 }
423}