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, *body),
134 other => (vec![], other),
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 resolved_ty = if generics.is_empty() && concrete_args.is_empty() {
157 body
158 } else {
159 let map: SubstitutionMap = generics
160 .iter()
161 .cloned()
162 .zip(concrete_args.iter().cloned())
163 .collect();
164 substitute(&body, &map)
165 };
166
167 if self.is_lis(store)
169 && qualified_name == "prelude.Ref"
170 && params.len() == 1
171 && let Some(inner) = resolved_ty.inner()
172 {
173 let peeled_inner = store.peel_alias(&inner.resolve_in(&self.env));
174 if let Some(inner_id) = peeled_inner.get_qualified_id()
175 && store.get_interface(inner_id).is_some()
176 {
177 self.sink.push(diagnostics::infer::ref_of_interface_type(
178 &inner,
179 *annotation_span,
180 ));
181 }
182 }
183
184 if qualified_name == "prelude.Map"
185 && !params.is_empty()
186 && let Some(key_ty) = resolved_ty
187 .get_type_params()
188 .and_then(|p| p.first().cloned())
189 {
190 self.check_map_key_comparable(store, &key_ty, *annotation_span);
191 }
192
193 let body_differs = match &resolved_ty {
196 Type::Nominal { id, .. } => id.as_str() != qualified_name.as_str(),
197 Type::Simple(_) | Type::Compound { .. } => true,
198 _ => false,
199 };
200 if body_differs
201 && let Some(Definition {
202 body:
203 DefinitionBody::TypeAlias {
204 annotation: alias_ann,
205 ..
206 },
207 ..
208 }) = store.get_definition(&qualified_name)
209 && !alias_ann.is_opaque()
210 {
211 return Type::Nominal {
212 id: qualified_name.into(),
213 params: concrete_args,
214 underlying_ty: Some(Box::new(resolved_ty)),
215 };
216 }
217
218 resolved_ty
219 }
220
221 Annotation::Tuple { elements, .. } => {
222 let element_types = elements
223 .iter()
224 .map(|e| self.convert_to_type(store, e, span))
225 .collect();
226 Type::Tuple(element_types)
227 }
228
229 Annotation::Opaque { .. } => {
230 unreachable!("Annotation::Opaque should not be converted to a type")
231 }
232 }
233 }
234
235 pub(super) fn classify_non_type_name(
236 &self,
237 store: &Store,
238 qualified_name: &str,
239 type_name: &str,
240 ) -> Option<(&'static str, Option<String>)> {
241 let definition = store.get_definition(qualified_name)?;
242 if !definition.is_value(qualified_name) {
243 return None;
244 }
245 let body = definition.ty.unwrap_forall();
246
247 let is_function = matches!(body, Type::Function(_));
248 let enum_id = match body {
249 Type::Function(f) => f.return_type.get_qualified_id(),
250 other => other.get_qualified_id(),
251 };
252 let variant_name = unqualified_name(qualified_name);
253 let parent_enum = enum_id.filter(|id| {
254 store.get_definition(id).is_some_and(|d| match &d.body {
255 DefinitionBody::Enum { variants, .. } => {
256 variants.iter().any(|v| v.name == variant_name)
257 }
258 _ => false,
259 })
260 });
261
262 if let Some(enum_id) = parent_enum {
263 let enum_name = unqualified_name(enum_id);
264 let help = if is_function {
265 format!(
266 "Use `{}` for the enum type, or call `{}(...)` to construct a value",
267 enum_name, type_name
268 )
269 } else {
270 format!("Use `{}` for the enum type", enum_name)
271 };
272 return Some(("enum variant", Some(help)));
273 }
274
275 if is_function {
276 return Some((
277 "function",
278 Some("Use a function type alias or write the function type directly".to_string()),
279 ));
280 }
281
282 Some(("value", Some("Only a type is allowed here".to_string())))
283 }
284
285 pub(super) fn resolve_type_with_arity(
286 &mut self,
287 store: &Store,
288 type_name: &str,
289 expected_arity: usize,
290 ) -> Option<(String, Type)> {
291 let arity_of = |ty: &Type| match ty {
292 Type::Forall { vars, .. } => vars.len(),
293 _ => 0,
294 };
295
296 if let Some((qname, ty)) = self.resolve_type_name(store, type_name) {
297 if arity_of(&ty) == expected_arity {
298 return Some((qname, ty));
299 }
300 if !type_name.contains('.')
301 && let Some((pname, pty)) = self.resolve_type_from_prelude(store, type_name)
302 && arity_of(&pty) == expected_arity
303 {
304 return Some((pname, pty));
305 }
306 return Some((qname, ty));
307 }
308
309 self.resolve_type_from_prelude(store, type_name)
310 }
311
312 pub fn instantiate_from_annotations(
313 &mut self,
314 store: &Store,
315 generics: &[EcoString],
316 body: &Type,
317 type_args: &[Annotation],
318 span: &Span,
319 ) -> Type {
320 let args: Vec<Type> = type_args
321 .iter()
322 .map(|arg_ann| self.convert_to_type(store, arg_ann, span))
323 .collect();
324
325 let map: SubstitutionMap = generics
326 .iter()
327 .zip(args.iter())
328 .map(|(name, ty)| (name.clone(), ty.clone()))
329 .collect();
330
331 substitute(body, &map)
332 }
333
334 pub(crate) fn check_undeclared_impl_type_params(
341 &mut self,
342 annotation: &Annotation,
343 generics: &[Generic],
344 ) {
345 let Annotation::Constructor {
346 name: receiver_name,
347 params,
348 ..
349 } = annotation
350 else {
351 return;
352 };
353
354 let undeclared: Vec<_> = params
355 .iter()
356 .filter_map(|param| {
357 let Annotation::Constructor {
358 name,
359 params: sub_params,
360 span: param_span,
361 } = param
362 else {
363 return None;
364 };
365
366 if sub_params.is_empty()
369 && name.len() == 1
370 && name.chars().next().is_some_and(|c| c.is_uppercase())
371 && self.lookup_generic_index(name).is_none()
372 {
373 Some((name.to_string(), *param_span))
374 } else {
375 None
376 }
377 })
378 .collect();
379
380 for (i, (name, param_span)) in undeclared.iter().enumerate() {
381 self.scopes
382 .current_mut()
383 .type_params
384 .get_or_insert_with(HashMap::default)
385 .insert(name.clone(), generics.len() + i);
386 self.sink
387 .push(diagnostics::infer::undeclared_impl_type_param(
388 name,
389 *param_span,
390 receiver_name,
391 ));
392 }
393 }
394
395 fn check_map_key_comparable(&mut self, store: &Store, key_ty: &Type, span: Span) {
396 let resolved = key_ty.resolve_in(&self.env);
397
398 if self.is_lis(store) && resolved.resolves_to_unknown() {
399 self.sink.push(diagnostics::infer::unknown_as_map_key(span));
400 return;
401 }
402
403 let reason = if matches!(&resolved, Type::Function(_)) {
404 "functions"
405 } else if resolved.has_name("Slice") {
406 "slices"
407 } else if resolved.has_name("Map") {
408 "maps"
409 } else {
410 return;
411 };
412
413 self.sink.push(diagnostics::infer::non_comparable_map_key(
414 &resolved, reason, span,
415 ));
416 }
417}