1use crate::circuit::gate::{ClassicalDataOp, Instruction};
22use crate::circuit::{
23 Circuit, CircuitError, ClassicalControlOp, ClassicalExpr, ClassicalType, ClassicalValue,
24 ControlBody, Operation,
25};
26use std::collections::{HashMap, HashSet};
27
28#[derive(Clone, Copy, Default)]
29struct ControlContext {
30 break_allowed: bool,
31 continue_allowed: bool,
32}
33
34impl ControlContext {
35 fn enter_loop(self) -> Self {
36 Self {
37 break_allowed: true,
38 continue_allowed: true,
39 }
40 }
41
42 fn enter_switch(self) -> Self {
43 Self {
44 break_allowed: true,
45 continue_allowed: self.continue_allowed,
46 }
47 }
48}
49
50struct ClassicalVerifier<'a> {
51 circuit: &'a Circuit,
52 definitions: HashMap<ClassicalValue, String>,
53}
54
55impl Circuit {
56 pub fn validate(&self) -> Result<(), CircuitError> {
67 self.validate_with_context(ControlContext::default(), true)
68 }
69
70 pub(super) fn validate_builder_state(&self) -> Result<(), CircuitError> {
71 let context = ControlContext {
72 break_allowed: self.control_scope_stack.iter().rev().any(|scope| {
73 matches!(
74 scope,
75 crate::circuit::circuit_classical::ControlScopeKind::Loop
76 | crate::circuit::circuit_classical::ControlScopeKind::Switch
77 )
78 }),
79 continue_allowed: self.control_scope_stack.iter().rev().any(|scope| {
80 matches!(
81 scope,
82 crate::circuit::circuit_classical::ControlScopeKind::Loop
83 )
84 }),
85 };
86 self.validate_with_context(context, false)
87 }
88
89 fn validate_with_context(
90 &self,
91 context: ControlContext,
92 require_all_values_defined: bool,
93 ) -> Result<(), CircuitError> {
94 let mut verifier = ClassicalVerifier {
95 circuit: self,
96 definitions: HashMap::new(),
97 };
98 verifier.verify_operations(self.operations(), HashSet::new(), context, "circuit")?;
99
100 if require_all_values_defined {
101 for (index, ty) in self.classical_values.iter().copied().enumerate() {
102 let value = ClassicalValue::new(self.circuit_id, index as u32, ty);
103 if !verifier.definitions.contains_key(&value) {
104 return Err(CircuitError::UndefinedClassicalValue {
105 index: index as u32,
106 context: "classical value table".to_string(),
107 });
108 }
109 }
110 }
111 Ok(())
112 }
113}
114
115impl ClassicalVerifier<'_> {
116 fn verify_operations(
117 &mut self,
118 operations: &[Operation],
119 mut available: HashSet<ClassicalValue>,
120 context: ControlContext,
121 context_name: &str,
122 ) -> Result<HashSet<ClassicalValue>, CircuitError> {
123 for (index, operation) in operations.iter().enumerate() {
124 match &operation.instruction {
125 Instruction::ClassicalData(op) => {
126 let operation_context = format!("{context_name} operation {index}");
127 self.verify_data_op(op, operation, &mut available, &operation_context)?;
128 }
129 Instruction::ClassicalControl(op) => {
130 let operation_context = format!("{context_name} operation {index}");
131 self.verify_control_op(op, &available, context, &operation_context)?;
132 match op {
133 ClassicalControlOp::Break => {
134 if !context.break_allowed {
135 return Err(CircuitError::InvalidOperation(
136 "break can only be used inside a loop or switch body"
137 .to_string(),
138 ));
139 }
140 if index + 1 != operations.len() {
141 return Err(CircuitError::NonTerminalControlTransfer {
142 operation: "break",
143 context: context_name.to_string(),
144 });
145 }
146 }
147 ClassicalControlOp::Continue => {
148 if !context.continue_allowed {
149 return Err(CircuitError::InvalidOperation(
150 "continue can only be used inside a loop body".to_string(),
151 ));
152 }
153 if index + 1 != operations.len() {
154 return Err(CircuitError::NonTerminalControlTransfer {
155 operation: "continue",
156 context: context_name.to_string(),
157 });
158 }
159 }
160 _ => {}
161 }
162 }
163 _ => {}
164 }
165 }
166 Ok(available)
167 }
168
169 fn verify_data_op(
170 &mut self,
171 op: &ClassicalDataOp,
172 operation: &Operation,
173 available: &mut HashSet<ClassicalValue>,
174 context: &str,
175 ) -> Result<(), CircuitError> {
176 match op {
177 ClassicalDataOp::Store { target, value } => {
178 self.circuit.validate_classical_var(*target)?;
179 self.verify_expr(value, available, context)?;
180 if target.ty() != value.ty() {
181 return Err(CircuitError::InvalidOperation(format!(
182 "store target type {:?} does not match value type {:?}",
183 target.ty(),
184 value.ty()
185 )));
186 }
187 }
188 ClassicalDataOp::MeasureBit { result } => {
189 self.circuit.validate_classical_value(*result)?;
190 if result.ty() != ClassicalType::Bit || operation.qubits.len() != 1 {
191 return Err(CircuitError::InvalidOperation(format!(
192 "measure_bit result {} must be Bit with one qubit",
193 result.index()
194 )));
195 }
196 self.define(*result, available, context)?;
197 }
198 ClassicalDataOp::MeasureBits { result } => {
199 self.circuit.validate_classical_value(*result)?;
200 if result.ty().measurement_width() != Some(operation.qubits.len() as u32) {
201 return Err(CircuitError::InvalidOperation(format!(
202 "measure_bits result {} type {:?} does not match {} qubits",
203 result.index(),
204 result.ty(),
205 operation.qubits.len()
206 )));
207 }
208 self.define(*result, available, context)?;
209 }
210 }
211 Ok(())
212 }
213
214 fn define(
215 &mut self,
216 value: ClassicalValue,
217 available: &mut HashSet<ClassicalValue>,
218 context: &str,
219 ) -> Result<(), CircuitError> {
220 if let Some(first) = self.definitions.insert(value, context.to_string()) {
221 return Err(CircuitError::DuplicateClassicalValueDefinition {
222 index: value.index(),
223 first,
224 second: context.to_string(),
225 });
226 }
227 available.insert(value);
228 Ok(())
229 }
230
231 fn verify_expr(
232 &self,
233 expr: &ClassicalExpr,
234 available: &HashSet<ClassicalValue>,
235 context: &str,
236 ) -> Result<(), CircuitError> {
237 for var in expr.vars() {
238 self.circuit.validate_classical_var(var)?;
239 }
240 for value in expr.values() {
241 self.circuit.validate_classical_value(value)?;
242 if !available.contains(&value) {
243 if self.definitions.contains_key(&value) {
244 return Err(CircuitError::ClassicalValueOutOfScope {
245 index: value.index(),
246 context: context.to_string(),
247 });
248 }
249 return Err(CircuitError::UndefinedClassicalValue {
250 index: value.index(),
251 context: context.to_string(),
252 });
253 }
254 }
255 Ok(())
256 }
257
258 fn verify_body(
259 &mut self,
260 body: &ControlBody,
261 available: &HashSet<ClassicalValue>,
262 context: ControlContext,
263 name: &str,
264 ) -> Result<(), CircuitError> {
265 self.verify_operations(body.operations(), available.clone(), context, name)?;
266 Ok(())
267 }
268
269 fn verify_control_op(
270 &mut self,
271 op: &ClassicalControlOp,
272 available: &HashSet<ClassicalValue>,
273 context: ControlContext,
274 operation_context: &str,
275 ) -> Result<(), CircuitError> {
276 match op {
277 ClassicalControlOp::If(op) => {
278 self.verify_expr(op.condition(), available, operation_context)?;
279 self.verify_body(
280 op.then_body(),
281 available,
282 context,
283 &format!("{operation_context} then body"),
284 )?;
285 if let Some(body) = op.else_body() {
286 self.verify_body(
287 body,
288 available,
289 context,
290 &format!("{operation_context} else body"),
291 )?;
292 }
293 }
294 ClassicalControlOp::While(op) => {
295 self.verify_expr(op.condition(), available, operation_context)?;
296 self.verify_body(
297 op.body(),
298 available,
299 context.enter_loop(),
300 &format!("{operation_context} while body"),
301 )?;
302 }
303 ClassicalControlOp::For(op) => {
304 self.circuit.validate_classical_var(op.var())?;
305 self.verify_expr(op.start(), available, operation_context)?;
306 self.verify_expr(op.stop(), available, operation_context)?;
307 self.verify_expr(op.step(), available, operation_context)?;
308 self.verify_body(
309 op.body(),
310 available,
311 context.enter_loop(),
312 &format!("{operation_context} for body"),
313 )?;
314 }
315 ClassicalControlOp::Switch(op) => {
316 self.verify_expr(op.target(), available, operation_context)?;
317 let body_context = context.enter_switch();
318 for case in op.cases() {
319 self.verify_body(
320 case.body(),
321 available,
322 body_context,
323 &format!("{operation_context} switch case {}", case.value()),
324 )?;
325 }
326 if let Some(body) = op.default() {
327 self.verify_body(
328 body,
329 available,
330 body_context,
331 &format!("{operation_context} switch default"),
332 )?;
333 }
334 }
335 ClassicalControlOp::Break | ClassicalControlOp::Continue => {}
336 }
337 Ok(())
338 }
339}