1mod scope;
9
10pub use scope::{InScope, PassScope, Preserve};
11
12use std::{error::Error, marker::PhantomData};
13
14use hugr_core::core::HugrNode;
15use hugr_core::hugr::{ValidationError, hugrmut::HugrMut};
16use itertools::Either;
17
18#[deprecated(
26 note = "`hugr-passes` is deprecated. Use tket::passes instead",
27 since = "0.26.2"
28)]
29pub trait ComposablePass<H: HugrMut>: WithScope + Sized {
30 type Error: Error;
32 type Result; fn run(&self, hugr: &mut H) -> Result<Self::Result, Self::Error>;
37
38 fn map_err<E2: Error>(
41 self,
42 f: impl Fn(Self::Error) -> E2,
43 ) -> impl ComposablePass<H, Result = Self::Result, Error = E2> {
44 ErrMapper::new(self, f)
45 }
46
47 fn then<P: ComposablePass<H>, E: ErrorCombiner<Self::Error, P::Error>>(
59 self,
60 other: P,
61 ) -> impl ComposablePass<H, Result = (Self::Result, P::Result), Error = E> {
62 struct Sequence<E, P1, P2>(P1, P2, PhantomData<E>);
63 impl<H, E, P1, P2> ComposablePass<H> for Sequence<E, P1, P2>
64 where
65 H: HugrMut,
66 P1: ComposablePass<H>,
67 P2: ComposablePass<H>,
68 E: ErrorCombiner<P1::Error, P2::Error>,
69 {
70 type Error = E;
71 type Result = (P1::Result, P2::Result);
72
73 fn run(&self, hugr: &mut H) -> Result<Self::Result, Self::Error> {
74 let res1 = self.0.run(hugr).map_err(E::from_first)?;
75 let res2 = self.1.run(hugr).map_err(E::from_second)?;
76 Ok((res1, res2))
77 }
78 }
79 impl<E, P1, P2> WithScope for Sequence<E, P1, P2>
80 where
81 P1: WithScope,
82 P2: WithScope,
83 {
84 fn with_scope(self, scope: impl Into<PassScope>) -> Self {
85 let scope = scope.into();
86 Self(
87 self.0.with_scope(scope.clone()),
88 self.1.with_scope(scope),
89 PhantomData,
90 )
91 }
92 }
93
94 Sequence(self, other, PhantomData)
95 }
96}
97
98#[deprecated(
101 note = "`hugr-passes` is deprecated. Use tket::passes instead",
102 since = "0.26.2"
103)]
104pub trait WithScope {
105 fn with_scope(self, scope: impl Into<PassScope>) -> Self;
111
112 #[must_use]
116 fn default_with_scope(scope: PassScope) -> Self
117 where
118 Self: Default,
119 {
120 Self::default().with_scope(scope)
121 }
122}
123
124#[deprecated(
127 note = "`hugr-passes` is deprecated. Use tket::passes instead",
128 since = "0.26.2"
129)]
130pub trait ErrorCombiner<A, B>: Error {
131 fn from_first(a: A) -> Self;
133 fn from_second(b: B) -> Self;
135}
136
137impl<A: Error, B: Into<A>> ErrorCombiner<A, B> for A {
138 fn from_first(a: A) -> Self {
139 a
140 }
141
142 fn from_second(b: B) -> Self {
143 b.into()
144 }
145}
146
147impl<A: Error, B: Error> ErrorCombiner<A, B> for Either<A, B> {
148 fn from_first(a: A) -> Self {
149 Either::Left(a)
150 }
151
152 fn from_second(b: B) -> Self {
153 Either::Right(b)
154 }
155}
156
157struct ErrMapper<P, H, E, F>(P, F, PhantomData<(E, H)>);
168
169impl<H: HugrMut, P: ComposablePass<H>, E: Error, F: Fn(P::Error) -> E> ErrMapper<P, H, E, F> {
170 fn new(pass: P, err_fn: F) -> Self {
171 Self(pass, err_fn, PhantomData)
172 }
173}
174
175impl<P: ComposablePass<H>, H: HugrMut, E: Error, F: Fn(P::Error) -> E> ComposablePass<H>
176 for ErrMapper<P, H, E, F>
177{
178 type Error = E;
179 type Result = P::Result;
180
181 fn run(&self, hugr: &mut H) -> Result<P::Result, Self::Error> {
182 self.0.run(hugr).map_err(&self.1)
183 }
184}
185
186impl<P: ComposablePass<H>, H: HugrMut, E: Error, F: Fn(P::Error) -> E> WithScope
187 for ErrMapper<P, H, E, F>
188{
189 fn with_scope(self, scope: impl Into<PassScope>) -> Self {
190 Self(self.0.with_scope(scope), self.1, PhantomData)
191 }
192}
193
194#[derive(thiserror::Error, Debug)]
198#[deprecated(
199 note = "`hugr-passes` is deprecated. Use tket::passes instead",
200 since = "0.26.2"
201)]
202pub enum ValidatePassError<N, E>
203where
204 N: HugrNode + 'static,
205{
206 #[error("Failed to validate input HUGR: {err}\n{pretty_hugr}")]
208 Input {
209 #[source]
211 err: Box<ValidationError<N>>,
212 pretty_hugr: String,
214 },
215 #[error("Failed to validate output HUGR: {err}\n{pretty_hugr}")]
217 Output {
218 #[source]
220 err: Box<ValidationError<N>>,
221 pretty_hugr: String,
223 },
224 #[error(transparent)]
226 Underlying(Box<E>),
227}
228
229impl<N: HugrNode, E> From<E> for ValidatePassError<N, E> {
230 fn from(err: E) -> Self {
231 Self::Underlying(Box::new(err))
232 }
233}
234
235#[deprecated(
238 note = "`hugr-passes` is deprecated. Use tket::passes instead",
239 since = "0.26.2"
240)]
241pub struct ValidatingPass<P, H>(P, PhantomData<H>);
242
243impl<P: ComposablePass<H>, H: HugrMut> ValidatingPass<P, H> {
244 pub fn new(underlying: P) -> Self {
246 Self(underlying, PhantomData)
247 }
248
249 fn validation_impl<E>(
250 &self,
251 hugr: &H,
252 mk_err: impl FnOnce(ValidationError<H::Node>, String) -> ValidatePassError<H::Node, E>,
253 ) -> Result<(), ValidatePassError<H::Node, E>> {
254 hugr.validate()
255 .map_err(|err| mk_err(err, hugr.mermaid_string()))
256 }
257}
258
259impl<P: ComposablePass<H>, H: HugrMut> ComposablePass<H> for ValidatingPass<P, H>
260where
261 H::Node: 'static,
262{
263 type Error = ValidatePassError<H::Node, P::Error>;
264 type Result = P::Result;
265
266 fn run(&self, hugr: &mut H) -> Result<P::Result, Self::Error> {
267 self.validation_impl(hugr, |err, pretty_hugr| ValidatePassError::Input {
268 err: Box::new(err),
269 pretty_hugr,
270 })?;
271 let res = self.0.run(hugr)?;
272 self.validation_impl(hugr, |err, pretty_hugr| ValidatePassError::Output {
273 err: Box::new(err),
274 pretty_hugr,
275 })?;
276 Ok(res)
277 }
278}
279
280impl<P: ComposablePass<H>, H: HugrMut> WithScope for ValidatingPass<P, H> {
281 fn with_scope(self, scope: impl Into<PassScope>) -> Self {
282 Self(self.0.with_scope(scope), self.1)
283 }
284}
285
286#[deprecated(
291 note = "`hugr-passes` is deprecated. Use tket::passes instead",
292 since = "0.26.2"
293)]
294pub struct IfThen<E, H, A, B>(A, B, PhantomData<(E, H)>);
295
296impl<
297 A: ComposablePass<H, Result = bool>,
298 B: ComposablePass<H>,
299 H: HugrMut,
300 E: ErrorCombiner<A::Error, B::Error>,
301> IfThen<E, H, A, B>
302{
303 pub fn new(fst: A, opt_snd: B) -> Self {
306 Self(fst, opt_snd, PhantomData)
307 }
308}
309
310impl<
311 A: ComposablePass<H, Result = bool>,
312 B: ComposablePass<H>,
313 H: HugrMut,
314 E: ErrorCombiner<A::Error, B::Error>,
315> ComposablePass<H> for IfThen<E, H, A, B>
316{
317 type Error = E;
318 type Result = Option<B::Result>;
319
320 fn run(&self, hugr: &mut H) -> Result<Self::Result, Self::Error> {
321 let res: bool = self.0.run(hugr).map_err(ErrorCombiner::from_first)?;
322 res.then(|| self.1.run(hugr).map_err(ErrorCombiner::from_second))
323 .transpose()
324 }
325}
326
327impl<E, H, A, B> WithScope for IfThen<E, H, A, B>
328where
329 A: WithScope,
330 B: WithScope,
331{
332 fn with_scope(self, scope: impl Into<PassScope>) -> Self {
333 let scope = scope.into();
334 Self(
335 self.0.with_scope(scope.clone()),
336 self.1.with_scope(scope),
337 PhantomData,
338 )
339 }
340}
341
342#[cfg(test)]
343pub(crate) mod test {
344 use hugr_core::ops::Value;
345 use hugr_core::ops::dataflow::IOTrait;
346 use itertools::{Either, Itertools};
347
348 use hugr_core::builder::{
349 Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder,
350 };
351 use hugr_core::extension::prelude::{ConstUsize, MakeTuple, UnpackTuple, bool_t, usize_t};
352 use hugr_core::hugr::hugrmut::HugrMut;
353 use hugr_core::ops::{DFG, Input, OpType, Output, handle::NodeHandle};
354 use hugr_core::std_extensions::arithmetic::int_types::INT_TYPES;
355 use hugr_core::types::{Signature, TypeRow};
356 use hugr_core::{Hugr, HugrView, IncomingPort, Node};
357
358 use crate::composable::WithScope;
359 use crate::const_fold::{ConstFoldError, ConstantFoldPass};
360 use crate::dead_code::DeadCodeElimError;
361 use crate::untuple::UntupleResult;
362 use crate::{DeadCodeElimPass, PassScope, ReplaceTypes, UntuplePass};
363
364 use super::{ComposablePass, IfThen, ValidatePassError, ValidatingPass};
365
366 pub(crate) fn run_validating<P: ComposablePass<H>, H: HugrMut>(
367 pass: P,
368 hugr: &mut H,
369 ) -> Result<P::Result, ValidatePassError<H::Node, P::Error>> {
370 ValidatingPass::new(pass).run(hugr)
371 }
372
373 #[test]
374 fn test_then() {
375 let mut mb = ModuleBuilder::new();
376 let id1 = mb
377 .define_function("id1", Signature::new_endo([usize_t()]))
378 .unwrap();
379 let inps = id1.input_wires();
380 let id1 = id1.finish_with_outputs(inps).unwrap();
381 let id2 = mb
382 .define_function("id2", Signature::new_endo([usize_t()]))
383 .unwrap();
384 let inps = id2.input_wires();
385 let id2 = id2.finish_with_outputs(inps).unwrap();
386 let hugr = mb.finish_hugr().unwrap();
387
388 let c_usz = Value::from(ConstUsize::new(2));
389 let not_a_node = Node::from(portgraph::NodeIndex::new(0xFFFF));
390 assert!(!hugr.contains_node(not_a_node));
391 let dce = DeadCodeElimPass::default().with_entry_points([not_a_node]);
392 let cfold = ConstantFoldPass::default().with_inputs(id2.node(), [(0, c_usz.clone())]);
393
394 cfold.run(&mut hugr.clone()).unwrap();
395
396 let dce_err = DeadCodeElimError::NodeNotFound(not_a_node);
397 let r: Result<_, Either<DeadCodeElimError, ConstFoldError>> =
398 dce.clone().then(cfold.clone()).run(&mut hugr.clone());
399 assert_eq!(r, Err(Either::Left(dce_err.clone())));
400
401 let r: Result<_, Either<_, _>> = cfold
402 .clone()
403 .with_inputs(id1.node(), [(0, c_usz)])
404 .then(dce.clone())
405 .run(&mut hugr.clone());
406 assert_eq!(r, Err(Either::Right(dce_err)));
407
408 let r = dce
410 .map_err(|e| match e {
411 DeadCodeElimError::NodeNotFound(node) => ConstFoldError::MissingEntryPoint { node },
412 })
413 .then(cfold.clone())
414 .run(&mut hugr.clone());
415 assert_eq!(
416 r,
417 Err(ConstFoldError::MissingEntryPoint { node: not_a_node })
418 );
419
420 let v = ValidatingPass::new(cfold.clone());
422 let r: Result<_, ValidatePassError<Node, ConstFoldError>> =
423 v.then(cfold).run(&mut hugr.clone());
424 r.unwrap();
425 }
426
427 #[test]
428 fn test_validation() {
429 let mut h = Hugr::new_with_entrypoint(DFG {
430 signature: Signature::new([usize_t()], [bool_t()]),
431 })
432 .unwrap();
433 let inp = h.add_node_with_parent(h.entrypoint(), Input::new([usize_t()]));
434 let outp = h.add_node_with_parent(h.entrypoint(), Output::new([bool_t()]));
435 h.connect(inp, 0, outp, 0);
436 let backup = h.clone();
437 let err = backup.validate().unwrap_err();
438
439 let no_inputs: [(IncomingPort, _); 0] = [];
440 let cfold = ConstantFoldPass::default().with_inputs(backup.entrypoint(), no_inputs);
441 cfold.run(&mut h).unwrap();
442 assert_eq!(h, backup); let r = ValidatingPass::new(cfold).run(&mut h);
445 assert!(matches!(r, Err(ValidatePassError::Input { err: e, .. }) if *e == err));
446 }
447
448 #[test]
449 fn test_if_then() {
450 let tr = TypeRow::from(vec![usize_t(); 2]);
451
452 let h = {
453 let sig = Signature::new_endo(tr.clone());
454 let mut fb = FunctionBuilder::new("tupuntup", sig).unwrap();
455 let [a, b] = fb.input_wires_arr();
456 let tup = fb
457 .add_dataflow_op(MakeTuple::new(tr.clone()), [a, b])
458 .unwrap();
459 let untup = fb
460 .add_dataflow_op(UnpackTuple::new(tr.clone()), tup.outputs())
461 .unwrap();
462 fb.finish_hugr_with_outputs(untup.outputs()).unwrap()
463 };
464
465 let untup = UntuplePass::default().with_scope(PassScope::EntrypointRecursive);
466 {
467 let mut repl = ReplaceTypes::default();
469 let usize_custom_t = usize_t().as_extension().unwrap().clone();
470 repl.set_replace_type(usize_custom_t, INT_TYPES[6].clone());
471 let ifthen = IfThen::<Either<_, _>, _, _, _>::new(repl, untup.clone());
472
473 let mut h = h.clone();
474 let r = run_validating(ifthen, &mut h).unwrap();
475 assert_eq!(
476 r,
477 Some(UntupleResult {
478 rewrites_applied: 1
479 })
480 );
481 let [tuple_in, tuple_out] = h.children(h.entrypoint()).collect_array().unwrap();
482 assert_eq!(h.output_neighbours(tuple_in).collect_vec(), [tuple_out; 2]);
483 }
484
485 let mut repl = ReplaceTypes::default();
487 let i32_custom_t = INT_TYPES[5].as_extension().unwrap().clone();
488 repl.set_replace_type(i32_custom_t, INT_TYPES[6].clone());
489 let ifthen = IfThen::<Either<_, _>, _, _, _>::new(repl, untup);
490 let mut h = h;
491 let r = run_validating(ifthen, &mut h).unwrap();
492 assert_eq!(r, None);
493 assert_eq!(h.children(h.entrypoint()).count(), 4);
494 let mktup = h
495 .output_neighbours(h.first_child(h.entrypoint()).unwrap())
496 .next()
497 .unwrap();
498 assert_eq!(h.get_optype(mktup), &OpType::from(MakeTuple::new(tr)));
499 }
500}