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 _ => false,
262 })
263 });
264
265 if let Some(enum_id) = parent_enum {
266 let enum_name = unqualified_name(enum_id);
267 let help = if is_function {
268 format!(
269 "Use `{}` for the enum type, or call `{}(...)` to construct a value",
270 enum_name, type_name
271 )
272 } else {
273 format!("Use `{}` for the enum type", enum_name)
274 };
275 return Some(("enum variant", Some(help)));
276 }
277
278 if is_function {
279 return Some((
280 "function",
281 Some("Use a function type alias or write the function type directly".to_string()),
282 ));
283 }
284
285 Some(("value", Some("Only a type is allowed here".to_string())))
286 }
287
288 pub(super) fn resolve_type_with_arity(
289 &mut self,
290 store: &Store,
291 type_name: &str,
292 expected_arity: usize,
293 ) -> Option<(String, Type)> {
294 let arity_of = |ty: &Type| match ty {
295 Type::Forall { vars, .. } => vars.len(),
296 _ => 0,
297 };
298
299 if let Some((qname, ty)) = self.resolve_type_name(store, type_name) {
300 if arity_of(&ty) == expected_arity {
301 return Some((qname, ty));
302 }
303 if !type_name.contains('.')
304 && let Some((pname, pty)) = self.resolve_type_from_prelude(store, type_name)
305 && arity_of(&pty) == expected_arity
306 {
307 return Some((pname, pty));
308 }
309 return Some((qname, ty));
310 }
311
312 self.resolve_type_from_prelude(store, type_name)
313 }
314
315 pub fn instantiate_from_annotations(
316 &mut self,
317 store: &Store,
318 generics: &[EcoString],
319 body: &Type,
320 type_args: &[Annotation],
321 span: &Span,
322 ) -> Type {
323 let args: Vec<Type> = type_args
324 .iter()
325 .map(|arg_ann| self.convert_to_type(store, arg_ann, span))
326 .collect();
327
328 let map: SubstitutionMap = generics
329 .iter()
330 .zip(args.iter())
331 .map(|(name, ty)| (name.clone(), ty.clone()))
332 .collect();
333
334 substitute(body, &map)
335 }
336
337 pub(crate) fn check_undeclared_impl_type_params(
344 &mut self,
345 annotation: &Annotation,
346 generics: &[Generic],
347 ) {
348 let Annotation::Constructor {
349 name: receiver_name,
350 params,
351 ..
352 } = annotation
353 else {
354 return;
355 };
356
357 let undeclared: Vec<_> = params
358 .iter()
359 .filter_map(|param| {
360 let Annotation::Constructor {
361 name,
362 params: sub_params,
363 span: param_span,
364 } = param
365 else {
366 return None;
367 };
368
369 if sub_params.is_empty()
372 && name.len() == 1
373 && name.chars().next().is_some_and(|c| c.is_uppercase())
374 && self.lookup_generic_index(name).is_none()
375 {
376 Some((name.to_string(), *param_span))
377 } else {
378 None
379 }
380 })
381 .collect();
382
383 for (i, (name, param_span)) in undeclared.iter().enumerate() {
384 self.scopes
385 .current_mut()
386 .type_params
387 .get_or_insert_with(HashMap::default)
388 .insert(name.clone(), generics.len() + i);
389 self.sink
390 .push(diagnostics::infer::undeclared_impl_type_param(
391 name,
392 *param_span,
393 receiver_name,
394 ));
395 }
396 }
397
398 fn check_map_key_comparable(&mut self, store: &Store, key_ty: &Type, span: Span) {
399 let resolved = key_ty.resolve_in(&self.env);
400
401 if self.is_lis(store) && resolved.resolves_to_unknown() {
402 self.sink.push(diagnostics::infer::unknown_as_map_key(span));
403 return;
404 }
405
406 let reason = if matches!(&resolved, Type::Function(_)) {
407 "functions"
408 } else if resolved.has_name("Slice") {
409 "slices"
410 } else if resolved.has_name("Map") {
411 "maps"
412 } else {
413 return;
414 };
415
416 self.sink.push(diagnostics::infer::non_comparable_map_key(
417 &resolved, reason, span,
418 ));
419 }
420}