lisette_semantics/checker/infer/
mod.rs1pub(crate) mod addressability;
2mod carry_mut;
3mod context;
4pub(crate) mod expressions;
5mod interface;
6mod unify;
7mod validation;
8
9pub use context::InferCtx;
10pub(crate) use unify::BuiltinBound;
11
12use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
13
14use super::freeze::FreezeFolder;
15use super::{FileContextKind, TaskState};
16use crate::store::Store;
17use syntax::ast::Expression;
18use syntax::program::{File, FileImport};
19
20impl TaskState<'_> {
21 pub fn take_module_files(&mut self, store: &mut Store, module_id: &str) -> Vec<File> {
23 self.with_module_cursor(module_id, |_this| {
24 let module = store
25 .get_module_mut(module_id)
26 .expect("module must exist for inference");
27 std::mem::take(&mut module.files).into_values().collect()
28 })
29 }
30}
31
32impl InferCtx<'_, '_> {
33 pub fn infer_module(&mut self, module_id: &str, files: Vec<File>) {
35 let items_per_file: Vec<&[Expression]> = files.iter().map(|f| f.items.as_slice()).collect();
36 self.check_const_cycles(&items_per_file);
37
38 for file in files {
39 self.infer_file(module_id, file);
40 }
41 }
42
43 fn infer_file(&mut self, module_id: &str, file: File) {
44 let store = self.store;
45 let file_id = file.id;
46 let imports = file.imports();
47
48 self.with_file_context(
49 store,
50 module_id,
51 file_id,
52 &imports,
53 FileContextKind::Standard,
54 |this, store| {
55 let mut ctx = InferCtx::new(this, store);
56 ctx.check_definition_module_collisions(&file.items, &imports);
57
58 let inferred_items: Vec<_> = file
59 .items
60 .into_iter()
61 .map(|item| {
62 let type_var = ctx.new_type_var();
63 ctx.infer_expression(item, &type_var)
64 })
65 .collect();
66
67 ctx.check_reference_sibling_aliasing(&inferred_items);
68
69 let frozen_items = {
70 let state = &mut *ctx;
71 let folder = FreezeFolder::new(&state.env);
72 folder.freeze_facts(&mut state.facts);
73 FreezeFolder::new(&state.env).freeze_items(inferred_items)
74 };
75
76 let typed_file = File {
77 id: file_id,
78 module_id: file.module_id,
79 name: file.name,
80 display_path: file.display_path,
81 source: file.source,
82 items: frozen_items,
83 };
84
85 ctx.typed_files.push((module_id.to_string(), typed_file));
86 },
87 );
88 }
89
90 fn check_definition_module_collisions(&mut self, items: &[Expression], imports: &[FileImport]) {
91 let store = self.store;
92 let alias_to_path: HashMap<String, String> = imports
93 .iter()
94 .filter_map(|imp| {
95 imp.effective_alias(&store.go_package_names)
96 .map(|alias| (alias, imp.name.to_string()))
97 })
98 .collect();
99
100 for item in items {
101 let (definition_name, name_span) = match item {
102 Expression::Function {
103 name, name_span, ..
104 } => (name.as_str(), *name_span),
105 Expression::Struct {
106 name, name_span, ..
107 } => (name.as_str(), *name_span),
108 Expression::Enum {
109 name, name_span, ..
110 } => (name.as_str(), *name_span),
111 Expression::TypeAlias {
112 name, name_span, ..
113 } => (name.as_str(), *name_span),
114 Expression::Const {
115 identifier,
116 identifier_span,
117 ..
118 } => (identifier.as_str(), *identifier_span),
119 Expression::Interface {
120 name, name_span, ..
121 } => (name.as_str(), *name_span),
122 _ => continue,
123 };
124
125 if let Some(import_path) = alias_to_path.get(definition_name) {
126 self.sink
127 .push(diagnostics::infer::definition_shadows_import(
128 definition_name,
129 import_path,
130 name_span,
131 ));
132 }
133 }
134 }
135
136 pub(crate) fn register_block_local_items(&mut self, items: &[Expression]) {
137 for item in items {
138 match item {
139 Expression::Const { .. } => self.register_block_local_const(item),
140 Expression::Function { .. } => self.register_block_local_fn(item),
141 _ => {}
142 }
143 }
144 }
145
146 fn register_block_local_const(&mut self, item: &Expression) {
147 let store = self.store;
148 let Expression::Const {
149 identifier,
150 identifier_span,
151 annotation,
152 expression,
153 span,
154 ..
155 } = item
156 else {
157 return;
158 };
159
160 let qualified_name = self.qualify_name(identifier);
161 let is_duplicate = self.scopes.lookup_const(identifier)
162 || self.is_const_name(store, qualified_name.as_str());
163 if is_duplicate && self.is_lis(store) {
164 self.sink.push(diagnostics::infer::duplicate_definition(
165 "constant",
166 identifier,
167 *identifier_span,
168 ));
169 return;
170 }
171
172 let before = self.sink.len();
173 let const_ty = if let Some(annotation) = annotation {
174 self.convert_to_type(store, annotation, span)
175 } else {
176 self.type_from_literal_expression(expression)
177 .unwrap_or_else(|| self.new_type_var())
178 };
179 self.sink.truncate(before);
180
181 let scope = self.scopes.current_mut();
182 scope.values.insert(identifier.to_string(), const_ty);
183 scope
184 .consts
185 .get_or_insert_with(HashSet::default)
186 .insert(identifier.to_string());
187 }
188
189 fn register_block_local_fn(&mut self, item: &Expression) {
190 let Expression::Function {
191 name,
192 generics,
193 params,
194 return_annotation,
195 span,
196 ..
197 } = item
198 else {
199 return;
200 };
201
202 let store = self.store;
203 let before = self.sink.len();
204 let fn_ty = self.extract_signature_parts(store, generics, params, return_annotation, span);
205 self.sink.truncate(before);
206
207 let scope = self.scopes.current_mut();
208 scope.values.insert(name.to_string(), fn_ty);
209 }
210}