Skip to content

Commit b63aabc

Browse files
acl-cqcdoug-qss2165
authored
feat!: Add monomorphization pass (#1733)
Closes #1630 #1772 tracks adding tests for `substitute`. BREAKING CHANGE: `Optrait` gains `Sized` and `Clone` as supertraits and is no longer object safe. BREAKING CHANGE: `DataflowOptrait` gains `Sized` as supertrait. BREAKING CHANGE: `DataflowOptrait` has an additional required method `substitute`. --------- Co-authored-by: Douglas Wilson <[email protected]> Co-authored-by: Douglas Wilson <[email protected]> Co-authored-by: Seyon Sivarajah <[email protected]>
1 parent ff75a2f commit b63aabc

File tree

12 files changed

+992
-10
lines changed

12 files changed

+992
-10
lines changed

hugr-core/src/ops.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::borrow::Cow;
1616

1717
use crate::extension::simple_op::MakeExtensionOp;
1818
use crate::extension::{ExtensionId, ExtensionRegistry, ExtensionSet};
19-
use crate::types::{EdgeKind, Signature};
19+
use crate::types::{EdgeKind, Signature, Substitution};
2020
use crate::{Direction, OutgoingPort, Port};
2121
use crate::{IncomingPort, PortIndex};
2222
use derive_more::Display;
@@ -369,7 +369,7 @@ pub trait StaticTag {
369369

370370
#[enum_dispatch]
371371
/// Trait implemented by all OpType variants.
372-
pub trait OpTrait {
372+
pub trait OpTrait: Sized + Clone {
373373
/// A human-readable description of the operation.
374374
fn description(&self) -> &str;
375375

@@ -433,6 +433,12 @@ pub trait OpTrait {
433433
}
434434
.is_some() as usize
435435
}
436+
437+
/// Apply a type-level substitution to this OpType, i.e. replace
438+
/// [type variables](crate::types::TypeArg::new_var_use) with new types.
439+
fn substitute(&self, _subst: &Substitution) -> Self {
440+
self.clone()
441+
}
436442
}
437443

438444
/// Properties of child graphs of ops, if the op has children.

hugr-core/src/ops/constant.rs

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ impl OpTrait for Const {
9292
fn static_output(&self) -> Option<EdgeKind> {
9393
Some(EdgeKind::Const(self.get_type()))
9494
}
95+
96+
// Constants cannot refer to TypeArgs of the enclosing Hugr, so no substitute().
9597
}
9698

9799
impl From<Const> for Value {

hugr-core/src/ops/controlflow.rs

+142
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ impl DataflowOpTrait for TailLoop {
4141
Signature::new(inputs, outputs).with_extension_delta(self.extension_delta.clone()),
4242
)
4343
}
44+
45+
fn substitute(&self, subst: &crate::types::Substitution) -> Self {
46+
Self {
47+
just_inputs: self.just_inputs.substitute(subst),
48+
just_outputs: self.just_outputs.substitute(subst),
49+
rest: self.rest.substitute(subst),
50+
extension_delta: self.extension_delta.substitute(subst),
51+
}
52+
}
4453
}
4554

4655
impl TailLoop {
@@ -111,6 +120,15 @@ impl DataflowOpTrait for Conditional {
111120
.with_extension_delta(self.extension_delta.clone()),
112121
)
113122
}
123+
124+
fn substitute(&self, subst: &crate::types::Substitution) -> Self {
125+
Self {
126+
sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst)).collect(),
127+
other_inputs: self.other_inputs.substitute(subst),
128+
outputs: self.outputs.substitute(subst),
129+
extension_delta: self.extension_delta.substitute(subst),
130+
}
131+
}
114132
}
115133

116134
impl Conditional {
@@ -140,6 +158,12 @@ impl DataflowOpTrait for CFG {
140158
fn signature(&self) -> Cow<'_, Signature> {
141159
Cow::Borrowed(&self.signature)
142160
}
161+
162+
fn substitute(&self, subst: &crate::types::Substitution) -> Self {
163+
Self {
164+
signature: self.signature.substitute(subst),
165+
}
166+
}
143167
}
144168

145169
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
@@ -223,6 +247,15 @@ impl OpTrait for DataflowBlock {
223247
Direction::Outgoing => self.sum_rows.len(),
224248
}
225249
}
250+
251+
fn substitute(&self, subst: &crate::types::Substitution) -> Self {
252+
Self {
253+
inputs: self.inputs.substitute(subst),
254+
other_outputs: self.other_outputs.substitute(subst),
255+
sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst)).collect(),
256+
extension_delta: self.extension_delta.substitute(subst),
257+
}
258+
}
226259
}
227260

228261
impl OpTrait for ExitBlock {
@@ -248,6 +281,12 @@ impl OpTrait for ExitBlock {
248281
Direction::Outgoing => 0,
249282
}
250283
}
284+
285+
fn substitute(&self, subst: &crate::types::Substitution) -> Self {
286+
Self {
287+
cfg_outputs: self.cfg_outputs.substitute(subst),
288+
}
289+
}
251290
}
252291

253292
/// Functionality shared by DataflowBlock and Exit CFG block types.
@@ -311,6 +350,12 @@ impl OpTrait for Case {
311350
fn tag(&self) -> OpTag {
312351
<Self as StaticTag>::TAG
313352
}
353+
354+
fn substitute(&self, subst: &crate::types::Substitution) -> Self {
355+
Self {
356+
signature: self.signature.substitute(subst),
357+
}
358+
}
314359
}
315360

