snarkvm_synthesizer_program/closure/
mod.rs1use crate::Instruction;
17
18mod input;
19use input::*;
20
21mod output;
22use output::*;
23
24mod bytes;
25mod parse;
26
27use console::{
28 network::{error, prelude::*},
29 program::{Identifier, Register, RegisterType},
30};
31
32use indexmap::IndexSet;
33
34#[derive(Clone, PartialEq, Eq)]
35pub struct ClosureCore<N: Network> {
36 name: Identifier<N>,
38 inputs: IndexSet<Input<N>>,
41 instructions: Vec<Instruction<N>>,
43 outputs: IndexSet<Output<N>>,
45}
46
47impl<N: Network> ClosureCore<N> {
48 pub fn new(name: Identifier<N>) -> Self {
50 Self { name, inputs: IndexSet::new(), instructions: Vec::new(), outputs: IndexSet::new() }
51 }
52
53 pub const fn name(&self) -> &Identifier<N> {
55 &self.name
56 }
57
58 pub const fn inputs(&self) -> &IndexSet<Input<N>> {
60 &self.inputs
61 }
62
63 pub fn instructions(&self) -> &[Instruction<N>] {
65 &self.instructions
66 }
67
68 pub const fn outputs(&self) -> &IndexSet<Output<N>> {
70 &self.outputs
71 }
72
73 pub fn contains_string_type(&self) -> bool {
76 self.instructions.iter().any(|instruction| instruction.contains_string_type())
77 }
78
79 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
81 self.inputs.iter().any(|input| input.register_type().exceeds_max_array_size(max_array_size))
82 || self.outputs.iter().any(|output| output.register_type().exceeds_max_array_size(max_array_size))
83 || self.instructions.iter().any(|instruction| instruction.exceeds_max_array_size(max_array_size))
84 }
85}
86
87impl<N: Network> ClosureCore<N> {
88 #[inline]
95 fn add_input(&mut self, input: Input<N>) -> Result<()> {
96 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
98 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
99
100 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
102 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
104
105 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
107
108 self.inputs.insert(input);
110 Ok(())
111 }
112
113 #[inline]
119 pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
120 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
122
123 ensure!(
125 self.instructions.len() < N::MAX_INSTRUCTIONS,
126 "Cannot add more than {} instructions",
127 N::MAX_INSTRUCTIONS
128 );
129
130 for register in instruction.destinations() {
132 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
133 }
134
135 self.instructions.push(instruction);
137 Ok(())
138 }
139
140 #[inline]
145 fn add_output(&mut self, output: Output<N>) -> Result<()> {
146 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
148
149 ensure!(!matches!(output.register_type(), RegisterType::Record(..)), "Output register cannot be a record");
151
152 self.outputs.insert(output);
154 Ok(())
155 }
156}
157
158impl<N: Network> TypeName for ClosureCore<N> {
159 #[inline]
161 fn type_name() -> &'static str {
162 "closure"
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 use crate::{Closure, Instruction};
171
172 type CurrentNetwork = console::network::MainnetV0;
173
174 #[test]
175 fn test_add_input() {
176 let name = Identifier::from_str("closure_core_test").unwrap();
178 let mut closure = Closure::<CurrentNetwork>::new(name);
179
180 let input = Input::<CurrentNetwork>::from_str("input r0 as field;").unwrap();
182 assert!(closure.add_input(input.clone()).is_ok());
183
184 assert!(closure.add_input(input).is_err());
186
187 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
189 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field;")).unwrap();
190
191 match closure.inputs.len() < CurrentNetwork::MAX_INPUTS {
192 true => assert!(closure.add_input(input).is_ok()),
193 false => assert!(closure.add_input(input).is_err()),
194 }
195 }
196 }
197
198 #[test]
199 fn test_add_instruction() {
200 let name = Identifier::from_str("closure_core_test").unwrap();
202 let mut closure = Closure::<CurrentNetwork>::new(name);
203
204 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
206 assert!(closure.add_instruction(instruction).is_ok());
207
208 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
210 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
211
212 match closure.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
213 true => assert!(closure.add_instruction(instruction).is_ok()),
214 false => assert!(closure.add_instruction(instruction).is_err()),
215 }
216 }
217 }
218
219 #[test]
220 fn test_add_output() {
221 let name = Identifier::from_str("closure_core_test").unwrap();
223 let mut closure = Closure::<CurrentNetwork>::new(name);
224
225 let output = Output::<CurrentNetwork>::from_str("output r0 as field;").unwrap();
227 assert!(closure.add_output(output).is_ok());
228
229 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
231 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field;")).unwrap();
232
233 match closure.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
234 true => assert!(closure.add_output(output).is_ok()),
235 false => assert!(closure.add_output(output).is_err()),
236 }
237 }
238 }
239}