Skip to main content

typr_core/components/context/
mod.rs

1pub mod config;
2pub mod fingerprint;
3pub mod graph;
4pub mod vartype;
5
6use crate::components::context::config::Config;
7use crate::components::context::config::Environment;
8use crate::components::context::config::TargetLanguage;
9use crate::components::context::graph::Graph;
10use crate::components::context::unification_map::UnificationMap;
11use crate::components::context::vartype::VarType;
12use crate::components::error_message::help_data::HelpData;
13use crate::components::language::var::Var;
14use crate::components::language::var_function::VarFunction;
15use crate::components::language::Lang;
16use crate::components::r#type::argument_type::ArgumentType;
17use crate::components::r#type::kind::Kind;
18use crate::components::r#type::type_system::TypeSystem;
19use crate::components::r#type::vector_type::ConstructorCategory;
20use crate::components::r#type::Type;
21use crate::processes::type_checking::match_types_to_generic;
22use crate::processes::type_checking::type_comparison::reduce_type;
23use crate::processes::type_checking::unification_map;
24use crate::utils::builder;
25use crate::utils::standard_library::not_in_blacklist;
26use serde::Deserialize;
27use serde::Serialize;
28use std::collections::HashMap;
29use std::collections::HashSet;
30
31use std::ops::Add;
32use std::sync::Arc;
33use tap::Pipe;
34
35/// True for the auto-generated names given to anonymous record types
36/// (`Record0`, `Record1`, …), as produced by `VarType::push_alias_increment`
37/// (`format!("{}{}", TypeCategory::Record, count)`).
38fn is_anonymous_record_name(name: &str) -> bool {
39    name.strip_prefix("Record")
40        .map(|rest| !rest.is_empty() && rest.chars().all(|c| c.is_ascii_digit()))
41        .unwrap_or(false)
42}
43
44#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
45pub struct Context {
46    pub typing_context: VarType,
47    pub subtypes: Graph<Type>,
48    /// Registry of user-declared `typeconstructor`s: (name, parameter signature, category).
49    #[serde(default)]
50    pub type_constructors: Vec<(String, Vec<Type>, ConstructorCategory)>,
51    /// Constraints mapping a rigid generic variable name to its interface.
52    /// Introduced when an interface type appears in parameter position:
53    /// fn(i: I): R  ⇒  i : A  with A: I  stored here.
54    #[serde(default)]
55    pub interface_constraints: HashMap<String, Type>,
56    /// Counter for generating unique rigid generic variable names.
57    #[serde(default)]
58    pub rigid_counter: u64,
59    /// Flat, whole-program registry of every `type X <- list { ... }` record
60    /// alias declared anywhere, including inside `mod` bodies. Unlike
61    /// `typing_context.aliases`, this is never scoped away at a module
62    /// boundary: R's S3 class system has no module privacy, so transpilation
63    /// needs the full picture to compute structural supertypes for the class
64    /// vector (see `record_field_class` callers in `processes::transpiling`).
65    #[serde(default)]
66    pub record_aliases: Vec<(String, Type)>,
67    /// Named type embedding (`embed field: Type`): provenance of every function
68    /// auto-generated by forwarding/reconstruction, as `(type_name, method_name,
69    /// source_field_name)`. Used to detect a later explicit definition that
70    /// collides with an inherited embedded function (E-EMBED-003).
71    #[serde(default)]
72    pub embedded_methods: Vec<(String, String, String)>,
73    /// RFC-TR-031: lines injected at the top of a `Test { ... }` file so the
74    /// test body can reach `@testable` private members of the enclosing module
75    /// (e.g. `sq <- Math$.test_sq`). Set while transpiling a module body in a
76    /// test build; empty otherwise. Not serialised.
77    #[serde(skip)]
78    pub test_preamble: Vec<String>,
79    /// `Self:{ ... }` (generic_constructor.md): the type bound to the
80    /// enclosing function/method's first parameter, set only while
81    /// type-checking that function's body. `None` everywhere else, which is
82    /// what makes `Self` invalid outside a function body.
83    #[serde(skip)]
84    pub self_type: Option<Type>,
85    /// Inner typing contexts computed while type-checking each `module M { ... }`
86    /// body, keyed by module name. Populated during type-checking; consumed
87    /// during transpilation to avoid re-running `typing()` on every module body.
88    #[serde(skip)]
89    pub module_inner_contexts: HashMap<String, Arc<Context>>,
90    /// Cache of fully type-checked modules. Maps module name → its
91    /// `Type::Module` so that `use Module::*;` can be resolved without
92    /// re-walking the module path in the variable table. Populated by
93    /// `eval()` for `Lang::Module` and consumed by `typing()` for
94    /// `Lang::UseModule`.
95    #[serde(skip)]
96    pub processed_modules: HashMap<String, Type>,
97    /// Set of module names whose body is currently being type-checked.
98    /// Used to detect circular import chains at the type-checking level.
99    /// A `Lang::UseModule` targeting a name in this set is a cycle error.
100    #[serde(skip)]
101    pub modules_in_progress: HashSet<String>,
102    /// Registry of `@extern` function declarations: (TypR name, R-side qualified name).
103    /// When `Option<String>` is `None` the TypR name is used directly as the R call.
104    #[serde(default)]
105    pub extern_fns: Vec<(String, Option<String>)>,
106    /// Registry of `@importFrom` declarations: (TypR function name, "pkg::fn_name").
107    /// At call sites the transpiler emits `pkg::fn_name(args)` instead of `fn_name(args)`,
108    /// bypassing TypR's own S3 generic stubs for names like `get`, `map`, `factor`.
109    #[serde(default)]
110    pub import_from_fns: Vec<(String, String)>,
111    config: Config,
112}
113
114/// The canonical sentinel nodes seeded into every subtype `Graph` so the
115/// kind-sigil generic categories materialize as intermediate levels of the
116/// lattice. Because `is_subtype_raw` already gives `RecordN <: %_ <: Generic
117/// <: Any` (a concrete Record is a subtype of a record-kinded `KindedGen`, and
118/// any generic is a subtype of the bare `Generic`), seeding one anchor per
119/// kind makes every monomorphized record/interface/char/bool/number auto-nest
120/// under its `G*` node. These sentinels all answer `has_generic() == true`, so
121/// `get_classes` filters them out of generated R `class = c(...)` vectors —
122/// they are a compile-time organisation of the hierarchy only.
123fn generic_sentinels() -> Vec<Type> {
124    let h = HelpData::default();
125    let name = "_".to_string();
126    vec![
127        Type::Generic(name.clone(), h.clone()),
128        Type::KindedGen(Kind::Record, name.clone(), h.clone()),
129        Type::KindedGen(Kind::Interface, name.clone(), h.clone()),
130        Type::KindedGen(Kind::String, name.clone(), h.clone()),
131        Type::KindedGen(Kind::Boolean, name.clone(), h.clone()),
132        Type::IndexGen(name, h),
133    ]
134}
135
136/// A fresh subtype graph pre-seeded with the kind-sigil generic sentinels
137/// (see [`generic_sentinels`]). Built against `Context::empty()` since the
138/// sentinel ordering only exercises the structural `is_subtype_raw` arms
139/// (`(_, Generic)`, `(_, Any)`, the `KindedGen`/`IndexGen` arms) and needs no
140/// typing context.
141fn seeded_subtype_graph() -> Graph<Type> {
142    Graph::new().add_types(&generic_sentinels(), &Context::empty())
143}
144
145impl Default for Context {
146    fn default() -> Self {
147        let config = Config::default();
148        Context {
149            config: config.clone(),
150            typing_context: VarType::from_config(config),
151            subtypes: seeded_subtype_graph(),
152            type_constructors: Vec::new(),
153            interface_constraints: HashMap::new(),
154            rigid_counter: 0,
155            record_aliases: Vec::new(),
156            embedded_methods: Vec::new(),
157            test_preamble: Vec::new(),
158            self_type: None,
159            extern_fns: Vec::new(),
160            import_from_fns: Vec::new(),
161            module_inner_contexts: HashMap::new(),
162            processed_modules: HashMap::new(),
163            modules_in_progress: HashSet::new(),
164        }
165    }
166}
167
168impl From<Vec<(Lang, Type)>> for Context {
169    fn from(val: Vec<(Lang, Type)>) -> Self {
170        let val2: Vec<(Var, Type)> = val
171            .iter()
172            .map(|(lan, typ)| (Var::from_language(lan.clone()).unwrap(), typ.clone()))
173            .collect();
174        Context {
175            typing_context: val2.into(),
176            ..Context::default()
177        }
178    }
179}
180
181impl Context {
182    pub fn new(types: Vec<(Var, Type)>) -> Context {
183        Context {
184            typing_context: types.into(),
185            ..Context::default()
186        }
187    }
188
189    pub fn empty() -> Self {
190        Context {
191            config: Config::default(),
192            typing_context: VarType::new(),
193            subtypes: Graph::new(),
194            type_constructors: Vec::new(),
195            interface_constraints: HashMap::new(),
196            rigid_counter: 0,
197            record_aliases: Vec::new(),
198            embedded_methods: Vec::new(),
199            test_preamble: Vec::new(),
200            self_type: None,
201            extern_fns: Vec::new(),
202            import_from_fns: Vec::new(),
203            module_inner_contexts: HashMap::new(),
204            processed_modules: HashMap::new(),
205            modules_in_progress: HashSet::new(),
206        }
207    }
208
209    pub fn is_extern_fn(&self, name: &str) -> bool {
210        self.extern_fns.iter().any(|(n, _)| n == name)
211    }
212
213    pub fn get_extern_r_name(&self, name: &str) -> Option<String> {
214        self.extern_fns
215            .iter()
216            .find(|(n, _)| n == name)
217            .and_then(|(_, r)| r.clone())
218    }
219
220    pub fn is_import_from_fn(&self, name: &str) -> bool {
221        self.import_from_fns.iter().any(|(n, _)| n == name)
222    }
223
224    pub fn get_import_from_r_name(&self, name: &str) -> Option<String> {
225        self.import_from_fns
226            .iter()
227            .find(|(n, _)| n == name)
228            .map(|(_, r)| r.clone())
229    }
230
231    pub fn set_config(self, config: Config) -> Self {
232        Self { config, ..self }
233    }
234
235    pub fn set_as_module_context(self) -> Context {
236        Self {
237            config: self.config.set_as_module(),
238            ..self
239        }
240    }
241
242    pub fn set_test_mode(self, val: bool) -> Context {
243        Self {
244            config: self.config.set_test_mode(val),
245            ..self
246        }
247    }
248
249    pub fn get_test_mode(&self) -> bool {
250        self.config.test_mode
251    }
252
253    pub fn set_test_preamble(self, lines: Vec<String>) -> Context {
254        Self {
255            test_preamble: lines,
256            ..self
257        }
258    }
259
260    /// `Self:{ ... }` (generic_constructor.md §4.1): bind/clear the type
261    /// denoted by `Self` for the duration of typing a function body.
262    pub fn set_self_type(self, self_type: Option<Type>) -> Context {
263        Self { self_type, ..self }
264    }
265
266    pub fn store_module_inner_context(mut self, name: &str, inner: &Context) -> Self {
267        // `inner` is the module body's final context, which inherits (and thus
268        // still carries) every `module_inner_contexts` entry that was already
269        // present in the *ambient* context before this module started (nested
270        // sibling modules typed earlier in the same file/enclosing module).
271        // Storing `inner` as-is would re-embed all of those already-stored
272        // snapshots inside this module's own boxed snapshot; since `Context`
273        // is cloned pervasively throughout type-checking, and every module
274        // boundary would repeat this, the nesting depth (and thus clone cost)
275        // grows exponentially with the number of `mod` declarations. Keep
276        // only the entries genuinely *new* to this module's own body (i.e.
277        // not already known to the ambient context) — those are exactly the
278        // modules declared/nested directly within this one.
279        let new_entries: HashMap<String, Arc<Context>> = inner
280            .module_inner_contexts
281            .iter()
282            .filter(|(k, _)| !self.module_inner_contexts.contains_key(k.as_str()))
283            .map(|(k, v)| (k.clone(), v.clone()))
284            .collect();
285        let mut trimmed = inner.clone();
286        trimmed.module_inner_contexts = new_entries;
287        self.module_inner_contexts
288            .insert(name.to_string(), Arc::new(trimmed));
289        self
290    }
291
292    pub fn get_module_inner_context(&self, name: &str) -> Option<&Context> {
293        self.module_inner_contexts.get(name).map(|b| b.as_ref())
294    }
295
296    /// Mark a module as currently being type-checked. Returns the updated
297    /// context with `name` added to `modules_in_progress`.
298    pub fn mark_module_in_progress(self, name: &str) -> Self {
299        let mut set = self.modules_in_progress.clone();
300        set.insert(name.to_string());
301        Self {
302            modules_in_progress: set,
303            ..self
304        }
305    }
306
307    /// Remove a module from the in-progress set after its body has been
308    /// fully type-checked.
309    pub fn unmark_module_in_progress(self, name: &str) -> Self {
310        let mut set = self.modules_in_progress.clone();
311        set.remove(name);
312        Self {
313            modules_in_progress: set,
314            ..self
315        }
316    }
317
318    /// True when `name` is currently being type-checked (its body is on the
319    /// call stack). A `use {name}::*;` encountered while this returns `true`
320    /// is a circular dependency.
321    pub fn is_module_in_progress(&self, name: &str) -> bool {
322        self.modules_in_progress.contains(name)
323    }
324
325    /// Register `module_type` in the processed-module cache so that
326    /// subsequent `use {name}::*;` directives can resolve it without
327    /// walking the full variable-path chain.
328    pub fn cache_processed_module(self, name: &str, module_type: Type) -> Self {
329        let mut map = self.processed_modules.clone();
330        map.insert(name.to_string(), module_type);
331        Self {
332            processed_modules: map,
333            ..self
334        }
335    }
336
337    /// Look up a previously cached module by name. Returns `Some(module_type)`
338    /// when `name` has already been fully type-checked in this session.
339    pub fn get_processed_module(&self, name: &str) -> Option<&Type> {
340        self.processed_modules.get(name)
341    }
342
343    pub fn set_in_module_body(self) -> Self {
344        Self {
345            config: self.config.set_in_module_body(true),
346            ..self
347        }
348    }
349
350    pub fn is_in_module_body(&self) -> bool {
351        self.config.in_module_body
352    }
353
354    /// Retourne un nouveau Context avec le Graph de sous-typage mis à jour
355    pub fn with_subtypes(self, subtypes: Graph<Type>) -> Self {
356        Self { subtypes, ..self }
357    }
358
359    pub fn get_members(&self) -> Vec<(Var, Type)> {
360        self.typing_context
361            .variables()
362            .chain(self.aliases())
363            .cloned()
364            .collect::<Vec<_>>()
365    }
366
367    pub fn variable_exist(&self, var: Var) -> Option<Var> {
368        self.typing_context.variable_exist(var, self)
369    }
370
371    pub fn get_type_from_variable(&self, var: &Var) -> Result<Type, String> {
372        let res = self
373            .typing_context
374            .entries_named(&var.get_name())
375            .into_iter()
376            .flat_map(|(var2, typ)| {
377                let conditions = (var.is_opaque == var2.is_opaque)
378                    && var.related_type.is_subtype(&var2.related_type, self).0;
379                if conditions {
380                    Some(typ)
381                } else {
382                    None
383                }
384            })
385            .reduce(|acc, x| if x.is_subtype(&acc, self).0 { x } else { acc });
386        match res {
387            Some(typ) => Ok(typ),
388            // Every call site discards this message via `.ok()`/`unwrap_or_else`,
389            // so `display_typing_context()` (formats ~1700 stdlib+user entries)
390            // is never actually read — skip it rather than pay it on every
391            // speculative lookup (see project_typechecking_optimization memory).
392            _ => Err(format!("Didn't find {} in the context", var.get_name())),
393        }
394    }
395
396    pub fn get_types_from_name(&self, name: &str) -> Vec<Type> {
397        self.typing_context
398            .entries_named(name)
399            .into_iter()
400            .map(|(_, typ)| typ)
401            .collect()
402    }
403
404    pub fn get_type_from_aliases(&self, var: &Var) -> Option<Type> {
405        self.aliases()
406            .flat_map(|(var2, type_)| {
407                let conditions = (var.name == var2.name)
408                    && (var.is_opaque == var2.is_opaque)
409                    && var.related_type.is_subtype(&var2.related_type, self).0;
410                if conditions {
411                    Some(type_.clone())
412                } else {
413                    None
414                }
415            })
416            .next()
417    }
418
419    fn is_matching_alias(&self, var1: &Var, var2: &Var) -> bool {
420        var1.name == var2.name
421    }
422
423    pub fn get_matching_alias_signature(&self, var: &Var) -> Option<(Type, Vec<Type>)> {
424        self.aliases()
425            .find(|(var2, _)| self.is_matching_alias(var, var2))
426            .map(|(var2, target_type)| {
427                if var2.is_opaque() {
428                    (var2.clone().to_alias_type(), vec![])
429                } else if let Type::Params(types, _) = var2.get_type() {
430                    (target_type.clone(), types.clone())
431                } else {
432                    panic!("The related type is not Params([...])");
433                }
434            })
435            // Module-internal record aliases are dropped from `aliases()` at
436            // the module boundary for encapsulation, but their names still
437            // appear inside hoisted structural types and exported signatures.
438            // `record_aliases` is the whole-program record registry kept for
439            // codegen (see `merge_record_aliases`) — resolve through it so
440            // structural subtype checks don't collapse such names to `Any`.
441            .or_else(|| {
442                self.record_aliases
443                    .iter()
444                    .find(|(name, _)| *name == var.get_name())
445                    .map(|(_, typ)| (typ.clone(), vec![]))
446            })
447    }
448
449    pub fn variables(&self) -> impl Iterator<Item = &(Var, Type)> + '_ {
450        self.typing_context.variables()
451    }
452
453    pub fn aliases(&self) -> impl Iterator<Item = &(Var, Type)> + '_ {
454        self.typing_context.aliases()
455    }
456
457    /// Generate a fresh rigid generic variable name (immutable builder pattern).
458    pub fn fresh_rigid_name(self) -> (String, Self) {
459        let name = format!("__rigid_{}", self.rigid_counter);
460        (
461            name,
462            Self {
463                rigid_counter: self.rigid_counter + 1,
464                ..self
465            },
466        )
467    }
468
469    /// Register a constraint: rigid variable → interface type.
470    pub fn add_interface_constraint(mut self, rigid_name: String, interface: Type) -> Context {
471        self.interface_constraints.insert(rigid_name, interface);
472        self
473    }
474
475    /// Look up the interface constraint for a rigid variable.
476    pub fn get_interface_constraint(&self, name: &str) -> Option<&Type> {
477        self.interface_constraints.get(name)
478    }
479
480    /// Check if a name is a constrained rigid variable.
481    pub fn is_rigid_constrained(&self, name: &str) -> bool {
482        self.interface_constraints.contains_key(name)
483    }
484
485    pub fn push_var_type(self, lang: Var, typ: Type, context: &Context) -> Context {
486        let reduced_type = typ.reduce(context);
487        let types = reduced_type.extract_types();
488        let new_subtypes = self.subtypes.add_types(&types, context);
489        let var_type = self
490            .typing_context
491            .pipe(|vt| {
492                if reduced_type.is_interface() && lang.is_variable() {
493                    vt.push_interface(lang.clone(), reduced_type, typ.clone(), context)
494                } else {
495                    vt.push_var_type(&[(lang.clone(), typ.clone())])
496                }
497            })
498            .push_types(&types);
499        Context {
500            typing_context: var_type,
501            subtypes: new_subtypes,
502            ..self
503        }
504    }
505
506    pub fn replace_or_push_var_type(self, lang: Var, typ: Type, context: &Context) -> Context {
507        let types = typ.reduce(context).extract_types();
508        let var_type = self
509            .typing_context
510            .clone()
511            .replace_or_push_var_type(&[(lang.clone(), typ.clone())])
512            .push_types(&types);
513        let new_subtypes = self.subtypes.add_types(&types, context);
514        Context {
515            typing_context: var_type,
516            subtypes: new_subtypes,
517            ..self
518        }
519    }
520
521    // Remove variables from the context
522    // For removing added variables for evaluating a function's body
523    pub fn remove_vars(self, vars: &[Var]) -> Context {
524        Context {
525            typing_context: self.typing_context.remove_vars(vars),
526            ..self
527        }
528    }
529
530    pub fn push_types(self, types: &[Type]) -> Self {
531        // The subtype graph is the whole-program registry the transpiler
532        // walks to compute class chains (`get_classes`): every registered
533        // type must be a node there, or values annotated with it lose their
534        // structural supertype classes at runtime dispatch.
535        let new_subtypes = self.subtypes.clone().add_types(types, &self);
536        Self {
537            typing_context: self.typing_context.clone().push_types(types),
538            subtypes: new_subtypes,
539            ..self
540        }
541    }
542
543    /// Hoists auto-generated type-alias registrations from an inner scope's
544    /// context (function body, module body) into this one — see
545    /// `VarType::hoist_aliases`. The hoisted types are also added to the
546    /// subtype graph so structural supertype lookups (S3 class chains)
547    /// keep working outside the scope that registered them.
548    pub fn hoist_aliases(self, inner: &Context) -> Self {
549        let hoisted_types: Vec<Type> = self
550            .typing_context
551            .hoisted_alias_pairs(&inner.typing_context)
552            .into_iter()
553            .map(|(_, typ)| typ)
554            .collect();
555        let new_subtypes = self.subtypes.clone().add_types(&hoisted_types, &self);
556        Self {
557            typing_context: self
558                .typing_context
559                .clone()
560                .hoist_aliases(&inner.typing_context),
561            subtypes: new_subtypes,
562            ..self
563        }
564    }
565
566    pub fn get_type_from_existing_variable(&self, var: Var) -> Type {
567        if let Type::UnknownFunction(_) = var.get_type() {
568            var.get_type()
569        } else {
570            self.typing_context
571                .variables()
572                .find(|(v, _)| var.match_with(v, self))
573                .map(|(_, ty)| ty)
574                // Return Any type instead of panicking if variable not found
575                .unwrap_or(&Type::Any(var.get_help_data()))
576                .clone()
577        }
578    }
579
580    pub fn get_true_variable(&self, var: &Var) -> Var {
581        let res = self
582            .typing_context
583            .variables()
584            .find(|(v, _)| var.match_with(v, self))
585            .map(|(v, _)| v);
586        match res {
587            Some(vari) => vari.clone(),
588            _ => {
589                // Return the variable with UnknownFunction type if it's a standard function
590                // Otherwise return with Any type to allow error collection
591                if self.is_an_untyped_function(&var.get_name()) {
592                    var.clone()
593                        .set_type(Type::UnknownFunction(var.get_help_data()))
594                } else {
595                    var.clone().set_type(Type::Any(var.get_help_data()))
596                }
597            }
598        }
599    }
600
601    fn is_a_standard_function(&self, name: &str) -> bool {
602        !self.typing_context.name_exists_outside_of_std(name)
603    }
604
605    pub fn is_an_untyped_function(&self, name: &str) -> bool {
606        self.is_a_standard_function(name)
607    }
608
609    pub fn get_class(&self, t: &Type) -> String {
610        // For a named alias whose underlying type is a record, return the alias name directly.
611        // push_types may have also registered the same record with an auto-generated "Record0"
612        // name; searching aliases by type value would find that first (insertion-order) and
613        // return the wrong name.
614        if let Type::Alias(name, _, false, _) = t {
615            if let Some((_, underlying)) = self.aliases().find(|(v, _)| v.get_name() == *name) {
616                if matches!(underlying, Type::Record(_, _)) {
617                    return "'".to_string() + name + "'";
618                }
619            }
620        }
621        let reduced = t.reduce(self);
622        if matches!(reduced, Type::Any(_)) {
623            if let Type::Alias(name, _, _, _) = t {
624                return "'".to_string() + name + "'";
625            }
626        }
627        self.typing_context.get_class(&reduced)
628    }
629
630    pub fn get_class_unquoted(&self, t: &Type) -> String {
631        // Same rationale as get_class: bypass the record-type alias search for named aliases.
632        if let Type::Alias(name, _, false, _) = t {
633            if let Some((_, underlying)) = self.aliases().find(|(v, _)| v.get_name() == *name) {
634                if matches!(underlying, Type::Record(_, _)) {
635                    return name.clone();
636                }
637            }
638        }
639        let reduced = t.reduce(self);
640        if matches!(reduced, Type::Any(_)) {
641            if let Type::Alias(name, _, _, _) = t {
642                return name.clone();
643            }
644        }
645        self.typing_context.get_class_unquoted(&reduced)
646    }
647
648    pub fn module_aliases(&self) -> Vec<(Var, Type)> {
649        self.variables()
650            .flat_map(|(_, typ)| typ.clone().to_module_type())
651            .flat_map(|module| module.get_aliases())
652            .collect()
653    }
654
655    pub fn get_type_anotations(&self) -> String {
656        self.aliases()
657            .chain(
658                [
659                    (Var::from_name("Integer"), builder::integer_type_default()),
660                    (
661                        Var::from_name("Character"),
662                        builder::character_type_default(),
663                    ),
664                    (Var::from_name("Number"), builder::number_type()),
665                    (Var::from_name("Boolean"), builder::boolean_type()),
666                ]
667                .iter(),
668            )
669            .cloned()
670            .chain(self.module_aliases())
671            .filter(|(_, typ)| typ.clone().to_module_type().is_err())
672            // Records that have a user-given alias are excluded: their `as.X` cast
673            // is emitted by the inline constructor/validator pipeline. Anonymous
674            // records (auto-named `Record0`, `Record1`, …) have no constructor, so
675            // they still need their `as.RecordN` annotation generated here.
676            .filter(|(var, typ)| {
677                !matches!(typ, Type::Record(_, _)) || is_anonymous_record_name(&var.get_name())
678            })
679            // Aliases whose underlying type still mentions an unresolved generic
680            // (e.g. `type Animator<%T> <- %T & list { ... }`) have no single
681            // fixed runtime class — there's no monomorphization, so a static
682            // `as.Animator` cast can't be generated. Without this, get_class
683            // panics trying to render `%T` as an R class name.
684            .filter(|(_, typ)| !typ.has_generic())
685            .map(|(var, typ)| (typ, var.get_name()))
686            .map(|(typ, name)| {
687                let name0 = if ["Integer", "Character", "Boolean", "Number"]
688                    .iter()
689                    .any(|x| name == *x)
690                {
691                    format!("'{}', ", name)
692                } else {
693                    Default::default()
694                };
695                let class_str = self.get_class(&typ);
696                let prefix = if name0.is_empty() && class_str != format!("'{}'", name) {
697                    format!("'{}', ", name)
698                } else {
699                    name0
700                };
701                format!(
702                    "as.{} <- function(x) x |> struct(c({}{}, {}))",
703                    name,
704                    prefix,
705                    class_str,
706                    self.get_classes(&typ).unwrap()
707                )
708            })
709            .collect::<Vec<_>>()
710            .join("\n")
711    }
712
713    pub fn get_type_anotation(&self, t: &Type) -> String {
714        self.typing_context.get_type_anotation(t)
715    }
716
717    pub fn get_type_anotation_no_parentheses(&self, t: &Type) -> String {
718        self.typing_context.get_type_anotation_no_parentheses(t)
719    }
720
721    pub fn get_classes(&self, t: &Type) -> Option<String> {
722        let res = self
723            .subtypes
724            .get_supertypes(t, self)
725            .iter()
726            .filter(|typ| (*typ).clone().to_module_type().is_err())
727            .filter(|typ| !typ.is_empty())
728            // A supertype that still mentions an unresolved generic (e.g. a
729            // record-kinded `%T` picked up structurally from a generic alias
730            // like `Animator<%T> <- %T & list {...}`) has no R class name —
731            // it's a compile-time-only constraint, never render it.
732            .filter(|typ| !typ.has_generic())
733            .map(|typ| self.get_class(typ))
734            .collect::<Vec<_>>()
735            .join(", ");
736        if res.is_empty() {
737            Some("'None'".to_string())
738        } else {
739            Some(res)
740        }
741    }
742
743    pub fn get_functions(&self, var1: Var) -> Vec<(Var, Type)> {
744        self.typing_context
745            .variables()
746            .filter(|(var2, typ)| {
747                let reduced_type1 = var1.get_type().reduce(self);
748                let reduced_type2 = var2.get_type().reduce(self);
749                var1.get_name() == var2.get_name()
750                    && typ.is_function()
751                    && reduced_type1.is_subtype(&reduced_type2, self).0
752            })
753            .cloned()
754            .collect()
755    }
756
757    pub fn get_all_generic_functions(&self) -> Vec<(Var, Type)> {
758        let res = self
759            .typing_context
760            .variables()
761            .filter(|(_, typ)| typ.is_function())
762            .filter(|(var, _)| not_in_blacklist(&var.get_name()))
763            .filter(|(var, _)| !var.get_type().is_any())
764            .collect::<HashSet<_>>();
765        let mut result: Vec<(Var, Type)> = res
766            .iter()
767            .map(|(var, typ)| (var.clone().add_backticks_if_percent(), typ.clone()))
768            .collect();
769        // Stable order: the list is rendered into generic_functions.R, which
770        // must not reshuffle between builds (HashSet iteration is random).
771        result.sort_by_key(|(var, _)| var.get_name());
772        result
773    }
774
775    pub fn get_first_matching_function(&self, var1: Var) -> Type {
776        let res = self.typing_context.variables().find(|(var2, typ)| {
777            let reduced_type1 = var1.get_type().reduce(self);
778            let reduced_type2 = var2.get_type().reduce(self);
779            var1.get_name() == var2.get_name()
780                && typ.is_function()
781                && (reduced_type1.is_subtype(&reduced_type2, self).0
782                    || reduced_type1.is_upperrank_of(&reduced_type2))
783        });
784        if let Some(res) = res {
785            res.1.clone()
786        } else {
787            self.typing_context
788                .standard_library()
789                .iter()
790                .find(|(var2, _)| var2.get_name() == var1.get_name())
791                .unwrap_or_else(|| {
792                    panic!(
793                        "Can't find var {} in the context:\n {}",
794                        var1,
795                        self.display_typing_context()
796                    )
797                })
798                .1
799                .clone()
800        }
801    }
802
803    pub fn get_matching_typed_functions(&self, var1: Var) -> Vec<Type> {
804        self.typing_context
805            .variables()
806            .filter(|(var2, typ)| {
807                let reduced_type1 = var1.get_type().reduce(self);
808                let reduced_type2 = var2.get_type().reduce(self);
809                var1.get_name() == var2.get_name()
810                    && typ.is_function()
811                    && (reduced_type1.is_subtype(&reduced_type2, self).0
812                        || reduced_type1.is_upperrank_of(&reduced_type2))
813            })
814            .map(|(_, typ)| typ.clone())
815            .collect::<Vec<_>>()
816    }
817
818    pub fn get_matching_untyped_functions(&self, var: Var) -> Result<Vec<Type>, String> {
819        let name1 = var.get_name();
820        let std_lib = self.typing_context.standard_library();
821        let res = std_lib
822            .iter()
823            .find(|(var2, _)| var2.get_name() == name1)
824            .map(|(_, typ)| typ);
825        match res {
826            Some(val) => Ok(vec![val.clone()]),
827            _ => Err(format!(
828                "Can't find var {} in the context:\n {}",
829                var,
830                self.display_typing_context()
831            )),
832        }
833    }
834
835    pub fn get_matching_functions(&self, var: Var) -> Result<Vec<Type>, String> {
836        let res = self.get_matching_typed_functions(var.clone());
837        if res.is_empty() {
838            self.get_matching_untyped_functions(var)
839        } else {
840            Ok(res)
841        }
842    }
843
844    pub fn get_type_from_class(&self, class: &str) -> Type {
845        self.typing_context.get_type_from_class(class)
846    }
847
848    pub fn add_arg_types(&self, params: &[ArgumentType]) -> Context {
849        let param_types = params
850            .iter()
851            .map(|arg_typ| reduce_type(self, &arg_typ.get_type()).for_var())
852            .map(|typ| match typ.to_owned() {
853                Type::Function(typs, _, _) => {
854                    if !typs.is_empty() {
855                        typs[0].get_type()
856                    } else {
857                        typ
858                    }
859                }
860                t => t,
861            })
862            .collect::<Vec<_>>();
863        params
864            .iter()
865            .zip(param_types.clone())
866            .map(|(arg_typ, par_typ): (&ArgumentType, Type)| {
867                (
868                    Var::from_name(&arg_typ.get_argument_str())
869                        .set_type(reduce_type(self, &par_typ)),
870                    reduce_type(self, &arg_typ.get_type()),
871                )
872            })
873            .fold(self.clone(), |cont: Context, (var, typ): (Var, Type)| {
874                cont.clone().push_var_type(var, typ, &cont)
875            })
876    }
877
878    pub fn set_environment(&self, e: Environment) -> Context {
879        Context {
880            config: self.config.set_environment(e),
881            ..self.clone()
882        }
883    }
884
885    pub fn display_typing_context(&self) -> String {
886        let res = self
887            .variables()
888            .chain(self.aliases())
889            .map(|(var, typ)| format!("{} ==> {}", var, typ))
890            .collect::<Vec<_>>()
891            .join("\n");
892        format!("CONTEXT:\n{}", res)
893    }
894
895    pub fn error(&self, msg: String) -> String {
896        format!("{}{}", msg, self.display_typing_context())
897    }
898
899    pub fn push_alias(self, alias_name: String, typ: Type) -> Self {
900        Context {
901            typing_context: self.typing_context.push_alias(alias_name, typ),
902            ..self
903        }
904    }
905
906    /// Register a user-declared `typeconstructor` in the registry.
907    pub fn push_type_constructor(
908        self,
909        name: String,
910        parameters: Vec<Type>,
911        category: ConstructorCategory,
912    ) -> Self {
913        let mut type_constructors = self.type_constructors.clone();
914        // Last declaration wins: drop any previous entry with the same name.
915        type_constructors.retain(|(n, _, _)| n != &name);
916        type_constructors.push((name, parameters, category));
917        Context {
918            type_constructors,
919            ..self
920        }
921    }
922
923    /// Look up a declared `typeconstructor` by name.
924    pub fn get_type_constructor(
925        &self,
926        name: &str,
927    ) -> Option<&(String, Vec<Type>, ConstructorCategory)> {
928        self.type_constructors.iter().find(|(n, _, _)| n == name)
929    }
930
931    /// Register a `type X <- list { ... }` record alias in the whole-program
932    /// registry, regardless of the current module scope. No-op for non-record
933    /// aliases. Last declaration for a given name wins.
934    pub fn push_record_alias(self, name: String, typ: Type) -> Self {
935        if !matches!(typ, Type::Record(_, _)) {
936            return self;
937        }
938        let mut record_aliases = self.record_aliases.clone();
939        record_aliases.retain(|(n, _)| n != &name);
940        record_aliases.push((name, typ));
941        Context {
942            record_aliases,
943            ..self
944        }
945    }
946
947    /// Merge another context's whole-program record-alias registry into this
948    /// one. Used at module boundaries, where the rest of the inner typing
949    /// context is intentionally discarded for encapsulation but this registry
950    /// must still bubble up (see `Lang::Module` in `processes::type_checking`).
951    pub fn merge_record_aliases(self, other: &Context) -> Self {
952        let mut record_aliases = self.record_aliases.clone();
953        for (name, typ) in &other.record_aliases {
954            if !record_aliases.iter().any(|(n, _)| n == name) {
955                record_aliases.push((name.clone(), typ.clone()));
956            }
957        }
958        Context {
959            record_aliases,
960            ..self
961        }
962    }
963
964    /// Record that `method_name` on `type_name` was auto-generated by named type
965    /// embedding (`embed field: Type`), forwarded from `field_name`. See
966    /// `processes::type_checking::embedding`.
967    pub fn push_embedded_method(
968        self,
969        type_name: String,
970        method_name: String,
971        field_name: String,
972    ) -> Self {
973        let mut embedded_methods = self.embedded_methods.clone();
974        embedded_methods.push((type_name, method_name, field_name));
975        Context {
976            embedded_methods,
977            ..self
978        }
979    }
980
981    /// If `method_name` on `type_name` was inherited via named type embedding,
982    /// return the source field name it was forwarded from.
983    pub fn get_embedded_method(&self, type_name: &str, method_name: &str) -> Option<String> {
984        self.embedded_methods
985            .iter()
986            .find(|(t, m, _)| t == type_name && m == method_name)
987            .map(|(_, _, field)| field.clone())
988    }
989
990    pub fn push_alias2(self, alias_var: Var, typ: Type) -> Self {
991        Context {
992            typing_context: self.typing_context.push_alias2(alias_var, typ),
993            ..self
994        }
995    }
996
997    pub fn in_a_project(&self) -> bool {
998        self.config.environment == Environment::Project
999    }
1000
1001    pub fn get_unification_map(
1002        &self,
1003        entered_types: &[Type],
1004        param_types: &[Type],
1005    ) -> Option<UnificationMap> {
1006        let res = entered_types
1007            .iter()
1008            .zip(param_types.iter())
1009            .map(|(val_typ, par_typ)| match_types_to_generic(self, &val_typ.clone(), par_typ))
1010            .collect::<Option<Vec<_>>>();
1011
1012        let val = res
1013            .map(|vec| vec.iter().flatten().cloned().collect::<Vec<_>>())
1014            .map(UnificationMap::new);
1015
1016        val
1017    }
1018
1019    fn s3_type_definition(&self, var: &Var, typ: &Type) -> String {
1020        let first_part = format!("{} <- function(x) x |> ", var.get_name());
1021        match typ {
1022            Type::RClass(v, _) => format!(
1023                "{} struct(c({}))",
1024                first_part,
1025                v.iter().cloned().collect::<Vec<_>>().join(", ")
1026            ),
1027            _ => {
1028                let class = if typ.is_primitive() {
1029                    format!("'{}'", var.get_name())
1030                } else {
1031                    self.get_class(typ)
1032                };
1033                format!("{} struct(c({}))", first_part, class)
1034            }
1035        }
1036    }
1037
1038    fn get_primitive_type_definition(&self) -> Vec<String> {
1039        let primitives = [
1040            ("Integer", builder::integer_type_default()),
1041            ("Character", builder::character_type_default()),
1042            ("Number", builder::number_type()),
1043            ("Boolean", builder::boolean_type()),
1044        ];
1045        let new_context = self.clone().push_types(
1046            &primitives
1047                .iter()
1048                .map(|(_, typ)| typ)
1049                .cloned()
1050                .collect::<Vec<_>>(),
1051        );
1052        primitives
1053            .iter()
1054            .map(|(name, prim)| {
1055                (
1056                    name,
1057                    new_context.get_classes(prim).unwrap(),
1058                    new_context.get_class(prim),
1059                )
1060            })
1061            .map(|(name, cls, cl)| {
1062                format!("{} <- function(x) x |> struct(c({}, {}))", name, cls, cl)
1063            })
1064            .collect::<Vec<_>>()
1065    }
1066
1067    pub fn get_related_functions(&self, typ: &Type, functions: &VarFunction) -> Vec<Lang> {
1068        let names = self.typing_context.get_related_functions(typ);
1069        functions.get_bodies(&names)
1070    }
1071
1072    pub fn get_functions_from_type(&self, typ: &Type) -> Vec<(Var, Type)> {
1073        self.variables()
1074            .filter(|&(var, typ2)| typ2.is_function() && var.get_type() == *typ)
1075            .cloned()
1076            .collect()
1077    }
1078
1079    pub fn get_functions_from_name(&self, name: &str) -> Vec<(Var, Type)> {
1080        self.typing_context
1081            .entries_named(name)
1082            .into_iter()
1083            .filter(|(_, typ2)| typ2.is_function())
1084            .collect()
1085    }
1086
1087    pub fn get_type_definition(&self, _functions: &VarFunction) -> String {
1088        match self.get_target_language() {
1089            TargetLanguage::R => self
1090                .typing_context
1091                .aliases
1092                .iter()
1093                .map(|(var, typ)| self.s3_type_definition(var, typ))
1094                .chain(self.get_primitive_type_definition().iter().cloned())
1095                .collect::<Vec<_>>()
1096                .join("\n"),
1097            TargetLanguage::JS => {
1098                todo!();
1099            }
1100        }
1101    }
1102
1103    pub fn update_variable(self, var: Var) -> Self {
1104        Self {
1105            typing_context: self.typing_context.update_variable(var),
1106            ..self
1107        }
1108    }
1109
1110    pub fn set_target_language(self, language: TargetLanguage) -> Self {
1111        Self {
1112            config: self.config.set_target_language(language),
1113            typing_context: self.typing_context.source(language),
1114            ..self
1115        }
1116    }
1117
1118    pub fn set_default_var_types(self) -> Self {
1119        Self {
1120            typing_context: self.typing_context.set_default_var_types(),
1121            ..self
1122        }
1123    }
1124
1125    pub fn get_target_language(&self) -> TargetLanguage {
1126        self.config.get_target_language()
1127    }
1128
1129    pub fn set_new_aliase_signature(self, alias: &str, related_type: Type) -> Self {
1130        let alias = Var::from_type(alias.parse::<Type>().unwrap()).unwrap();
1131        self.clone().push_alias2(alias, related_type)
1132    }
1133
1134    pub fn extract_module_as_vartype(&self, module_name: &str) -> Self {
1135        let typ = self
1136            .get_type_from_variable(&Var::from_name(module_name))
1137            .expect("The module name was not found");
1138        let empty_context = Context::default();
1139        let new_context = match typ.clone() {
1140            Type::Module(args, _, _) => {
1141                args.iter()
1142                    .rev()
1143                    .map(|arg_type| {
1144                        (
1145                            Var::try_from(arg_type.0.clone()).unwrap(),
1146                            arg_type.1.clone(),
1147                        )
1148                    }) //TODO: Differenciate between pushing variable and
1149                    //aliases
1150                    .fold(empty_context.clone(), |acc, (var, typ)| {
1151                        acc.clone().push_var_type(var, typ, &acc)
1152                    })
1153            }
1154            _ => panic!("{} is not a module", module_name),
1155        };
1156        new_context
1157            .clone()
1158            .push_var_type(Var::from_name(module_name), typ, &new_context)
1159    }
1160
1161    pub fn get_vartype(&self) -> VarType {
1162        self.clone().typing_context
1163    }
1164
1165    pub fn get_environment(&self) -> Environment {
1166        self.config.environment
1167    }
1168    pub fn extend_typing_context(self, var_types: VarType) -> Self {
1169        Self {
1170            typing_context: self.typing_context + var_types,
1171            ..self
1172        }
1173    }
1174}
1175
1176impl Add for Context {
1177    type Output = Self;
1178
1179    fn add(self, other: Self) -> Self::Output {
1180        let mut type_constructors = self.type_constructors;
1181        for (name, params, cat) in other.type_constructors {
1182            type_constructors.retain(|(n, _, _)| n != &name);
1183            type_constructors.push((name, params, cat));
1184        }
1185        let mut interface_constraints = self.interface_constraints;
1186        interface_constraints.extend(other.interface_constraints);
1187        let rigid_counter = self.rigid_counter.max(other.rigid_counter);
1188        let mut test_preamble = self.test_preamble;
1189        test_preamble.extend(other.test_preamble);
1190        let mut record_aliases = self.record_aliases;
1191        for entry in other.record_aliases {
1192            if !record_aliases.contains(&entry) {
1193                record_aliases.push(entry);
1194            }
1195        }
1196        let mut embedded_methods = self.embedded_methods;
1197        for entry in other.embedded_methods {
1198            if !embedded_methods.contains(&entry) {
1199                embedded_methods.push(entry);
1200            }
1201        }
1202        let mut extern_fns = self.extern_fns;
1203        for entry in other.extern_fns {
1204            if !extern_fns.iter().any(|(n, _)| n == &entry.0) {
1205                extern_fns.push(entry);
1206            }
1207        }
1208        let mut import_from_fns = self.import_from_fns;
1209        for entry in other.import_from_fns {
1210            if !import_from_fns.iter().any(|(n, _)| n == &entry.0) {
1211                import_from_fns.push(entry);
1212            }
1213        }
1214        let mut module_inner_contexts = self.module_inner_contexts;
1215        module_inner_contexts.extend(other.module_inner_contexts);
1216        let mut processed_modules = self.processed_modules;
1217        processed_modules.extend(other.processed_modules);
1218        let mut modules_in_progress = self.modules_in_progress;
1219        modules_in_progress.extend(other.modules_in_progress);
1220        Context {
1221            typing_context: self.typing_context + other.typing_context,
1222            subtypes: self.subtypes + other.subtypes,
1223            type_constructors,
1224            interface_constraints,
1225            rigid_counter,
1226            record_aliases,
1227            embedded_methods,
1228            test_preamble,
1229            self_type: None,
1230            extern_fns,
1231            import_from_fns,
1232            config: self.config,
1233            module_inner_contexts,
1234            processed_modules,
1235            modules_in_progress,
1236        }
1237    }
1238}
1239
1240#[cfg(test)]
1241mod tests {
1242    use super::*;
1243
1244    #[test]
1245    fn test_default_context1() {
1246        let context = Context::default();
1247        assert!(!context.display_typing_context().is_empty());
1248    }
1249
1250    #[test]
1251    fn test_record_nests_under_grecord_sentinel() {
1252        let ctx = Context::default();
1253        let rec = builder::record_type(&[("x".to_string(), builder::integer_type_default())]);
1254        let graph = ctx.subtypes.clone().add_type(rec.clone(), &ctx);
1255        let supers = graph.get_supertypes(&rec, &ctx);
1256        assert!(
1257            supers
1258                .iter()
1259                .any(|t| matches!(t, Type::KindedGen(Kind::Record, _, _))),
1260            "a record must nest under the GRecord (%_) sentinel; supers = {:?}",
1261            supers
1262        );
1263        assert!(
1264            supers.iter().any(|t| matches!(t, Type::Generic(_, _))),
1265            "GRecord must itself sit under the bare Generic sentinel; supers = {:?}",
1266            supers
1267        );
1268    }
1269
1270    #[test]
1271    fn test_generic_sentinels_absent_from_r_classes() {
1272        let ctx = Context::default();
1273        let rec = builder::record_type(&[("x".to_string(), builder::integer_type_default())]);
1274        let graph = ctx.subtypes.clone().add_type(rec.clone(), &ctx);
1275        let ctx = ctx.with_subtypes(graph);
1276        let classes = ctx.get_classes(&rec).unwrap();
1277        assert!(
1278            !classes.contains("GRecord") && !classes.contains('%') && !classes.contains("Generic"),
1279            "generic sentinels must be filtered out of generated R classes, got: {}",
1280            classes
1281        );
1282    }
1283}