Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
47b8f66
Dump debug info in hugr-cli
cgh-qtnm Mar 10, 2026
82fc4b2
Make it compile and pass existing tests
cgh-qtnm Mar 25, 2026
435a456
Work on 2qb
cgh-qtnm Apr 7, 2026
5e9e3c7
Working 2qb LLVM debug info
cgh-qtnm Apr 8, 2026
e2f837f
Working O0 debug info in binary
cgh-qtnm Apr 8, 2026
5f3419f
Add support for opaque pointers
cgh-qtnm Apr 10, 2026
1220995
Generate pretty function names
cgh-qtnm Apr 13, 2026
26db7ea
Implement random debug info testing
cgh-qtnm Apr 14, 2026
cf9e51d
Add random debug info to snapshots
cgh-qtnm Apr 15, 2026
f848fdc
Refactor test code
cgh-qtnm Apr 15, 2026
1485b15
Correctly handle compiler-generated wrapper
cgh-qtnm Apr 15, 2026
54b5a97
Make add_random_debug_info public
cgh-qtnm Apr 15, 2026
27f4fd1
Get pointer size from caller
cgh-qtnm Apr 16, 2026
84226c6
hugr-llvm: add unit tests for debug info generation
cgh-qtnm Apr 16, 2026
a4bdfc8
hugr-llvm: add error path tests for debug info generation
cgh-qtnm Apr 16, 2026
3376256
hugr-llvm: simplify debug info tests
cgh-qtnm Apr 16, 2026
8c3c2cf
Fixes from copilot
cgh-qtnm Apr 17, 2026
2feac5a
Documentation
cgh-qtnm Apr 17, 2026
18f1fb4
hugr-llvm: test failure on invalid debug info JSON schema
cgh-qtnm Apr 17, 2026
6a0e206
Fixes from copilot and self
cgh-qtnm Apr 17, 2026
dae040b
fix(hugr-persistent): handle NodeLabel::MetadataKey in mermaid_string…
cgh-qtnm Apr 17, 2026
4ebb9b8
hugr-llvm: extract unmangle_hugr_func_name and add unit tests
cgh-qtnm Apr 20, 2026
3bd16c6
Add 'notail' to all HUGR function calls
cgh-qtnm Apr 21, 2026
2d59311
Revert "Add 'notail' to all HUGR function calls"
cgh-qtnm Apr 22, 2026
08bddaf
Document and restructure hugr-core::metadata::debug_info
cgh-qtnm Apr 22, 2026
6aa4bf1
Minor fixes from Augustin:
cgh-qtnm Apr 22, 2026
5413ea7
Conform to updates in guppylang metadata gen
cgh-qtnm Apr 22, 2026
e0b44be
Enhance metadata node labels to print a list of keys
cgh-qtnm Apr 22, 2026
60f1585
Move try_get_metadata into HugrView implementation
cgh-qtnm Apr 29, 2026
7d58ef7
Review from Agustín:
cgh-qtnm May 13, 2026
dc76fce
Merge branch 'main' into george/debuginfo
cgh-qtnm May 13, 2026
998b3a2
Support ALIASES in try_get_metadata
cgh-qtnm May 13, 2026
e26c36b
Document requirement for callers of EmitModuleContext::finish
cgh-qtnm May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ num-rational = "0.4.1"
pastey = "0.2.1"
proptest = "1.11.0"
proptest-derive = "0.8.0"
rand = "0.10.1"
regex = "1.12.3"
regex-syntax = "0.8.9"
rstest = "0.26.1"
Expand Down
25 changes: 23 additions & 2 deletions hugr-cli/src/mermaid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use anyhow::Result;
use clap::Parser;
use clio::Output;
use hugr::HugrView;
use hugr::hugr::views::render::NodeLabel;
use hugr::metadata::DEBUGINFO_META_KEY;
use hugr::package::PackageValidationError;

/// Dump the standard extensions.
Expand All @@ -26,6 +28,16 @@ pub struct MermaidArgs {
help = "Validate before rendering, includes extension inference."
)]
pub validate: bool,

