Skip to main content

knave_expand/expand/
mod.rs

1mod binder_form;
2mod bindings;
3mod budget_arena;
4mod classified_form;
5mod compile_time_check;
6mod context;
7mod declared_trait;
8mod defining_call;
9mod docstring_notes;
10mod documented_terms;
11mod duplicate_definitions;
12mod enclosing_index;
13mod evaluated_forms;
14mod expansion_limit;
15mod expression;
16mod failed_definition;
17mod field_keyword;
18mod form_budget;
19mod generated_entry;
20mod globals;
21mod host;
22mod input_terms;
23mod introduced;
24mod macro_call;
25mod macro_function;
26mod macro_origins;
27#[cfg(test)]
28mod macro_value_limits;
29mod misplaced_definition;
30mod module_body;
31mod module_loading;
32mod module_notes;
33mod module_survey;
34mod names;
35mod noted_term;
36mod noted_terms;
37mod notes_module;
38mod prelude_state;
39mod problem_mark;
40mod problem_report;
41mod problems;
42mod recorded_inputs;
43mod recovered_errors;
44mod recur_check;
45mod rest_of_body;
46mod result_printer;
47mod returned_arguments;
48mod rust_clauses;
49mod rust_type;
50mod saved_definitions;
51mod saved_index;
52mod saved_notes;
53mod saved_values;
54mod session;
55mod set_aside_input;
56mod set_aside_problems;
57mod shape;
58mod sourced_items;
59mod spliced_forms;
60mod string_view;
61mod structural_path_claim;
62mod structural_path_claims;
63mod structural_traits;
64mod top_level_form;
65mod try_check;
66mod type_expr;
67mod type_note;
68mod type_parameters;
69
70use std::rc::Rc;
71
72use rustc_hash::{FxHashMap, FxHashSet};
73
74use crate::core::{
75    BindingArena, BindingId, BindingKind, CoreModule, ModuleId, ModulePath, Namespace, References,
76};
77use crate::diagnostic::Diagnostics;
78use crate::interpret::{Closure, Interpreter, Value};
79use crate::modules::{ModuleRegistry, ModuleSources};
80use crate::reader::Form;
81use crate::scope::{BindingTable, Phase, Scope, Scopes};
82use crate::source::{SourceId, SourceMap, Span};
83use bindings::Bindings;
84use budget_arena::BudgetArena;
85pub use classified_form::ClassifiedForm;
86use docstring_notes::DocstringNotes;
87use documented_terms::DocumentedTerms;
88use duplicate_definitions::DuplicateDefinitions;
89use evaluated_forms::EvaluatedForms;
90pub use generated_entry::GeneratedEntry;
91use globals::{GlobalTable, LoweredGlobals};
92pub use input_terms::InputTerms;
93pub use macro_call::MacroOrigin;
94use macro_call::MacroTable;
95pub use macro_origins::MacroOrigins;
96use module_notes::{ModuleNotes, NotesCheckpoint};
97pub use module_survey::ModuleSurvey;
98use noted_term::NotedTerm;
99use noted_terms::NotedTerms;
100use notes_module::NotesModule;
101pub use prelude_state::{PreludeState, UnreadablePrelude};
102use problem_report::ProblemReport;
103pub use problems::ExpandProblems;
104use recorded_inputs::RecordedInputs;
105pub use recorded_inputs::{BoundName, LoadedModule};
106use recovered_errors::RecoveredErrors;
107pub use result_printer::{GeneratedFunction, ResultPrinter};
108pub use saved_definitions::PendingDefinitions;
109use saved_definitions::{DefinitionSaver, SavedDefinitions, SavedTables};
110use saved_values::SavedValues;
111pub use session::ExpansionSession;
112pub use set_aside_input::SetAsideInput;
113pub use set_aside_problems::SetAsideProblems;
114use string_view::StringViewSearch;
115use structural_path_claims::StructuralPathClaims;
116use structural_traits::StructuralTraits;
117use type_note::TypeNote;
118
119pub struct Program {
120    pub modules: Vec<CoreModule>,
121    pub entry: ModuleId,
122    pub entry_function: Option<BindingId>,
123    pub bindings: BindingArena,
124    pub macro_origins: Rc<MacroOrigins>,
125    pub macro_helpers: FxHashSet<BindingId>,
126    pub string_view: Option<BindingId>,
127    pub checked_roots: Vec<BindingId>,
128    pub pending_definitions: PendingDefinitions,
129    pub warnings: Diagnostics,
130    pub expansion: ExpansionWork,
131}
132
133#[derive(Clone, Copy, Default)]
134pub struct ExpansionWork {
135    pub lowered_forms: usize,
136}
137
138impl Program {
139    pub(super) const WRITTEN_ENTRY_FUNCTION: &str = "main";
140
141    pub fn entry_module(&self) -> &CoreModule {
142        self.modules
143            .iter()
144            .find(|module| module.id == self.entry)
145            .expect("the entry module is expanded")
146    }
147
148    pub fn prelude_macro_helpers(&self) -> FxHashSet<BindingId> {
149        let prelude: FxHashSet<ModuleId> = self
150            .modules
151            .iter()
152            .filter(|module| ModuleSources::is_prelude(&module.path))
153            .map(|module| module.id)
154            .collect();
155        let is_prelude_global = |binding: BindingId| {
156            matches!(self.bindings.get(binding).kind(),
157                BindingKind::Global(module) if prelude.contains(&module))
158        };
159        self.macro_helpers
160            .iter()
161            .copied()
162            .filter(|binding| self.bindings.is_saved(*binding) || is_prelude_global(*binding))
163            .collect()
164    }
165
166    pub fn check_also(&mut self, binding: BindingId) {
167        self.checked_roots.push(binding);
168        self.pending_definitions
169            .restore(&mut self.modules, vec![binding]);
170    }
171
172    fn restore_reached(&mut self) {
173        let roots = self
174            .modules
175            .iter()
176            .flat_map(CoreModule::definitions)
177            .flat_map(|(_, value)| References::of(value, &self.bindings).into_globals())
178            .collect();
179        self.pending_definitions.restore(&mut self.modules, roots);
180    }
181
182    fn written_entry_function(&self) -> Option<BindingId> {
183        self.entry_module()
184            .definitions()
185            .map(|(binding, _)| binding)
186            .find(|binding| self.bindings.get(*binding).name() == Self::WRITTEN_ENTRY_FUNCTION)
187    }
188}
189
190#[derive(Clone, serde::Serialize, serde::Deserialize)]
191pub struct TraitMethods {
192    pub rust_name: Rc<str>,
193    pub self_variable: crate::core::BindingId,
194    pub associated: Vec<(crate::core::BindingId, Rc<str>)>,
195    pub methods: Vec<(crate::core::BindingId, crate::core::TypeExpr)>,
196    pub defaults: rustc_hash::FxHashSet<crate::core::BindingId>,
197}
198
199pub struct Expander<'s> {
200    sources: &'s mut SourceMap,
201    module_sources: ModuleSources,
202    modules: ModuleRegistry,
203    tables: ExpansionTables,
204    current_module: Option<ModuleId>,
205    problems: ProblemReport,
206    reported_hidden_modules: Vec<std::path::PathBuf>,
207    library_modules: Vec<(ModulePath, Rc<str>)>,
208    phase: Phase,
209    expansion_depth: usize,
210    nesting_depth: usize,
211    form_budgets: BudgetArena,
212    recovered: RecoveredErrors,
213    type_note: TypeNote,
214    saved_prelude: Option<PreludeState>,
215    saved_definitions: SavedDefinitions,
216    saved_macros: SavedValues<Closure>,
217    saved_globals: SavedValues<Value>,
218    expansion: ExpansionWork,
219    evaluated_forms: EvaluatedForms,
220    recorded_inputs: RecordedInputs,
221    declared_definitions: Vec<failed_definition::DeclaredName>,
222    failed_body_definitions: Vec<BindingId>,
223    introductions: Vec<Scope>,
224    duplicate_definitions: DuplicateDefinitions,
225}
226
227#[derive(Default, serde::Serialize, serde::Deserialize)]
228struct ExpansionTables {
229    scopes: Scopes,
230    table: BindingTable,
231    bindings: BindingArena,
232    macros: MacroTable,
233    macro_origins: MacroOrigins,
234    global_macros: Vec<(crate::core::BindingId, Form)>,
235    trait_paths: FxHashMap<crate::core::BindingId, Rc<str>>,
236    structural_path_claims: StructuralPathClaims,
237    enum_variants: FxHashMap<crate::core::BindingId, Vec<crate::core::BindingId>>,
238    variant_fields: FxHashMap<crate::core::BindingId, Vec<Rc<str>>>,
239    trait_methods: FxHashMap<crate::core::BindingId, TraitMethods>,
240    globals: GlobalTable,
241    module_notes: ModuleNotes,
242    expanded: FxHashMap<ModuleId, CoreModule>,
243    order: Vec<ModuleId>,
244    string_view: Option<BindingId>,
245}
246
247impl ExpansionTables {
248    fn forget_locals(&mut self) {
249        let bindings = &self.bindings;
250        self.table
251            .forget(|binding| matches!(bindings.get(binding).kind(), BindingKind::Local));
252    }
253
254    fn find_string_view(&self) -> Option<BindingId> {
255        let modules = self
256            .order
257            .iter()
258            .filter_map(|id| self.expanded.get(id))
259            .filter(|module| ModuleSources::is_embedded_prelude(&module.path));
260        StringViewSearch::in_modules(modules)
261    }
262
263    fn save_definitions(&mut self) -> SavedTables {
264        let mut saver = DefinitionSaver::new(&self.bindings, self.string_view);
265        let modules = self.expanded.values_mut();
266        for module in modules.filter(|module| ModuleSources::is_embedded_prelude(&module.path)) {
267            saver.take_from(module);
268        }
269        saver.finish()
270    }
271
272    fn take_lowered_globals(&mut self) -> LoweredGlobals {
273        let definitions = self.expanded.values().flat_map(CoreModule::definitions);
274        self.globals.take_lowered(definitions)
275    }
276
277    fn restore_lowered_globals(&mut self, lowered: &LoweredGlobals) {
278        let definitions = self.expanded.values().flat_map(CoreModule::definitions);
279        self.globals.restore_lowered(lowered, definitions);
280    }
281}
282
283impl<'s> Expander<'s> {
284    pub fn new(sources: &'s mut SourceMap, module_sources: ModuleSources) -> Self {
285        Self {
286            sources,
287            module_sources,
288            modules: ModuleRegistry::default(),
289            tables: ExpansionTables::default(),
290            current_module: None,
291            problems: ProblemReport::default(),
292            reported_hidden_modules: Vec::new(),
293            library_modules: Vec::new(),
294            phase: Phase::Runtime,
295            expansion_depth: 0,
296            nesting_depth: 0,
297            form_budgets: BudgetArena::default(),
298            recovered: RecoveredErrors::default(),
299            type_note: TypeNote::default(),
300            saved_prelude: None,
301            saved_definitions: SavedDefinitions::default(),
302            saved_macros: SavedValues::default(),
303            saved_globals: SavedValues::default(),
304            expansion: ExpansionWork::default(),
305            evaluated_forms: EvaluatedForms::default(),
306            recorded_inputs: RecordedInputs::default(),
307            declared_definitions: Vec::new(),
308            failed_body_definitions: Vec::new(),
309            introductions: Vec::new(),
310            duplicate_definitions: DuplicateDefinitions::default(),
311        }
312    }
313
314    pub fn evaluating(self, spans: impl IntoIterator<Item = Span>) -> Self {
315        Self {
316            evaluated_forms: EvaluatedForms::new(spans),
317            ..self
318        }
319    }
320
321    #[cfg(test)]
322    pub(crate) fn take_diagnostics_with_code(
323        &mut self,
324        code: &crate::diagnostic::DiagnosticCode,
325    ) -> Vec<crate::diagnostic::Diagnostic> {
326        self.take_diagnostics()
327            .in_source_order()
328            .into_iter()
329            .filter(|diagnostic| diagnostic.code() == Some(code))
330            .cloned()
331            .collect()
332    }
333
334    pub fn with_prelude(
335        sources: &'s mut SourceMap,
336        module_sources: ModuleSources,
337        prelude: PreludeState,
338    ) -> Self {
339        Self {
340            saved_prelude: Some(prelude),
341            ..Self::new(sources, module_sources)
342        }
343    }
344
345    #[inline(never)]
346    pub fn expand(mut self, path: ModulePath, entry: SourceId) -> Result<Program, Diagnostics> {
347        let entry = self.load_source_module(path, &[entry]);
348        self.into_program(entry)
349    }
350
351    fn into_program(mut self, entry: ModuleId) -> Result<Program, Diagnostics> {
352        let diagnostics = self.take_diagnostics();
353        self.tables.macro_origins.forget_introduced_names();
354        let string_view = self
355            .tables
356            .string_view
357            .or_else(|| self.tables.find_string_view());
358        let mut program = Program {
359            modules: self.take_modules(),
360            entry,
361            entry_function: None,
362            bindings: self.tables.bindings,
363            macro_helpers: self.tables.globals.macro_helpers(),
364            string_view,
365            checked_roots: Vec::new(),
366            pending_definitions: PendingDefinitions::new(self.saved_definitions),
367            macro_origins: Rc::new(self.tables.macro_origins),
368            warnings: self.problems.take_warnings(),
369            expansion: self.expansion,
370        };
371        program.restore_reached();
372        program.entry_function = program.written_entry_function();
373        diagnostics.into_result(program)
374    }
375
376    fn take_diagnostics(&mut self) -> Diagnostics {
377        self.recovered.hand_over(&mut self.problems);
378        self.problems.take_errors()
379    }
380
381    fn structural_traits(&mut self) -> StructuralTraits<'_> {
382        StructuralTraits::new(
383            &self.tables.bindings,
384            &mut self.tables.structural_path_claims,
385            &mut self.problems,
386        )
387    }
388
389    fn check_documented_terms(&mut self, module: ModuleId, since: NotesCheckpoint) {
390        for undefined in self.documented_terms(module, since).undefined() {
391            self.problems.report(undefined);
392        }
393    }
394
395    fn documented_terms(
396        &mut self,
397        module: ModuleId,
398        since: NotesCheckpoint,
399    ) -> DocumentedTerms<'_> {
400        let noted = self.noted_terms(module, since);
401        DocumentedTerms::new(self.bindings(), &self.duplicate_definitions, noted)
402    }
403
404    fn bindings(&self) -> Bindings<'_> {
405        Bindings::new(&self.tables.table, &self.tables.bindings, self.phase)
406    }
407
408    fn noted_terms(&mut self, module: ModuleId, since: NotesCheckpoint) -> NotedTerms {
409        let is_prelude = ModuleSources::is_prelude(self.modules.get(module).path());
410        if is_prelude || self.tables.module_notes.checkpoint() == since {
411            return NotedTerms::new(module, Vec::new());
412        }
413        let Some(notes) = self.docstring_notes() else {
414            return NotedTerms::new(module, Vec::new());
415        };
416        let terms = notes
417            .keys()
418            .into_iter()
419            .flat_map(|key| self.tables.module_notes.added_since(since, module, key))
420            .collect::<Vec<Form>>()
421            .into_iter()
422            .filter_map(|note| self.noted_term(notes.noted_term(), note))
423            .collect();
424        NotedTerms::new(module, terms)
425    }
426
427    fn docstring_notes(&mut self) -> Option<DocstringNotes> {
428        let notes_module = NotesModule::new(&self.modules, self.bindings()).find()?;
429        let key = self.exported_value(notes_module, DocstringNotes::DOCSTRINGS_KEY)?;
430        let documented_terms_key =
431            self.exported_value(notes_module, DocstringNotes::DOCUMENTED_TERMS_KEY)?;
432        let noted_term = self.exported_value(notes_module, DocstringNotes::NOTED_TERM)?;
433        DocstringNotes::of(&key, &documented_terms_key, noted_term)
434    }
435
436    fn exported_value(&mut self, module: ModuleId, name: &str) -> Option<Value> {
437        let binding = self.modules.get(module).exported(name, Namespace::Value)?;
438        let span = self.tables.bindings.get(binding).span()?;
439        self.evaluate_global(binding, span).ok()
440    }
441
442    fn noted_term(&mut self, accessor: &Value, note: Form) -> Option<NotedTerm> {
443        let span = note.span();
444        let steps = self.form_budgets.current().steps();
445        let term = Interpreter::new(self, span, steps)
446            .call(accessor, vec![Value::Form(note)], span)
447            .ok()?;
448        let term = term.as_form()?;
449        Some(NotedTerm {
450            term: term.as_symbol()?.clone(),
451            span: term.span(),
452        })
453    }
454
455    fn take_modules(&mut self) -> Vec<CoreModule> {
456        self.tables
457            .order
458            .iter()
459            .filter_map(|id| self.tables.expanded.remove(id))
460            .collect()
461    }
462}