1use crate::helpers::NodeExt;
7use crate::raw::{Node, NodeKind};
8use crate::symbol::Symbol;
9use crate::types::TypeRef;
10
11#[derive(Clone, Copy)]
13pub struct Specialization<'ctx> {
14 raw: Node<'ctx>,
15}
16
17impl<'ctx> Specialization<'ctx> {
18 pub fn new(raw: Node<'ctx>) -> Self {
20 Self { raw }
21 }
22
23 pub fn raw(&self) -> Node<'ctx> {
25 self.raw
26 }
27
28 pub fn kind(&self) -> SpecializationKind {
30 match self.raw.kind() {
31 NodeKind::GenericSpecialization => SpecializationKind::Generic,
32 NodeKind::GenericSpecializationNotReAbstracted => {
33 SpecializationKind::GenericNotReAbstracted
34 }
35 NodeKind::GenericSpecializationInResilienceDomain => {
36 SpecializationKind::GenericInResilienceDomain
37 }
38 NodeKind::GenericSpecializationPrespecialized => SpecializationKind::Prespecialized,
39 NodeKind::GenericPartialSpecialization => SpecializationKind::Partial,
40 NodeKind::GenericPartialSpecializationNotReAbstracted => {
41 SpecializationKind::PartialNotReAbstracted
42 }
43 NodeKind::FunctionSignatureSpecialization => SpecializationKind::FunctionSignature,
44 _ => SpecializationKind::Other,
45 }
46 }
47
48 pub fn pass_id(&self) -> Option<u64> {
52 self.raw
53 .child_of_kind(NodeKind::SpecializationPassID)
54 .and_then(|c| c.index())
55 }
56
57 pub fn type_arguments(&self) -> Vec<TypeRef<'ctx>> {
62 let mut args = Vec::new();
63 for child in self.raw.children() {
64 if child.kind() == NodeKind::GenericSpecializationParam {
65 for inner in child.children() {
67 if inner.kind() == NodeKind::Type {
68 args.push(TypeRef::new(inner.child(0).unwrap_or(inner)));
69 }
70 }
71 }
72 }
73 args
74 }
75
76 pub fn function_signature_params(&self) -> Vec<FunctionSignatureParam<'ctx>> {
81 let mut params = Vec::new();
82 for child in self.raw.children() {
83 if child.kind() == NodeKind::FunctionSignatureSpecializationParam {
84 params.push(FunctionSignatureParam::new(child));
85 }
86 }
87 params
88 }
89
90 pub fn inner(&self) -> Option<Symbol<'ctx>> {
95 None
100 }
101
102 pub fn module(&self) -> Option<&'ctx str> {
104 for node in self.raw.descendants() {
106 if node.kind() == NodeKind::Module {
107 return node.text();
108 }
109 }
110 None
111 }
112}
113
114impl std::fmt::Debug for Specialization<'_> {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 let mut s = f.debug_struct("Specialization");
117 s.field("kind", &self.kind());
118 s.field("pass_id", &self.pass_id());
119 let type_args = self.type_arguments();
120 if !type_args.is_empty() {
121 s.field("type_arguments", &type_args);
122 }
123 let sig_params = self.function_signature_params();
124 if !sig_params.is_empty() {
125 s.field("function_signature_params", &sig_params);
126 }
127 s.field("module", &self.module());
128 s.finish()
129 }
130}
131
132impl std::fmt::Display for Specialization<'_> {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 write!(f, "{}", self.raw)
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub enum SpecializationKind {
141 Generic,
143 GenericNotReAbstracted,
145 GenericInResilienceDomain,
147 Prespecialized,
149 Partial,
151 PartialNotReAbstracted,
153 FunctionSignature,
155 Other,
157}
158
159impl SpecializationKind {
160 pub fn name(&self) -> &'static str {
162 match self {
163 SpecializationKind::Generic => "generic specialization",
164 SpecializationKind::GenericNotReAbstracted => {
165 "generic specialization (not re-abstracted)"
166 }
167 SpecializationKind::GenericInResilienceDomain => {
168 "generic specialization (resilience domain)"
169 }
170 SpecializationKind::Prespecialized => "pre-specialization",
171 SpecializationKind::Partial => "partial specialization",
172 SpecializationKind::PartialNotReAbstracted => {
173 "partial specialization (not re-abstracted)"
174 }
175 SpecializationKind::FunctionSignature => "function signature specialization",
176 SpecializationKind::Other => "specialization",
177 }
178 }
179}
180
181#[derive(Clone, Copy)]
185pub struct FunctionSignatureParam<'ctx> {
186 raw: Node<'ctx>,
187}
188
189impl<'ctx> FunctionSignatureParam<'ctx> {
190 pub fn new(raw: Node<'ctx>) -> Self {
192 Self { raw }
193 }
194
195 pub fn raw(&self) -> Node<'ctx> {
197 self.raw
198 }
199
200 pub fn kind(&self) -> FunctionSignatureParamKind {
202 for child in self.raw.children() {
203 if child.kind() == NodeKind::FunctionSignatureSpecializationParamKind
204 && let Some(idx) = child.index()
205 {
206 return FunctionSignatureParamKind::from_index(idx);
207 }
208 }
209 FunctionSignatureParamKind::Unknown(0)
210 }
211
212 pub fn flags(&self) -> FunctionSignatureParamFlags {
214 for child in self.raw.children() {
215 if child.kind() == NodeKind::FunctionSignatureSpecializationParamKind
216 && let Some(idx) = child.index()
217 {
218 return FunctionSignatureParamFlags::from_index(idx);
219 }
220 }
221 FunctionSignatureParamFlags::default()
222 }
223
224 pub fn payloads(&self) -> Vec<&'ctx str> {
226 self.raw
227 .children()
228 .filter(|c| c.kind() == NodeKind::FunctionSignatureSpecializationParamPayload)
229 .filter_map(|c| c.text())
230 .collect()
231 }
232
233 pub fn types(&self) -> Vec<TypeRef<'ctx>> {
235 self.raw
236 .children()
237 .filter(|c| c.kind() == NodeKind::Type)
238 .map(|c| TypeRef::new(c.child(0).unwrap_or(c)))
239 .collect()
240 }
241}
242
243impl std::fmt::Debug for FunctionSignatureParam<'_> {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 let mut s = f.debug_struct("FunctionSignatureParam");
246 s.field("kind", &self.kind());
247 let payloads = self.payloads();
248 if !payloads.is_empty() {
249 s.field("payloads", &payloads);
250 }
251 let types = self.types();
252 if !types.is_empty() {
253 s.field("types", &types);
254 }
255 s.finish()
256 }
257}
258
259impl std::fmt::Display for FunctionSignatureParam<'_> {
260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 write!(f, "{}", self.raw)
262 }
263}
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270pub enum FunctionSignatureParamKind {
271 ConstantPropFunction,
273 ConstantPropGlobal,
275 ConstantPropInteger,
277 ConstantPropFloat,
279 ConstantPropString,
281 ClosureProp,
283 BoxToValue,
285 BoxToStack,
287 InOutToOut,
289 ConstantPropKeyPath,
291 Unknown(u64),
293}
294
295impl FunctionSignatureParamKind {
296 fn from_index(idx: u64) -> Self {
298 let base = idx & 0x3F;
300 match base {
301 0 => FunctionSignatureParamKind::ConstantPropFunction,
302 1 => FunctionSignatureParamKind::ConstantPropGlobal,
303 2 => FunctionSignatureParamKind::ConstantPropInteger,
304 3 => FunctionSignatureParamKind::ConstantPropFloat,
305 4 => FunctionSignatureParamKind::ConstantPropString,
306 5 => FunctionSignatureParamKind::ClosureProp,
307 6 => FunctionSignatureParamKind::BoxToValue,
308 7 => FunctionSignatureParamKind::BoxToStack,
309 8 => FunctionSignatureParamKind::InOutToOut,
310 9 => FunctionSignatureParamKind::ConstantPropKeyPath,
311 _ => FunctionSignatureParamKind::Unknown(base),
312 }
313 }
314
315 pub fn name(&self) -> &'static str {
317 match self {
318 FunctionSignatureParamKind::ConstantPropFunction => "constant prop (function)",
319 FunctionSignatureParamKind::ConstantPropGlobal => "constant prop (global)",
320 FunctionSignatureParamKind::ConstantPropInteger => "constant prop (integer)",
321 FunctionSignatureParamKind::ConstantPropFloat => "constant prop (float)",
322 FunctionSignatureParamKind::ConstantPropString => "constant prop (string)",
323 FunctionSignatureParamKind::ClosureProp => "closure propagated",
324 FunctionSignatureParamKind::BoxToValue => "box to value",
325 FunctionSignatureParamKind::BoxToStack => "box to stack",
326 FunctionSignatureParamKind::InOutToOut => "inout to out",
327 FunctionSignatureParamKind::ConstantPropKeyPath => "constant prop (keypath)",
328 FunctionSignatureParamKind::Unknown(_) => "unknown",
329 }
330 }
331}
332
333#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
335pub struct FunctionSignatureParamFlags {
336 pub dead: bool,
338 pub owned_to_guaranteed: bool,
340 pub exploded: bool,
342 pub guaranteed_to_owned: bool,
344 pub existential_to_generic: bool,
346}
347
348impl FunctionSignatureParamFlags {
349 fn from_index(idx: u64) -> Self {
351 Self {
352 dead: (idx & (1 << 6)) != 0,
353 owned_to_guaranteed: (idx & (1 << 7)) != 0,
354 exploded: (idx & (1 << 8)) != 0,
355 guaranteed_to_owned: (idx & (1 << 9)) != 0,
356 existential_to_generic: (idx & (1 << 10)) != 0,
357 }
358 }
359
360 pub fn any(&self) -> bool {
362 self.dead
363 || self.owned_to_guaranteed
364 || self.exploded
365 || self.guaranteed_to_owned
366 || self.existential_to_generic
367 }
368
369 pub fn names(&self) -> Vec<&'static str> {
371 let mut names = Vec::new();
372 if self.dead {
373 names.push("dead");
374 }
375 if self.owned_to_guaranteed {
376 names.push("owned to guaranteed");
377 }
378 if self.exploded {
379 names.push("exploded");
380 }
381 if self.guaranteed_to_owned {
382 names.push("guaranteed to owned");
383 }
384 if self.existential_to_generic {
385 names.push("existential to generic");
386 }
387 names
388 }
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394 use crate::raw::Context;
395 use crate::symbol::SpecializedSymbol;
396
397 #[test]
398 fn test_prespecialization() {
399 let ctx = Context::new();
400 let symbol = Symbol::parse(
402 &ctx,
403 "$sSa16_createNewBuffer14bufferIsUnique15minimumCapacity13growForAppendySb_SiSbtFyXl_Ts5",
404 )
405 .unwrap();
406 assert!(symbol.is_specialization());
407 if let Symbol::Specialization(SpecializedSymbol {
408 specialization,
409 inner,
410 }) = symbol
411 {
412 assert_eq!(specialization.kind(), SpecializationKind::Prespecialized);
413 assert_eq!(specialization.pass_id(), Some(5));
414 let type_args = specialization.type_arguments();
415 assert_eq!(type_args.len(), 1);
416 assert!(inner.is_function());
418 } else {
419 panic!("Expected specialization");
420 }
421 }
422
423 #[test]
424 fn test_function_signature_specialization() {
425 let ctx = Context::new();
426 let symbol = Symbol::parse(
428 &ctx,
429 "_TTSf1cl35_TFF7specgen6callerFSiT_U_FTSiSi_T_Si___TF7specgen12take_closureFFTSiSi_T_T_",
430 )
431 .unwrap();
432 assert!(symbol.is_specialization());
433 if let Symbol::Specialization(SpecializedSymbol {
434 specialization,
435 inner,
436 }) = symbol
437 {
438 assert_eq!(specialization.kind(), SpecializationKind::FunctionSignature);
439 assert_eq!(specialization.pass_id(), Some(1));
440
441 let type_args = specialization.type_arguments();
443 assert!(type_args.is_empty());
444
445 let func_params = specialization.function_signature_params();
446 assert_eq!(func_params.len(), 1);
447
448 let param = &func_params[0];
449 assert_eq!(param.kind(), FunctionSignatureParamKind::ClosureProp);
450 assert!(!param.flags().any()); assert!(!param.payloads().is_empty());
454
455 let types = param.types();
457 assert_eq!(types.len(), 1);
458
459 assert!(inner.is_function());
461 } else {
462 panic!("Expected specialization");
463 }
464 }
465
466 #[test]
467 fn test_nested_function_signature_specialization() {
468 let ctx = Context::new();
469 let symbol = Symbol::parse(
471 &ctx,
472 "_TTSf2dgs___TTSf2s_d___TFVs17_LegacyStringCoreCfVs13_StringBufferS_",
473 )
474 .unwrap();
475 assert!(symbol.is_specialization());
476
477 if let Symbol::Specialization(SpecializedSymbol {
479 specialization,
480 inner,
481 }) = symbol
482 {
483 assert_eq!(specialization.kind(), SpecializationKind::FunctionSignature);
484
485 let func_params = specialization.function_signature_params();
486 assert_eq!(func_params.len(), 1);
487 let flags = func_params[0].flags();
488 assert!(flags.dead);
489 assert!(flags.owned_to_guaranteed);
490 assert!(flags.exploded);
491
492 assert!(inner.is_specialization());
494 if let Symbol::Specialization(SpecializedSymbol {
495 specialization: inner_spec,
496 inner: innermost,
497 }) = inner.as_ref()
498 {
499 assert_eq!(inner_spec.kind(), SpecializationKind::FunctionSignature);
500
501 let inner_params = inner_spec.function_signature_params();
502 assert_eq!(inner_params.len(), 2);
503 assert!(inner_params[0].flags().exploded);
504 assert!(inner_params[1].flags().dead);
505
506 assert!(innermost.is_constructor());
508 } else {
509 panic!("Expected inner specialization");
510 }
511 } else {
512 panic!("Expected specialization");
513 }
514 }
515}