rukalang/elab/
instantiate_functions.rs

1use super::prelude::*;
2
3/// Instantiate one runtime template function specialization.
4///
5/// Input invariant: all specialization bindings correspond to `specialization_key`.
6/// Output invariant: returned function name is registered in specialization/cache maps.
7///
8/// This pass performs specialization cache lookup, recursion guard handling,
9/// concrete function synthesis, signature registration, and body elaboration
10/// for one runtime template instance.
11///
12/// Pass name: `elab.instantiate_runtime_function`.
13struct InstantiateRuntimeFunctionPass<'a> {
14    elaborator: &'a mut Elaborator,
15    callee_name: &'a str,
16    template: &'a RuntimeFunctionTemplate,
17    type_bindings: &'a BTreeMap<String, Ty>,
18    meta_bindings: &'a BTreeMap<String, TemplateMetaBinding>,
19    specialization_key: &'a SpecializationKey,
20    variadic_pack_tys: &'a [Ty],
21}
22
23impl<'a> Pass for InstantiateRuntimeFunctionPass<'a> {
24    type In = ();
25    type Out = String;
26    type Error = ElabError;
27
28    const NAME: &'static str = "elab.instantiate_runtime_function";
29
30    fn run(&mut self, _input: Self::In, _cx: &mut PassContext) -> Result<Self::Out, Self::Error> {
31        if let Some(existing) = self.elaborator.specializations.get(self.specialization_key) {
32            return Ok(existing.clone());
33        }
34        if !self
35            .elaborator
36            .instantiating_functions
37            .insert(self.specialization_key.clone())
38        {
39            return self
40                .elaborator
41                .specializations
42                .get(self.specialization_key)
43                .cloned()
44                .ok_or_else(|| ElabError::ConflictingSpecialization {
45                    name: self.callee_name.to_owned(),
46                });
47        }
48
49        let concrete_name = self.elaborator.next_instance_name(self.callee_name);
50        if self.elaborator.signatures.contains_key(&concrete_name) {
51            return Ok(concrete_name);
52        }
53
54        let mut function = self.template.function.clone();
55        function.name = concrete_name.clone();
56        self.elaborator
57            .specialize_function_params(&mut function, self.variadic_pack_tys)?;
58        self.elaborator.apply_type_bindings_to_function(
59            &mut function,
60            self.type_bindings,
61            &self.template.type_params,
62        )?;
63        self.elaborator
64            .apply_meta_bindings_to_function(&mut function, self.meta_bindings)?;
65        function = expand_function_template_instance(
66            &self.elaborator.program,
67            &function,
68            self.meta_bindings,
69        )?;
70
71        let signature = self.elaborator.resolve_function_signature(
72            &function,
73            &self.template.type_params,
74            self.type_bindings,
75        )?;
76        self.elaborator
77            .signatures
78            .insert(concrete_name.clone(), signature);
79        self.elaborator.elaborate_function_body(&mut function)?;
80        self.elaborator.program.functions.push(function);
81        self.elaborator
82            .specializations
83            .insert(self.specialization_key.clone(), concrete_name.clone());
84        let _ = self
85            .elaborator
86            .instantiating_functions
87            .remove(self.specialization_key);
88        Ok(concrete_name)
89    }
90}
91
92impl Elaborator {
93    /// Instantiate a runtime template function with resolved specialization bindings.
94    pub(super) fn instantiate_runtime_function(
95        &mut self,
96        callee_name: &str,
97        template: &RuntimeFunctionTemplate,
98        type_bindings: &BTreeMap<String, Ty>,
99        meta_bindings: &BTreeMap<String, TemplateMetaBinding>,
100        specialization_key: &SpecializationKey,
101        variadic_pack_tys: &[Ty],
102    ) -> Result<String, ElabError> {
103        let mut pass = InstantiateRuntimeFunctionPass {
104            elaborator: self,
105            callee_name,
106            template,
107            type_bindings,
108            meta_bindings,
109            specialization_key,
110            variadic_pack_tys,
111        };
112        let (output, timings) = Elaborator::run_subpass(&mut pass, ())?;
113        self.record_subpass_timings(&timings);
114        Ok(output)
115    }
116}