Skip to main content

lisette_semantics/checker/infer/
mod.rs

1pub(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    /// Extract a module's `.lis` files from the store.
22    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    /// Infer types for `files` belonging to `module_id`.
34    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}