/// Print debug metadata.
#[arg(
short = 'D',
long,
help = "Print debug info attached to nodes if it exists. For rendering purposes, \
we replace double quotes with single quotes and newlines with spaces."
)]
pub debug_info: bool,

/// Output file '-' for stdout
#[clap(long, short, value_parser, default_value = "-")]
output: Output,
Expand Down Expand Up @@ -64,10 +76,19 @@ impl MermaidArgs {
}

for hugr in package.modules {
let mmd_fmt = if self.debug_info {
hugr.mermaid_format()
.with_node_labels(NodeLabel::MetadataValues {
print_keys: [DEBUGINFO_META_KEY.to_string()].into(),
})
} else {
hugr.mermaid_format()
};

if let Some(ref mut writer) = output_override {
writeln!(writer, "{}", hugr.mermaid_string())?;
writeln!(writer, "{}", mmd_fmt.finish())?;
} else {
writeln!(self.output, "{}", hugr.mermaid_string())?;
writeln!(self.output, "{}", mmd_fmt.finish())?;
}
}
Ok(())
Expand Down
28 changes: 27 additions & 1 deletion hugr-core/src/hugr/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod tests;

use ::petgraph::visit as pv;
use serde::de::Deserialize;
use std::any::type_name;
use std::borrow::Cow;
use std::collections::HashMap;

Expand All @@ -30,7 +31,7 @@ use crate::core::HugrNode;
use crate::extension::ExtensionRegistry;
use crate::hugr::internal::{DefaultPGNodeMap, PortgraphNodeMap};
use crate::hugr::views::syn_edge::SynEdgeWrapper;
use crate::metadata::{Metadata, RawMetadataValue};
use crate::metadata::{Metadata, MetadataError, RawMetadataValue};
use crate::ops::{OpParent, OpTag, OpTrait, OpType, handle::NodeHandle};
use crate::types::{EdgeKind, PolyFuncType, Signature, Type};
use crate::{Direction, IncomingPort, OutgoingPort, Port};
Expand Down Expand Up @@ -112,6 +113,31 @@ pub trait HugrView: HugrInternals {
.and_then(|value| <<M as Metadata>::Type<'_> as Deserialize>::deserialize(value).ok())
}

/// Returns the metadata associated with a node, differentiating between a missing and
/// invalid payload.
///
/// If there is no metadata found with a key matching `M::KEY` or `M::ALIASES`, returns Ok(None)
/// If metadata is present but it does not deserialize into M, return Err
/// Otherwise, return Ok(Some(metadata)).
#[inline]
fn try_get_metadata<M: Metadata>(
&self,
node: Self::Node,
) -> Result<Option<<M as Metadata>::Type<'_>>, MetadataError> {
if let Some(raw_value) = std::iter::once(<M as Metadata>::KEY)
.chain(<M as Metadata>::ALIASES.iter().copied())
.find_map(|key| self.get_metadata_any(node, key))
{
<<M as Metadata>::Type<'_> as Deserialize>::deserialize(raw_value)
.map_err(|json_err| {
MetadataError::MetadataDeserializationError(type_name::<M>(), json_err)
})
.map(Some)
} else {
Ok(None)
}
}

/// Returns a metadata entry associated with a node and a string key.
///
/// When possible, prefer using the type-safe accessor [`HugrView::get_metadata`] instead.
Expand Down
54 changes: 43 additions & 11 deletions hugr-core/src/hugr/views/render.rs
Comment thread
cgh-qtnm marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Helper methods to compute the node/edge/port style when rendering a HUGR
//! into dot or mermaid format.

use itertools::Itertools;
use std::collections::HashMap;

