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