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 output_types(&self) -> Vec<RegisterType<N>> {
75 self.outputs.iter().map(|output| output.register_type()).cloned().collect()
76 }
77
78 pub fn contains_external_struct(&self) -> bool {
80 self.inputs.iter().any(|input| input.register_type().contains_external_struct())
81 || self.outputs.iter().any(|output| output.register_type().contains_external_struct())
82 || self.instructions.iter().any(|instruction| instruction.contains_external_struct())
83 }
84
85 pub fn contains_string_type(&self) -> bool {
88 self.instructions.iter().any(|instruction| instruction.contains_string_type())
89 }
90
91 pub fn contains_identifier_type(&self) -> Result<bool> {
93 for input in &self.inputs {
94 if input.register_type().contains_identifier_type()? {
95 return Ok(true);
96 }
97 }
98 for output in &self.outputs {
99 if output.register_type().contains_identifier_type()? {
100 return Ok(true);
101 }
102 }
103 for instruction in &self.instructions {
105 if instruction.contains_identifier_type()? {
106 return Ok(true);
107 }
108 }
109 Ok(false)
110 }
111
112 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
114 self.inputs.iter().any(|input| input.register_type().exceeds_max_array_size(max_array_size))
115 || self.outputs.iter().any(|output| output.register_type().exceeds_max_array_size(max_array_size))
116 || self.instructions.iter().any(|instruction| instruction.exceeds_max_array_size(max_array_size))
117 }
118}
119
120impl<N: Network> ClosureCore<N> {
121 #[inline]
128 fn add_input(&mut self, input: Input<N>) -> Result<()> {
129 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
131 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
132
133 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
135 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
137
138 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
140
141 self.inputs.insert(input);
143 Ok(())
144 }
145
146 #[inline]
152 pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
153 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
155
156 ensure!(
158 self.instructions.len() < N::MAX_INSTRUCTIONS,
159 "Cannot add more than {} instructions",
160 N::MAX_INSTRUCTIONS
161 );
162
163 for register in instruction.destinations() {
165 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
166 }
167
168 self.instructions.push(instruction);
170 Ok(())
171 }
172
173 #[inline]
178 fn add_output(&mut self, output: Output<N>) -> Result<()> {
179 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
181
182 ensure!(!matches!(output.register_type(), RegisterType::Record(..)), "Closure outputs do not support records");
185
186 self.outputs.insert(output);
188 Ok(())
189 }
190}
191
192impl<N: Network> TypeName for ClosureCore<N> {
193 #[inline]
195 fn type_name() -> &'static str {
196 "closure"
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 use crate::{Closure, Instruction};
205
206 type CurrentNetwork = console::network::MainnetV0;
207
208 #[test]
209 fn test_add_input() {
210 let name = Identifier::from_str("closure_core_test").unwrap();
212 let mut closure = Closure::<CurrentNetwork>::new(name);
213
214 let input = Input::<CurrentNetwork>::from_str("input r0 as field;").unwrap();
216 assert!(closure.add_input(input.clone()).is_ok());
217
218 assert!(closure.add_input(input).is_err());
220
221 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
223 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field;")).unwrap();
224
225 match closure.inputs.len() < CurrentNetwork::MAX_INPUTS {
226 true => assert!(closure.add_input(input).is_ok()),
227 false => assert!(closure.add_input(input).is_err()),
228 }
229 }
230 }
231
232 #[test]
233 fn test_add_instruction() {
234 let name = Identifier::from_str("closure_core_test").unwrap();
236 let mut closure = Closure::<CurrentNetwork>::new(name);
237
238 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
240 assert!(closure.add_instruction(instruction).is_ok());
241
242 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
244 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
245
246 match closure.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
247 true => assert!(closure.add_instruction(instruction).is_ok()),
248 false => assert!(closure.add_instruction(instruction).is_err()),
249 }
250 }
251 }
252
253 #[test]
254 fn test_add_output() {
255 let name = Identifier::from_str("closure_core_test").unwrap();
257 let mut closure = Closure::<CurrentNetwork>::new(name);
258
259 let output = Output::<CurrentNetwork>::from_str("output r0 as field;").unwrap();
261 assert!(closure.add_output(output).is_ok());
262
263 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
265 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field;")).unwrap();
266
267 match closure.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
268 true => assert!(closure.add_output(output).is_ok()),
269 false => assert!(closure.add_output(output).is_err()),
270 }
271 }
272 }
273
274 #[test]
275 fn test_add_output_record_types_rejected() {
276 let name = Identifier::from_str("closure_core_test").unwrap();
277
278 let mut closure = Closure::<CurrentNetwork>::new(name);
280 let output = Output::<CurrentNetwork>::from_str("output r0 as dynamic.record;").unwrap();
281 assert!(closure.add_output(output).is_ok());
282
283 let mut closure = Closure::<CurrentNetwork>::new(name);
285 let output = Output::<CurrentNetwork>::from_str("output r0 as test_prog.aleo/token.record;").unwrap();
286 assert!(closure.add_output(output).is_ok());
287
288 let mut closure = Closure::<CurrentNetwork>::new(name);
290 let output = Output::<CurrentNetwork>::from_str("output r0 as token.record;").unwrap();
291 let result = closure.add_output(output);
292 assert!(result.is_err());
293 assert!(result.unwrap_err().to_string().contains("Closure outputs do not support records"));
294 }
295}