use portgraph::render::{EdgeStyle, NodeStyle, PortStyle, PresentationStyle};
Expand Down Expand Up @@ -179,6 +180,12 @@ pub enum NodeLabel<N: HugrNode = Node> {
/// Display the node index as a number.
#[default]
Numeric,
/// Display the numeric node index and a list of metadata keys and their JSON values.
/// Prints "null" if a key is not present on a node.
MetadataValues {
/// List of metadata keys to display
print_keys: Vec<String>,
},
/// Display the labels corresponding to the node indices.
Custom(HashMap<N, String>),
}
Expand All @@ -196,6 +203,14 @@ pub(in crate::hugr) fn node_style<'a>(
}
}

fn numeric_label(h: &Hugr, n: NodeIndex, is_entry: bool) -> String {
if is_entry {
format!("({}) [**{}**]", n.index(), node_name(h, n))
} else {
format!("({}) {}", n.index(), node_name(h, n))
}
}

let mut entrypoint_style = PresentationStyle::default();
entrypoint_style.stroke = Some("#832561".to_string());
entrypoint_style.stroke_width = Some("3px".to_string());
Expand All @@ -204,18 +219,9 @@ pub(in crate::hugr) fn node_style<'a>(
match formatter.node_labels {
NodeLabel::Numeric => Box::new(move |n| {
if Some(n) == entrypoint {
NodeStyle::boxed(format!(
"({ni}) [**{name}**]",
ni = n.index(),
name = node_name(h, n)
))
.with_attrs(entrypoint_style.clone())
NodeStyle::boxed(numeric_label(h, n, true)).with_attrs(entrypoint_style.clone())
} else {
NodeStyle::boxed(format!(
"({ni}) {name}",
ni = n.index(),
name = node_name(h, n)
))
NodeStyle::boxed(numeric_label(h, n, false))
}
}),
NodeLabel::None => Box::new(move |n| {
Expand All @@ -226,6 +232,32 @@ pub(in crate::hugr) fn node_style<'a>(
NodeStyle::boxed(node_name(h, n))
}
}),
NodeLabel::MetadataValues { print_keys } => Box::new(move |n| {
let kv_str = print_keys
.iter()
.filter_map(|key| {
h.get_metadata_any(n.into(), key).map(|json_value| {
format!(
"{key}={}",
serde_json::to_string(json_value)
.expect("JSON metadata should be serializable")
// the mermaid renderer in portgraph generates verbose escapes
// for double quotes and newlines, so we replace them with
// single quotes and spaces
.replace('\n', " ")
.replace('"', "\'")
)
})
})
.join("; ");

if Some(n) == entrypoint {
NodeStyle::boxed(format!("{}; {kv_str}", numeric_label(h, n, true)))
.with_attrs(entrypoint_style.clone())
} else {
NodeStyle::boxed(format!("{}; {kv_str}", numeric_label(h, n, false)))
}
}),
NodeLabel::Custom(labels) => Box::new(move |n| {
if Some(n) == entrypoint {
NodeStyle::boxed(format!(
Expand Down
19 changes: 19 additions & 0 deletions hugr-core/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@
//! let payload = hugr.get_metadata::<SomeMetadata>(hugr.module_root());
//! assert_eq!(payload, Some("payload"));
//! ```

/// Definitions of the HUGR debug info metadata types
pub mod debug_info;

use thiserror::Error;

#[doc(inline)]
pub use self::debug_info::{
CompileUnitRecord, DEBUGINFO_META_KEY, DebugRecordKind, LocationRecord, SubprogramRecord,
};
//
// When adding new metadata keys, they should be re-exported by the python bindings.
// See hugr-py/rust/metadata.rs
Comment thread
cgh-qtnm marked this conversation as resolved.
Expand Down Expand Up @@ -73,3 +83,12 @@ impl Metadata for HugrUsedExtensions {
type Type<'hugr> = Vec<crate::envelope::description::ExtensionDesc>;
const KEY: &'static str = "core.used_extensions";
}

/// Errors related to metadata
#[derive(Error, Debug)]
pub enum MetadataError {
/// Returned by `try_get_metadata` if the metadata present at the requested key
/// cannot be deserialized into the expected type.
#[error("Metadata value does not deserialize to {0}: {1}")]
MetadataDeserializationError(&'static str, serde_json::Error),
}
Loading
Loading