mago_codex/ttype/resolution.rs
1use mago_atom::Atom;
2use mago_atom::AtomMap;
3use mago_atom::AtomSet;
4use serde::Deserialize;
5use serde::Serialize;
6
7use crate::misc::GenericParent;
8use crate::ttype::union::TUnion;
9
10/// Holds contextual information necessary for resolving generic template types (`@template`).
11///
12/// This context typically includes the definitions of template parameters available in the current scope
13/// (e.g., from class or function `@template` tags) and any concrete types that these templates
14/// have been resolved to (e.g., when a generic class is instantiated or a generic method is called).
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
16pub struct TypeResolutionContext {
17 /// Definitions of template types available in this context, including their constraints.
18 template_definitions: Vec<(Atom, Vec<(GenericParent, TUnion)>)>,
19
20 /// Concrete types that template parameters (often from an outer scope) resolve to
21 /// within this specific context.
22 resolved_template_types: Vec<(Atom, TUnion)>,
23
24 /// Type aliases defined in the current class scope (from @type tags).
25 type_aliases: AtomSet,
26
27 /// Imported type aliases (from @import-type tags).
28 /// Maps local alias name to (source class FQCN, original type name).
29 imported_type_aliases: AtomMap<(Atom, Atom)>,
30}
31
32/// Provides a default, empty type resolution context.
33impl Default for TypeResolutionContext {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl TypeResolutionContext {
40 /// Creates a new, empty `TypeResolutionContext` with no defined or resolved template types.
41 pub fn new() -> Self {
42 Self {
43 template_definitions: vec![],
44 resolved_template_types: vec![],
45 type_aliases: AtomSet::default(),
46 imported_type_aliases: AtomMap::default(),
47 }
48 }
49
50 /// Checks if this context is empty, meaning it has no template definitions or resolved types.
51 #[inline]
52 pub fn is_empty(&self) -> bool {
53 self.template_definitions.is_empty()
54 && self.resolved_template_types.is_empty()
55 && self.type_aliases.is_empty()
56 && self.imported_type_aliases.is_empty()
57 }
58
59 /// Adds a template type definition (e.g., from an `@template T of Constraint` tag).
60 ///
61 /// # Arguments
62 ///
63 /// * `name`: The name of the template parameter (e.g., `"T"`).
64 /// * `constraints`: A list of constraints, each specifying the origin (parent) and the constraint type.
65 pub fn with_template_definition(mut self, name: Atom, constraints: Vec<(GenericParent, TUnion)>) -> Self {
66 self.template_definitions.push((name, constraints));
67 self
68 }
69
70 /// Adds a mapping indicating that a template parameter resolves to a specific concrete type
71 /// within this context.
72 ///
73 /// # Arguments
74 ///
75 /// * `name`: The name of the template parameter (e.g., `"T"`).
76 /// * `resolved_type`: The concrete `TUnion` type that `name` resolves to here.
77 pub fn with_resolved_template_type(mut self, name: Atom, resolved_type: TUnion) -> Self {
78 self.resolved_template_types.push((name, resolved_type));
79 self
80 }
81
82 /// Returns a slice of the defined template parameters and their constraints for this context.
83 #[inline]
84 pub fn get_template_definitions(&self) -> &[(Atom, Vec<(GenericParent, TUnion)>)] {
85 &self.template_definitions
86 }
87
88 /// Returns a mutable slice of the defined template parameters and their constraints for this context.
89 #[inline]
90 pub fn get_template_definitions_mut(&mut self) -> &mut [(Atom, Vec<(GenericParent, TUnion)>)] {
91 &mut self.template_definitions
92 }
93
94 /// Returns a slice of the template parameters that have resolved to concrete types in this context.
95 #[inline]
96 pub fn get_resolved_template_types(&self) -> &[(Atom, TUnion)] {
97 &self.resolved_template_types
98 }
99
100 /// Returns a mutable slice of the template parameters that have resolved to concrete types in this context.
101 #[inline]
102 pub fn get_resolved_template_types_mut(&mut self) -> &mut [(Atom, TUnion)] {
103 &mut self.resolved_template_types
104 }
105
106 /// Looks up the constraints for a specific template parameter defined in this context.
107 ///
108 /// # Arguments
109 ///
110 /// * `name`: The name of the template parameter (e.g., `"T"`) to look up.
111 ///
112 /// # Returns
113 ///
114 /// `Some` containing a reference to the vector of constraints if the template is defined, `None` otherwise.
115 pub fn get_template_definition(&self, name: &str) -> Option<&Vec<(GenericParent, TUnion)>> {
116 self.template_definitions.iter().find(|(n, _)| n == name).map(|(_, constraints)| constraints)
117 }
118
119 /// Checks if a specific template parameter is defined in this context.
120 ///
121 /// # Arguments
122 ///
123 /// * `name`: The name of the template parameter (e.g., `"T"`) to check.
124 ///
125 /// # Returns
126 ///
127 /// `true` if the template parameter is defined, `false` otherwise.
128 pub fn has_template_definition(&self, name: &str) -> bool {
129 self.template_definitions.iter().any(|(n, _)| n == name)
130 }
131
132 /// Adds type aliases from a class to this context.
133 ///
134 /// # Arguments
135 ///
136 /// * `aliases`: A set of type alias names.
137 pub fn with_type_aliases(mut self, aliases: AtomSet) -> Self {
138 self.type_aliases = aliases;
139 self
140 }
141
142 /// Adds a single type alias to this context.
143 ///
144 /// # Arguments
145 ///
146 /// * `name`: The name of the type alias to add.
147 pub fn with_type_alias(mut self, name: Atom) -> Self {
148 self.type_aliases.insert(name);
149 self
150 }
151
152 /// Checks if a specific type alias is defined in this context.
153 ///
154 /// # Arguments
155 ///
156 /// * `name`: The name of the type alias to check.
157 pub fn has_type_alias(&self, name: &Atom) -> bool {
158 self.type_aliases.contains(name)
159 }
160
161 /// Adds an imported type alias to this context.
162 ///
163 /// # Arguments
164 ///
165 /// * `local_name`: The local name of the imported alias (possibly renamed with "as").
166 /// * `source_class`: The FQCN of the class where the type alias is defined.
167 /// * `original_name`: The original name of the type alias in the source class.
168 pub fn with_imported_type_alias(mut self, local_name: Atom, source_class: Atom, original_name: Atom) -> Self {
169 self.imported_type_aliases.insert(local_name, (source_class, original_name));
170 self
171 }
172
173 /// Looks up an imported type alias in this context.
174 ///
175 /// # Arguments
176 ///
177 /// * `name`: The local name of the imported alias to look up.
178 ///
179 /// # Returns
180 ///
181 /// `Some` containing a reference to (source_class, original_name) if found, `None` otherwise.
182 pub fn get_imported_type_alias(&self, name: &Atom) -> Option<&(Atom, Atom)> {
183 self.imported_type_aliases.get(name)
184 }
185
186 /// Checks if a specific imported type alias is defined in this context.
187 ///
188 /// # Arguments
189 ///
190 /// * `name`: The local name of the imported alias to check.
191 pub fn has_imported_type_alias(&self, name: &Atom) -> bool {
192 self.imported_type_aliases.contains_key(name)
193 }
194
195 /// Looks up the concrete type that a specific template parameter resolves to in this context.
196 ///
197 /// # Arguments
198 ///
199 /// * `name`: The name of the template parameter (e.g., `"T"`) to look up.
200 ///
201 /// # Returns
202 ///
203 /// `Some` containing a reference to the resolved `TUnion` type if found, `None` otherwise.
204 /// Note: If multiple entries exist for the same name (due to shadowing or errors),
205 /// this currently returns the first match found.
206 pub fn get_resolved_template_type(&self, name: &str) -> Option<&TUnion> {
207 self.resolved_template_types
208 .iter()
209 // Iterate in reverse if shadowing means the *last* added binding is correct
210 // .rev()
211 .find(|(n, _)| n == name)
212 .map(|(_, resolved_type)| resolved_type)
213 }
214
215 /// Checks if this context contains any template definitions or resolved template types.
216 #[inline]
217 pub fn has_templates(&self) -> bool {
218 !self.template_definitions.is_empty() || !self.resolved_template_types.is_empty()
219 }
220
221 /// Checks if a specific template parameter has a concrete resolved type in this context.
222 #[inline]
223 pub fn is_template_resolved(&self, name: &str) -> bool {
224 self.resolved_template_types.iter().any(|(n, _)| n == name)
225 }
226
227 /// Merges another `TypeResolutionContext` into this one, combining their template definitions
228 /// and resolved types.
229 #[inline]
230 pub fn merge(&mut self, other: TypeResolutionContext) {
231 self.template_definitions.extend(other.template_definitions);
232 self.resolved_template_types.extend(other.resolved_template_types);
233 }
234}