Skip to main content

swift_demangler/
specialization.rs

1//! Specialization symbol representation.
2//!
3//! Specializations are pre-compiled versions of generic functions with
4//! specific type arguments substituted for performance.
5
6use crate::helpers::NodeExt;
7use crate::raw::{Node, NodeKind};
8use crate::symbol::Symbol;
9use crate::types::TypeRef;
10
11/// A Swift specialization symbol.
12#[derive(Clone, Copy)]
13pub struct Specialization<'ctx> {
14    raw: Node<'ctx>,
15}
16
17impl<'ctx> Specialization<'ctx> {
18    /// Create a Specialization from a raw node.
19    pub fn new(raw: Node<'ctx>) -> Self {
20        Self { raw }
21    }
22
23    /// Get the underlying raw node.
24    pub fn raw(&self) -> Node<'ctx> {
25        self.raw
26    }
27
28    /// Get the kind of specialization.
29    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    /// Get the specialization pass ID if present.
49    ///
50    /// This indicates which optimization pass created the specialization.
51    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    /// Get the type arguments used for specialization.
58    ///
59    /// Returns a list of types that were substituted for generic parameters.
60    /// For function signature specializations, use `function_signature_params()` instead.
61    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                // GenericSpecializationParam contains a Type child
66                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    /// Get the function signature specialization parameters.
77    ///
78    /// Returns a list of parameter transformations for function signature specializations.
79    /// For generic specializations, use `type_arguments()` instead.
80    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    /// Get the inner symbol that this specialization wraps.
91    ///
92    /// This is the generic function/method being specialized.
93    /// Returns `None` if the inner symbol cannot be determined.
94    pub fn inner(&self) -> Option<Symbol<'ctx>> {
95        // The inner symbol is a sibling node in the Global parent, not a child
96        // We need to get the parent (Global) and find the function child
97        // Since we don't have parent pointers, we can't do this directly.
98        // Instead, specializations are handled specially in Symbol::from_node
99        None
100    }
101
102    /// Get the module containing this specialization.
103    pub fn module(&self) -> Option<&'ctx str> {
104        // Search descendants for Module
105        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/// The kind of specialization.
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub enum SpecializationKind {
141    /// Full generic specialization.
142    Generic,
143    /// Generic specialization without re-abstraction.
144    GenericNotReAbstracted,
145    /// Generic specialization within a resilience domain.
146    GenericInResilienceDomain,
147    /// Pre-specialized generic (compiled ahead of time).
148    Prespecialized,
149    /// Partial generic specialization.
150    Partial,
151    /// Partial specialization without re-abstraction.
152    PartialNotReAbstracted,
153    /// Function signature specialization (parameter/return type changes).
154    FunctionSignature,
155    /// Other specialization type.
156    Other,
157}
158
159impl SpecializationKind {
160    /// Get a human-readable name for this specialization kind.
161    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/// A parameter in a function signature specialization.
182///
183/// Describes how a function parameter is transformed in the specialization.
184#[derive(Clone, Copy)]
185pub struct FunctionSignatureParam<'ctx> {
186    raw: Node<'ctx>,
187}
188
189impl<'ctx> FunctionSignatureParam<'ctx> {
190    /// Create a FunctionSignatureParam from a raw node.
191    pub fn new(raw: Node<'ctx>) -> Self {
192        Self { raw }
193    }
194
195    /// Get the underlying raw node.
196    pub fn raw(&self) -> Node<'ctx> {
197        self.raw
198    }
199
200    /// Get the base kind of parameter transformation.
201    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    /// Get the option flags for this parameter transformation.
213    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    /// Get all payload texts (e.g., encoding and value for ConstantPropString).
225    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    /// Get the type arguments for this parameter transformation.
234    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/// The base kind of function signature parameter transformation.
266///
267/// This represents the low bits (0-5) of the param kind value.
268/// Additional flags (Dead, OwnedToGuaranteed, etc.) can be combined.
269#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270pub enum FunctionSignatureParamKind {
271    /// Function constant propagation.
272    ConstantPropFunction,
273    /// Global constant propagation.
274    ConstantPropGlobal,
275    /// Integer constant propagation.
276    ConstantPropInteger,
277    /// Float constant propagation.
278    ConstantPropFloat,
279    /// String constant propagation.
280    ConstantPropString,
281    /// Closure parameter was propagated/inlined.
282    ClosureProp,
283    /// Box converted to value.
284    BoxToValue,
285    /// Box converted to stack allocation.
286    BoxToStack,
287    /// In-out parameter converted to out.
288    InOutToOut,
289    /// KeyPath constant propagation.
290    ConstantPropKeyPath,
291    /// Unknown base kind.
292    Unknown(u64),
293}
294
295impl FunctionSignatureParamKind {
296    /// Convert from the raw index value (low 6 bits) to a base param kind.
297    fn from_index(idx: u64) -> Self {
298        // Extract low 6 bits for base kind
299        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    /// Get a human-readable name for this param kind.
316    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/// Option flags that can be combined with the base param kind.
334#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
335pub struct FunctionSignatureParamFlags {
336    /// Parameter was found dead (unused) and removed.
337    pub dead: bool,
338    /// Owned parameter converted to guaranteed (borrowed).
339    pub owned_to_guaranteed: bool,
340    /// Exploded (SROA - Scalar Replacement of Aggregates) - struct/tuple split into fields.
341    pub exploded: bool,
342    /// Guaranteed parameter converted to owned.
343    pub guaranteed_to_owned: bool,
344    /// Existential parameter specialized to concrete type.
345    pub existential_to_generic: bool,
346}
347
348impl FunctionSignatureParamFlags {
349    /// Extract flags from the raw index value (bits 6+).
350    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    /// Returns true if any flag is set.
361    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    /// Get a list of flag names that are set.
370    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        // generic pre-specialization <Swift.AnyObject> of Array._createNewBuffer
401        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            // Inner should be a function
417            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        // function signature specialization with closure propagation
427        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            // Should have function signature params, not type arguments
442            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()); // No flags set
451
452            // Should have payload (the closure mangled name)
453            assert!(!param.payloads().is_empty());
454
455            // Should have one type argument (Int)
456            let types = param.types();
457            assert_eq!(types.len(), 1);
458
459            // Inner should be a function
460            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        // Nested specialization: spec of spec of constructor
470        let symbol = Symbol::parse(
471            &ctx,
472            "_TTSf2dgs___TTSf2s_d___TFVs17_LegacyStringCoreCfVs13_StringBufferS_",
473        )
474        .unwrap();
475        assert!(symbol.is_specialization());
476
477        // Outer specialization
478        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            // Inner should also be a specialization
493            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                // Innermost should be a constructor
507                assert!(innermost.is_constructor());
508            } else {
509                panic!("Expected inner specialization");
510            }
511        } else {
512            panic!("Expected specialization");
513        }
514    }
515}