Skip to content

Commit

Permalink
Merge branch 'main' into feat/uninitialized-array
Browse files Browse the repository at this point in the history
  • Loading branch information
mark-koch authored Nov 21, 2024
2 parents 6a3034f + 6bd094f commit 18bca6b
Show file tree
Hide file tree
Showing 31 changed files with 770 additions and 555 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-py.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ jobs:
- name: Upload python coverage to codecov.io
if: github.event_name != 'merge_group' && matrix.python-version.coverage
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
files: coverage.xml
name: python
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-rs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ jobs:
- name: Generate coverage report
run: cargo llvm-cov --all-features report --codecov --output-path coverage.json
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
files: coverage.json
name: rust
Expand Down
11 changes: 6 additions & 5 deletions .github/workflows/notify-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ jobs:
if: needs.coverage-trend.outputs.should_notify == 'true'
steps:
- name: Send notification
uses: slackapi/slack-github-action@v1.27.0
uses: slackapi/slack-github-action@v2.0.0
with:
channel-id: 'C04SHCL4FKP'
slack-message: ${{ needs.coverage-trend.outputs.msg }}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: 'C04SHCL4FKP'
text: ${{ needs.coverage-trend.outputs.msg }}
28 changes: 13 additions & 15 deletions .github/workflows/unsoundness.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: Unsoundness checks

on:
push:
branches:
- main
schedule:
# Weekly on Monday at 04:00 UTC
- cron: '0 4 * * 1'
workflow_dispatch: {}

concurrency:
Expand Down Expand Up @@ -40,19 +40,17 @@ jobs:
run: cargo miri test


notify-slack:
uses: CQCL/hugrverse-actions/.github/workflows/slack-notifier.yml@main
create-issue:
uses: CQCL/hugrverse-actions/.github/workflows/create-issue.yml@main
needs: miri
if: always() && needs.miri.result == 'failure' && github.event_name == 'push'
with:
channel-id: 'C04SHCL4FKP'
slack-message: |
💥 The unsoundness check for `CQCL/hugr` failed.
<https://github.com/CQCL/hugr/actions/runs/${{ github.run_id }}|Please investigate>.
# Rate-limit the message to once per day
timeout-minutes: 1440
# A repository variable used to store the last message timestamp.
timeout-variable: "UNSOUNDNESS_MSG_SENT"
secrets:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
GITHUB_PAT: ${{ secrets.HUGRBOT_PAT }}
with:
title: "💥 Unsoundness check failed on main"
body: |
The unsoundness check for `CQCL/hugr` failed.
[https://github.com/CQCL/hugr/actions/runs/${{ github.run_id }}](Please investigate).
unique-label: "unsoundness-checks"
other-labels: "bug"
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ portgraph = { version = "0.12.2" }
insta = { version = "1.34.0" }
bitvec = "1.0.1"
cgmath = "0.18.0"
context-iterators = "0.2.0"
cool_asserts = "2.0.3"
criterion = "0.5.1"
delegate = "0.13.0"
Expand Down
1 change: 0 additions & 1 deletion hugr-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ bitvec = { workspace = true, features = ["serde"] }
enum_dispatch = { workspace = true }
lazy_static = { workspace = true }
petgraph = { workspace = true }
context-iterators = { workspace = true }
serde_json = { workspace = true }
delegate = { workspace = true }
paste = { workspace = true }
Expand Down
107 changes: 79 additions & 28 deletions hugr-core/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
type_param::{TypeArgVariable, TypeParam},
type_row::TypeRowBase,
CustomType, FuncTypeBase, MaybeRV, PolyFuncTypeBase, RowVariable, SumType, TypeArg,
TypeBase, TypeEnum,
TypeBase, TypeBound, TypeEnum,
},
Direction, Hugr, HugrView, IncomingPort, Node, Port,
};
Expand Down Expand Up @@ -44,8 +44,21 @@ struct Context<'a> {
bump: &'a Bump,
/// Stores the terms that we have already seen to avoid duplicates.
term_map: FxHashMap<model::Term<'a>, model::TermId>,

/// The current scope for local variables.
///
/// This is set to the id of the smallest enclosing node that defines a polymorphic type.
/// We use this when exporting local variables in terms.
local_scope: Option<model::NodeId>,

/// Constraints to be added to the local scope.
///
/// When exporting a node that defines a polymorphic type, we use this field
/// to collect the constraints that need to be added to that polymorphic
/// type. Currently this is used to record `nonlinear` constraints on uses
/// of `TypeParam::Type` with a `TypeBound::Copyable` bound.
local_constraints: Vec<model::TermId>,

/// Mapping from extension operations to their declarations.
decl_operations: FxHashMap<(ExtensionId, OpName), model::NodeId>,
}
Expand All @@ -63,13 +76,14 @@ impl<'a> Context<'a> {
term_map: FxHashMap::default(),
local_scope: None,
decl_operations: FxHashMap::default(),
local_constraints: Vec::new(),
}
}

