mago_codex/ttype/template/
mod.rs

1use ahash::HashMap;
2use ahash::RandomState;
3use indexmap::IndexMap;
4
5use mago_atom::Atom;
6use mago_span::Span;
7
8use crate::misc::GenericParent;
9use crate::ttype::union::TUnion;
10
11pub mod inferred_type_replacer;
12pub mod standin_type_replacer;
13pub mod variance;
14
15#[derive(Clone, Debug, Default)]
16pub struct TemplateResult {
17    pub template_types: IndexMap<Atom, Vec<(GenericParent, TUnion)>, RandomState>,
18    pub lower_bounds: IndexMap<Atom, HashMap<GenericParent, Vec<TemplateBound>>, RandomState>,
19    pub upper_bounds: IndexMap<Atom, HashMap<GenericParent, TemplateBound>, RandomState>,
20    pub readonly: bool,
21    pub upper_bounds_unintersectable_types: Vec<TUnion>,
22}
23
24impl TemplateResult {
25    pub fn new(
26        template_types: IndexMap<Atom, Vec<(GenericParent, TUnion)>, RandomState>,
27        lower_bounds: IndexMap<Atom, HashMap<GenericParent, TUnion>, RandomState>,
28    ) -> TemplateResult {
29        let mut new_lower_bounds = IndexMap::with_hasher(RandomState::new());
30
31        for (k, v) in lower_bounds {
32            let mut th = HashMap::default();
33
34            for (vk, vv) in v {
35                th.insert(vk, vec![TemplateBound::new(vv, 0, None, None)]);
36            }
37
38            new_lower_bounds.insert(k, th);
39        }
40
41        TemplateResult {
42            template_types,
43            lower_bounds: new_lower_bounds,
44            upper_bounds: IndexMap::with_hasher(RandomState::new()),
45            readonly: false,
46            upper_bounds_unintersectable_types: Vec::new(),
47        }
48    }
49
50    pub fn has_template_types(&self) -> bool {
51        !self.template_types.is_empty()
52    }
53
54    pub fn add_lower_bounds(&mut self, lower_bounds: IndexMap<Atom, HashMap<GenericParent, TUnion>, RandomState>) {
55        for (k, v) in lower_bounds {
56            let mut th = HashMap::default();
57
58            for (vk, vv) in v {
59                th.insert(vk, vec![TemplateBound::new(vv, 0, None, None)]);
60            }
61
62            self.lower_bounds.insert(k, th);
63        }
64    }
65
66    pub fn add_lower_bound(&mut self, parameter_name: Atom, generic_parent: GenericParent, bound: TUnion) {
67        let entry = self.lower_bounds.entry(parameter_name).or_default();
68
69        entry.entry(generic_parent).or_default().push(TemplateBound::new(bound, 0, None, None));
70    }
71
72    pub fn add_template_type(&mut self, parameter_name: Atom, generic_parent: GenericParent, constraint: TUnion) {
73        let entry = self.template_types.entry(parameter_name).or_default();
74        entry.push((generic_parent, constraint));
75    }
76
77    pub fn add_upper_bound(&mut self, parameter_name: Atom, generic_parent: GenericParent, bound: TemplateBound) {
78        let entry = self.upper_bounds.entry(parameter_name).or_default();
79        entry.insert(generic_parent, bound);
80    }
81
82    pub fn add_upper_bound_unintersectable_type(&mut self, bound: TUnion) {
83        self.upper_bounds_unintersectable_types.push(bound);
84    }
85
86    pub fn has_lower_bound(&self, parameter_name: &Atom, generic_parent: &GenericParent) -> bool {
87        self.lower_bounds
88            .get(parameter_name)
89            .and_then(|bounds| bounds.get(generic_parent))
90            .is_some_and(|bounds| !bounds.is_empty())
91    }
92
93    pub fn has_lower_bound_for_class_like(&self, parameter_name: &Atom, classlike_name: &Atom) -> bool {
94        self.has_lower_bound(parameter_name, &GenericParent::ClassLike(*classlike_name))
95    }
96
97    pub fn get_lower_bounds_for_class_like(
98        &self,
99        parameter_name: &Atom,
100        classlike_name: &Atom,
101    ) -> Option<&Vec<TemplateBound>> {
102        self.lower_bounds.get(parameter_name).and_then(|bounds| bounds.get(&GenericParent::ClassLike(*classlike_name)))
103    }
104}
105
106#[derive(Clone, Debug, PartialEq, Eq, Hash)]
107pub struct TemplateBound {
108    pub bound_type: TUnion,
109    pub appearance_depth: usize,
110    pub argument_offset: Option<usize>,
111    pub equality_bound_classlike: Option<Atom>,
112    pub span: Option<Span>,
113}
114
115impl TemplateBound {
116    pub fn new(
117        bound_type: TUnion,
118        appearance_depth: usize,
119        argument_offset: Option<usize>,
120        equality_bound_classlike: Option<Atom>,
121    ) -> Self {
122        Self { bound_type, appearance_depth, argument_offset, equality_bound_classlike, span: None }
123    }
124
125    pub fn of_type(bound_type: TUnion) -> Self {
126        Self { bound_type, appearance_depth: 0, argument_offset: None, equality_bound_classlike: None, span: None }
127    }
128}