316361
impl Case {
@@ -324,3 +369,100 @@ impl Case {
324369
&self.signature.output
325370
}
326371
}
372+
373+
#[cfg(test)]
374+
mod test {
375+
use crate::{
376+
extension::{
377+
prelude::{qb_t, usize_t, PRELUDE_ID},
378+
ExtensionSet, PRELUDE_REGISTRY,
379+
},
380+
ops::{Conditional, DataflowOpTrait, DataflowParent},
381+
types::{Signature, Substitution, Type, TypeArg, TypeBound, TypeRV},
382+
};
383+
384+
use super::{DataflowBlock, TailLoop};
385+
386+
#[test]
387+
fn test_subst_dataflow_block() {
388+
use crate::ops::OpTrait;
389+
let tv0 = Type::new_var_use(0, TypeBound::Any);
390+
let dfb = DataflowBlock {
391+
inputs: vec![usize_t(), tv0.clone()].into(),
392+
other_outputs: vec![tv0.clone()].into(),
393+
sum_rows: vec![usize_t().into(), vec![qb_t(), tv0.clone()].into()],
394+
extension_delta: ExtensionSet::type_var(1),
395+
};
396+
let dfb2 = dfb.substitute(&Substitution::new(
397+
&[
398+
qb_t().into(),
399+
TypeArg::Extensions {
400+
es: PRELUDE_ID.into(),
401+
},
402+
],
403+
&PRELUDE_REGISTRY,
404+
));
405+
let st = Type::new_sum(vec![vec![usize_t()], vec![qb_t(); 2]]);
406+
assert_eq!(
407+
dfb2.inner_signature(),
408+
Signature::new(vec![usize_t(), qb_t()], vec![st, qb_t()])
409+
.with_extension_delta(PRELUDE_ID)
410+
);
411+
}
412+
413+
#[test]
414+
fn test_subst_conditional() {
415+
let tv1 = Type::new_var_use(1, TypeBound::Any);
416+
let cond = Conditional {
417+
sum_rows: vec![usize_t().into(), tv1.clone().into()],
418+
other_inputs: vec![Type::new_tuple(TypeRV::new_row_var_use(0, TypeBound::Any))].into(),
419+
outputs: vec![usize_t(), tv1].into(),
420+
extension_delta: ExtensionSet::new(),
421+
};
422+
let cond2 = cond.substitute(&Substitution::new(
423+
&[
424+
TypeArg::Sequence {
425+
elems: vec![usize_t().into(); 3],
426+
},
427+
qb_t().into(),
428+
],
429+
&PRELUDE_REGISTRY,
430+
));
431+
let st = Type::new_sum(vec![usize_t(), qb_t()]); //both single-element variants
432+
assert_eq!(
433+
cond2.signature(),
434+
Signature::new(
435+
vec![st, Type::new_tuple(vec![usize_t(); 3])],
436+
vec![usize_t(), qb_t()]
437+
)
438+
);
439+
}
440+
441+
#[test]
442+
fn test_tail_loop() {
443+
let tv0 = Type::new_var_use(0, TypeBound::Copyable);
444+
let tail_loop = TailLoop {
445+
just_inputs: vec![qb_t(), tv0.clone()].into(),
446+
just_outputs: vec![tv0.clone(), qb_t()].into(),
447+
rest: vec![tv0.clone()].into(),
448+
extension_delta: ExtensionSet::type_var(1),
449+
};
450+
let tail2 = tail_loop.substitute(&Substitution::new(
451+
&[
452+
usize_t().into(),
453+
TypeArg::Extensions {
454+
es: PRELUDE_ID.into(),
455+
},
456+
],
457+
&PRELUDE_REGISTRY,
458+
));
459+
assert_eq!(
460+
tail2.signature(),
461+
Signature::new(
462+
vec![qb_t(), usize_t(), usize_t()],
463+
vec![usize_t(), qb_t(), usize_t()]
464+
)
465+
.with_extension_delta(PRELUDE_ID)
466+
);
467+
}
468+
}

hugr-core/src/ops/custom.rs

+28
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,26 @@ impl DataflowOpTrait for ExtensionOp {
166166
fn signature(&self) -> Cow<'_, Signature> {
167167
Cow::Borrowed(&self.signature)
168168
}
169+
170+
fn substitute(&self, subst: &crate::types::Substitution) -> Self {
171+
let args = self
172+
.args
173+
.iter()
174+
.map(|ta| ta.substitute(subst))
175+
.collect::<Vec<_>>();
176+
let signature = self.signature.substitute(subst);
177+
debug_assert_eq!(
178+
self.def
179+
.compute_signature(&args, subst.extension_registry())
180+
.as_ref(),
181+
Ok(&signature)
182+
);
183+
Self {
184+
def: self.def.clone(),
185+
args,
186+
signature,
187+
}
188+
}
169189
}
170190

171191
/// An opaquely-serialized op that refers to an as-yet-unresolved [`OpDef`].
@@ -259,6 +279,14 @@ impl DataflowOpTrait for OpaqueOp {
259279
fn signature(&self) -> Cow<'_, Signature> {
260280
Cow::Borrowed(&self.signature)
261281
}
282+
283+
fn substitute(&self, subst: &crate::types::Substitution) -> Self {
284+
Self {
285+
args: self.args.iter().map(|ta| ta.substitute(subst)).collect(),
286+
signature: self.signature.substitute(subst),
287+
..self.clone()
288+
}
289+
}
262290
}
263291

264292
/// Errors that arise after loading a Hugr containing opaque ops (serialized just as their names)

0 commit comments

Comments
 (0)