snarkvm_synthesizer_program/function/
mod.rs1mod input;
17use input::*;
18
19mod output;
20use output::*;
21
22mod bytes;
23mod parse;
24
25use crate::{
26 finalize::FinalizeCore,
27 traits::{CommandTrait, InstructionTrait},
28};
29use console::{
30 network::prelude::*,
31 program::{Identifier, Register, ValueType, Variant},
32};
33
34use indexmap::IndexSet;
35
36#[derive(Clone, PartialEq, Eq)]
37pub struct FunctionCore<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> {
38 name: Identifier<N>,
40 inputs: IndexSet<Input<N>>,
43 instructions: Vec<Instruction>,
45 outputs: IndexSet<Output<N>>,
47 finalize_logic: Option<FinalizeCore<N, Command>>,
49}
50
51impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> FunctionCore<N, Instruction, Command> {
52 pub fn new(name: Identifier<N>) -> Self {
54 Self { name, inputs: IndexSet::new(), instructions: Vec::new(), outputs: IndexSet::new(), finalize_logic: None }
55 }
56
57 pub const fn name(&self) -> &Identifier<N> {
59 &self.name
60 }
61
62 pub const fn inputs(&self) -> &IndexSet<Input<N>> {
64 &self.inputs
65 }
66
67 pub fn input_types(&self) -> Vec<ValueType<N>> {
69 self.inputs.iter().map(|input| input.value_type()).cloned().collect()
70 }
71
72 pub fn input_variants(&self) -> Vec<Variant> {
74 self.inputs.iter().map(|input| input.value_type().variant()).collect()
75 }
76
77 pub fn instructions(&self) -> &[Instruction] {
79 &self.instructions
80 }
81
82 pub const fn outputs(&self) -> &IndexSet<Output<N>> {
84 &self.outputs
85 }
86
87 pub fn output_types(&self) -> Vec<ValueType<N>> {
89 self.outputs.iter().map(|output| output.value_type()).cloned().collect()
90 }
91
92 pub fn output_variants(&self) -> Vec<Variant> {
94 self.outputs.iter().map(|output| output.value_type().variant()).collect()
95 }
96
97 pub const fn finalize_logic(&self) -> Option<&FinalizeCore<N, Command>> {
99 self.finalize_logic.as_ref()
100 }
101}
102
103impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> FunctionCore<N, Instruction, Command> {
104 #[inline]
112 fn add_input(&mut self, input: Input<N>) -> Result<()> {
113 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
115 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
116
117 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
119 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
121
122 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
124
125 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
127
128 self.inputs.insert(input);
130 Ok(())
131 }
132
133 #[inline]
140 pub fn add_instruction(&mut self, instruction: Instruction) -> Result<()> {
141 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
143
144 ensure!(
146 self.instructions.len() < N::MAX_INSTRUCTIONS,
147 "Cannot add more than {} instructions",
148 N::MAX_INSTRUCTIONS
149 );
150
151 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
153
154 for register in instruction.destinations() {
156 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
157 }
158
159 self.instructions.push(instruction);
161 Ok(())
162 }
163
164 #[inline]
170 fn add_output(&mut self, output: Output<N>) -> Result<()> {
171 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
173 ensure!(!self.outputs.contains(&output), "Cannot add duplicate output statement");
175
176 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
178
179 self.outputs.insert(output);
181 Ok(())
182 }
183
184 #[inline]
192 fn add_finalize(&mut self, finalize: FinalizeCore<N, Command>) -> Result<()> {
193 ensure!(self.finalize_logic.is_none(), "Cannot add multiple finalize scopes to function '{}'", self.name);
195 ensure!(*finalize.name() == self.name, "Finalize scope name must match function name '{}'", self.name);
197 ensure!(finalize.inputs().len() <= N::MAX_INPUTS, "Cannot add more than {} inputs to finalize", N::MAX_INPUTS);
199
200 self.finalize_logic = Some(finalize);
202 Ok(())
203 }
204}
205
206impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> TypeName
207 for FunctionCore<N, Instruction, Command>
208{
209 #[inline]
211 fn type_name() -> &'static str {
212 "function"
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 use crate::{Function, Instruction};
221
222 type CurrentNetwork = console::network::MainnetV0;
223
224 #[test]
225 fn test_add_input() {
226 let name = Identifier::from_str("function_core_test").unwrap();
228 let mut function = Function::<CurrentNetwork>::new(name);
229
230 let input = Input::<CurrentNetwork>::from_str("input r0 as field.private;").unwrap();
232 assert!(function.add_input(input.clone()).is_ok());
233
234 assert!(function.add_input(input).is_err());
236
237 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
239 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field.private;")).unwrap();
240
241 match function.inputs.len() < CurrentNetwork::MAX_INPUTS {
242 true => assert!(function.add_input(input).is_ok()),
243 false => assert!(function.add_input(input).is_err()),
244 }
245 }
246 }
247
248 #[test]
249 fn test_add_instruction() {
250 let name = Identifier::from_str("function_core_test").unwrap();
252 let mut function = Function::<CurrentNetwork>::new(name);
253
254 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
256 assert!(function.add_instruction(instruction).is_ok());
257
258 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
260 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
261
262 match function.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
263 true => assert!(function.add_instruction(instruction).is_ok()),
264 false => assert!(function.add_instruction(instruction).is_err()),
265 }
266 }
267 }
268
269 #[test]
270 fn test_add_output() {
271 let name = Identifier::from_str("function_core_test").unwrap();
273 let mut function = Function::<CurrentNetwork>::new(name);
274
275 let output = Output::<CurrentNetwork>::from_str("output r0 as field.private;").unwrap();
277 assert!(function.add_output(output).is_ok());
278
279 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
281 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field.private;")).unwrap();
282
283 match function.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
284 true => assert!(function.add_output(output).is_ok()),
285 false => assert!(function.add_output(output).is_err()),
286 }
287 }
288 }
289}