/// Exports the root module of the HUGR graph.
pub fn export_root(&mut self) {
let hugr_children = self.hugr.children(self.hugr.root());
let mut children = Vec::with_capacity(hugr_children.len());
let mut children = Vec::with_capacity(hugr_children.size_hint().0);

for child in self.hugr.children(self.hugr.root()) {
children.push(self.export_node(child));
Expand Down Expand Up @@ -110,7 +124,7 @@ impl<'a> Context<'a> {
num_ports: usize,
) -> &'a [model::LinkRef<'a>] {
let ports = self.hugr.node_ports(node, direction);
let mut links = BumpVec::with_capacity_in(ports.len(), self.bump);
let mut links = BumpVec::with_capacity_in(ports.size_hint().0, self.bump);

for port in ports.take(num_ports) {
links.push(model::LinkRef::Id(self.get_link_id(node, port)));
Expand Down Expand Up @@ -173,9 +187,11 @@ impl<'a> Context<'a> {
}

fn with_local_scope<T>(&mut self, node: model::NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
let old_scope = self.local_scope.replace(node);
let prev_local_scope = self.local_scope.replace(node);
let prev_local_constraints = std::mem::take(&mut self.local_constraints);
let result = f(self);
self.local_scope = old_scope;
self.local_scope = prev_local_scope;
self.local_constraints = prev_local_constraints;
result
}

Expand Down Expand Up @@ -232,10 +248,11 @@ impl<'a> Context<'a> {

OpType::FuncDefn(func) => self.with_local_scope(node_id, |this| {
let name = this.get_func_name(node).unwrap();
let (params, signature) = this.export_poly_func_type(&func.signature);
let (params, constraints, signature) = this.export_poly_func_type(&func.signature);
let decl = this.bump.alloc(model::FuncDecl {
name,
params,
constraints,
signature,
});
let extensions = this.export_ext_set(&func.signature.body().extension_reqs);
Expand All @@ -247,10 +264,11 @@ impl<'a> Context<'a> {

OpType::FuncDecl(func) => self.with_local_scope(node_id, |this| {
let name = this.get_func_name(node).unwrap();
let (params, func) = this.export_poly_func_type(&func.signature);
let (params, constraints, func) = this.export_poly_func_type(&func.signature);
let decl = this.bump.alloc(model::FuncDecl {
name,
params,
constraints,
signature: func,
});
model::Operation::DeclareFunc { decl }
Expand Down Expand Up @@ -450,10 +468,11 @@ impl<'a> Context<'a> {

let decl = self.with_local_scope(node, |this| {
let name = this.make_qualified_name(opdef.extension(), opdef.name());
let (params, r#type) = this.export_poly_func_type(poly_func_type);
let (params, constraints, r#type) = this.export_poly_func_type(poly_func_type);
let decl = this.bump.alloc(model::OperationDecl {
name,
params,
constraints,
r#type,
});
decl
Expand Down Expand Up @@ -579,7 +598,7 @@ impl<'a> Context<'a> {
let targets = self.make_ports(output_node, Direction::Incoming, output_op.types.len());

// Export the remaining children of the node.
let mut region_children = BumpVec::with_capacity_in(children.len(), self.bump);
let mut region_children = BumpVec::with_capacity_in(children.size_hint().0, self.bump);

for child in children {
region_children.push(self.export_node(child));
Expand Down Expand Up @@ -609,7 +628,7 @@ impl<'a> Context<'a> {
/// Creates a control flow region from the given node's children.
pub fn export_cfg(&mut self, node: Node) -> model::RegionId {
let mut children = self.hugr.children(node);
let mut region_children = BumpVec::with_capacity_in(children.len() + 1, self.bump);
let mut region_children = BumpVec::with_capacity_in(children.size_hint().0 + 1, self.bump);

// The first child is the entry block.
// We create a source port on the control flow region and connect it to the
Expand All @@ -623,16 +642,16 @@ impl<'a> Context<'a> {
let source = model::LinkRef::Id(self.get_link_id(entry_block, IncomingPort::from(0)));
region_children.push(self.export_node(entry_block));

// Export the remaining children of the node, except for the last one.
for _ in 0..children.len() - 1 {
region_children.push(self.export_node(children.next().unwrap()));
}

// The last child is the exit block.
// Contrary to the entry block, the exit block does not have a dataflow subgraph.
// We therefore do not export the block itself, but simply use its output ports
// as the target ports of the control flow region.
let exit_block = children.next().unwrap();
let exit_block = children.next_back().unwrap();

// Export the remaining children of the node, except for the last one.
for child in children {
region_children.push(self.export_node(child));
}

let OpType::ExitBlock(_) = self.hugr.get_optype(exit_block) else {
panic!("expected an `ExitBlock` node as the last child node");
Expand All @@ -657,7 +676,7 @@ impl<'a> Context<'a> {
/// Export the `Case` node children of a `Conditional` node as data flow regions.
pub fn export_conditional_regions(&mut self, node: Node) -> &'a [model::RegionId] {
let children = self.hugr.children(node);
let mut regions = BumpVec::with_capacity_in(children.len(), self.bump);
let mut regions = BumpVec::with_capacity_in(children.size_hint().0, self.bump);

for child in children {
let OpType::Case(case_op) = self.hugr.get_optype(child) else {
Expand All @@ -671,22 +690,36 @@ impl<'a> Context<'a> {
regions.into_bump_slice()
}

/// Exports a polymorphic function type.
///
/// The returned triple consists of:
/// - The static parameters of the polymorphic function type.
/// - The constraints of the polymorphic function type.
/// - The function type itself.
pub fn export_poly_func_type<RV: MaybeRV>(
&mut self,
t: &PolyFuncTypeBase<RV>,
) -> (&'a [model::Param<'a>], model::TermId) {
) -> (&'a [model::Param<'a>], &'a [model::TermId], model::TermId) {
let mut params = BumpVec::with_capacity_in(t.params().len(), self.bump);
let scope = self
.local_scope
.expect("exporting poly func type outside of local scope");

for (i, param) in t.params().iter().enumerate() {
let name = self.bump.alloc_str(&i.to_string());
let r#type = self.export_type_param(param);
let param = model::Param::Implicit { name, r#type };
let r#type = self.export_type_param(param, Some(model::LocalRef::Index(scope, i as _)));
let param = model::Param {
name,
r#type,
sort: model::ParamSort::Implicit,
};
params.push(param)
}

let constraints = self.bump.alloc_slice_copy(&self.local_constraints);
let body = self.export_func_type(t.body());

(params.into_bump_slice(), body)
(params.into_bump_slice(), constraints, body)
}

pub fn export_type<RV: MaybeRV>(&mut self, t: &TypeBase<RV>) -> model::TermId {
Expand All @@ -703,7 +736,6 @@ impl<'a> Context<'a> {
}
TypeEnum::Function(func) => self.export_func_type(func),
TypeEnum::Variable(index, _) => {
// This ignores the type bound for now
let node = self.local_scope.expect("local variable out of scope");
self.make_term(model::Term::Var(model::LocalRef::Index(node, *index as _)))
}
Expand Down Expand Up @@ -794,20 +826,39 @@ impl<'a> Context<'a> {
self.make_term(model::Term::List { items, tail: None })
}

pub fn export_type_param(&mut self, t: &TypeParam) -> model::TermId {
/// Exports a `TypeParam` to a term.
///
/// The `var` argument is set when the type parameter being exported is the
/// type of a parameter to a polymorphic definition. In that case we can
/// generate a `nonlinear` constraint for the type of runtime types marked as
/// `TypeBound::Copyable`.
pub fn export_type_param(
&mut self,
t: &TypeParam,
var: Option<model::LocalRef<'static>>,
) -> model::TermId {
match t {
// This ignores the type bound for now.
TypeParam::Type { .. } => self.make_term(model::Term::Type),
// This ignores the type bound for now.
TypeParam::Type { b } => {
if let (Some(var), TypeBound::Copyable) = (var, b) {
let term = self.make_term(model::Term::Var(var));
let non_linear = self.make_term(model::Term::NonLinearConstraint { term });
self.local_constraints.push(non_linear);
}

self.make_term(model::Term::Type)
}
// This ignores the bound on the natural for now.
TypeParam::BoundedNat { .. } => self.make_term(model::Term::NatType),
TypeParam::String => self.make_term(model::Term::StrType),
TypeParam::List { param } => {
let item_type = self.export_type_param(param);
let item_type = self.export_type_param(param, None);
self.make_term(model::Term::ListType { item_type })
}
TypeParam::Tuple { params } => {
let items = self.bump.alloc_slice_fill_iter(
params.iter().map(|param| self.export_type_param(param)),
params
.iter()
.map(|param| self.export_type_param(param, None)),
);
let types = self.make_term(model::Term::List { items, tail: None });
self.make_term(model::Term::ApplyFull {
Expand Down
1 change: 1 addition & 0 deletions hugr-core/src/extension/op_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ impl OpDef {
}

/// Iterate over all miscellaneous data in the [OpDef].
#[allow(unused)] // Unused when no features are enabled
pub(crate) fn iter_misc(&self) -> impl ExactSizeIterator<Item = (&str, &serde_json::Value)> {
self.misc.iter().map(|(k, v)| (k.as_str(), v))
}
Expand Down
Loading

0 comments on commit 18bca6b

Please sign in to comment.