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