snarkvm_synthesizer_program/function/
mod.rs1mod input;
17use input::*;
18
19mod output;
20use output::*;
21
22mod bytes;
23mod parse;
24
25use crate::{Instruction, finalize::FinalizeCore};
26use console::{
27 network::prelude::*,
28 program::{Identifier, Register, ValueType, Variant},
29};
30
31use indexmap::IndexSet;
32
33#[derive(Clone, PartialEq, Eq)]
34pub struct FunctionCore<N: Network> {
35 name: Identifier<N>,
37 inputs: IndexSet<Input<N>>,
40 instructions: Vec<Instruction<N>>,
42 outputs: IndexSet<Output<N>>,
44 finalize_logic: Option<FinalizeCore<N>>,
46}
47
48impl<N: Network> FunctionCore<N> {
49 pub fn new(name: Identifier<N>) -> Self {
51 Self { name, inputs: IndexSet::new(), instructions: Vec::new(), outputs: IndexSet::new(), finalize_logic: None }
52 }
53
54 pub const fn name(&self) -> &Identifier<N> {
56 &self.name
57 }
58
59 pub const fn inputs(&self) -> &IndexSet<Input<N>> {
61 &self.inputs
62 }
63
64 pub fn input_types(&self) -> Vec<ValueType<N>> {
66 self.inputs.iter().map(|input| input.value_type()).cloned().collect()
67 }
68
69 pub fn input_variants(&self) -> Vec<Variant> {
71 self.inputs.iter().map(|input| input.value_type().variant()).collect()
72 }
73
74 pub fn instructions(&self) -> &[Instruction<N>] {
76 &self.instructions
77 }
78
79 pub const fn outputs(&self) -> &IndexSet<Output<N>> {
81 &self.outputs
82 }
83
84 pub fn output_types(&self) -> Vec<ValueType<N>> {
86 self.outputs.iter().map(|output| output.value_type()).cloned().collect()
87 }
88
89 pub fn output_variants(&self) -> Vec<Variant> {
91 self.outputs.iter().map(|output| output.value_type().variant()).collect()
92 }
93
94 pub const fn finalize_logic(&self) -> Option<&FinalizeCore<N>> {
96 self.finalize_logic.as_ref()
97 }
98}
99
100impl<N: Network> FunctionCore<N> {
101 #[inline]
109 fn add_input(&mut self, input: Input<N>) -> Result<()> {
110 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
112 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
113
114 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
116 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
118
119 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
121
122 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
124
125 self.inputs.insert(input);
127 Ok(())
128 }
129
130 #[inline]
137 pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
138 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
140
141 ensure!(
143 self.instructions.len() < N::MAX_INSTRUCTIONS,
144 "Cannot add more than {} instructions",
145 N::MAX_INSTRUCTIONS
146 );
147
148 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
150
151 for register in instruction.destinations() {
153 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
154 }
155
156 self.instructions.push(instruction);
158 Ok(())
159 }
160
161 #[inline]
167 fn add_output(&mut self, output: Output<N>) -> Result<()> {
168 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
170 ensure!(!self.outputs.contains(&output), "Cannot add duplicate output statement");
172
173 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
175
176 self.outputs.insert(output);
178 Ok(())
179 }
180
181 #[inline]
189 fn add_finalize(&mut self, finalize: FinalizeCore<N>) -> Result<()> {
190 ensure!(self.finalize_logic.is_none(), "Cannot add multiple finalize scopes to function '{}'", self.name);
192 ensure!(*finalize.name() == self.name, "Finalize scope name must match function name '{}'", self.name);
194 ensure!(finalize.inputs().len() <= N::MAX_INPUTS, "Cannot add more than {} inputs to finalize", N::MAX_INPUTS);
196
197 self.finalize_logic = Some(finalize);
199 Ok(())
200 }
201}
202
203impl<N: Network> TypeName for FunctionCore<N> {
204 #[inline]
206 fn type_name() -> &'static str {
207 "function"
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 use crate::{Function, Instruction};
216
217 type CurrentNetwork = console::network::MainnetV0;
218
219 #[test]
220 fn test_add_input() {
221 let name = Identifier::from_str("function_core_test").unwrap();
223 let mut function = Function::<CurrentNetwork>::new(name);
224
225 let input = Input::<CurrentNetwork>::from_str("input r0 as field.private;").unwrap();
227 assert!(function.add_input(input.clone()).is_ok());
228
229 assert!(function.add_input(input).is_err());
231
232 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
234 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field.private;")).unwrap();
235
236 match function.inputs.len() < CurrentNetwork::MAX_INPUTS {
237 true => assert!(function.add_input(input).is_ok()),
238 false => assert!(function.add_input(input).is_err()),
239 }
240 }
241 }
242
243 #[test]
244 fn test_add_instruction() {
245 let name = Identifier::from_str("function_core_test").unwrap();
247 let mut function = Function::<CurrentNetwork>::new(name);
248
249 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
251 assert!(function.add_instruction(instruction).is_ok());
252
253 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
255 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
256
257 match function.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
258 true => assert!(function.add_instruction(instruction).is_ok()),
259 false => assert!(function.add_instruction(instruction).is_err()),
260 }
261 }
262 }
263
264 #[test]
265 fn test_add_output() {
266 let name = Identifier::from_str("function_core_test").unwrap();
268 let mut function = Function::<CurrentNetwork>::new(name);
269
270 let output = Output::<CurrentNetwork>::from_str("output r0 as field.private;").unwrap();
272 assert!(function.add_output(output).is_ok());
273
274 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
276 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field.private;")).unwrap();
277
278 match function.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
279 true => assert!(function.add_output(output).is_ok()),
280 false => assert!(function.add_output(output).is_err()),
281 }
282 }
283 }
284}