Skip to main content

lisette_semantics/checker/infer/
mod.rs

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