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