-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat!: used_extensions
calls for both ops and signatures
#1739
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1739 +/- ##
==========================================
+ Coverage 86.28% 86.29% +0.01%
==========================================
Files 177 179 +2
Lines 32087 32662 +575
Branches 28999 29574 +575
==========================================
+ Hits 27687 28187 +500
- Misses 2775 2797 +22
- Partials 1625 1678 +53
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Done |
update_type_row_exts(node, &mut db.other_outputs, extensions, used_extensions)?; | ||
for row in db.sum_rows.iter_mut() { | ||
update_type_row_exts(node, row, extensions, used_extensions)?; | ||
collect_type_row_exts(&db.inputs, &mut used, &mut missing); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the signature of a DataflowBlock include all the elements of the inputs
, other_outputs
and sum_rows
? So can't you just collect_signature_exts
on the block's signature, and the same for e.g. TailLoop, Conditional, and so on, like OpaqueOp and most other blocks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but that requires allocating a Signature
, cloning all row elements, computing the concrete input/output rows, and immediately destructuring them to run this same logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this true even with Cow
of signature? I mean, we're gonna be computing that signature a good few times anyway...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmm, even when we have Cow<Signature>
? I mean, we are gonna be constructing that thing a few times, clearly it's gonna help to cache it in the op. OK don't add that for this PR, but if you're optimizing that anyway and could use that here...
/// signature. The extension deltas added via [`Self::with_extension_delta`] | ||
/// refer to _runtime_ extensions, which may not be in all places that | ||
/// manipulate a HUGR. | ||
pub fn used_extensions(&self) -> Result<ExtensionRegistry, ExtensionCollectionError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the other way to do this - is to put the collect_blah_exts
methods as impls of a single method in a trait, and then you can add a trait-default method (or a method taking an impl ExtensionCollectionTrait
) that does this...that'd get you the two used_extensions
methods that you have plus a load more with only having to write this once. (I wasn't going to suggest the trait approach until I saw this...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't work if you change OpType::used_extensions
to take the extra Option<Node>
parameter I suggested elsewhere, though. So feel free to drop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opted for keeping all the logic in a single module, as it's short and quite similar between all the ops.
let mut collected_exts = ExtensionRegistry::default(); | ||
for node in hugr.nodes() { | ||
let op = hugr.get_optype(node); | ||
collected_exts.extend(collect_op_extensions(Some(node), op).unwrap()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you changed OpType::used_extensions to take the Option<Node>
param as extra, which seems reasonable, then you could call that here and remove all direct calls to collect_op_
(type_
)extensions
. Then hide those (or inline into the single caller OpType::used_extensions
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was calling the resolve methods explicitly, but it's exactly the same as op.used_exts
.
I just changed it to that.
/// - `node`: The node where the operation is located, if available. | ||
/// This is used to provide context in the error message. | ||
/// - `op`: The operation to collect the extensions from. | ||
pub fn collect_op_types_extensions( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be pub
? The nomenclature is a bit confusing here - it feels equivalent to Signature::used_extensions
but actually it's only one part of OpType::used_extensions
. However the other collect
things take two &mut
s. Should one really use this directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's hidden inside the private types
module. But I'll add the (crate)
for clarity.
//! Resolve weak links inside `CustomType`s in an optype's signature, while | ||
//! collecting all used extensions. | ||
//! | ||
//! For a non-mutating option see [`super::collect_op_types_extensions`]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reckon the pub function here should be OpType::used_extensions
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a private module. (Otherwise the docs check would complain)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, yes I missed that. Ok I'll hold some fire there then ;)
used_extensions
calls for both ops and signaturesused_extensions
calls for both ops and signatures
This PR contains breaking changes to the public Rust API. cargo-semver-checks summary
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally - great :). I think all of these comments are now small and local, so looks good and thanks @aborgna-q , let's get it in :)
I think there is a bit of an issue that ideally one would like Hugr::validate
to also validate the registry (if not cached in that AtomicBool
), but that requires passing an &mut ExtensionRegistry
which is gonna require changing everywhere (and annoyingly). Otherwise you could add methods to validate immutably (i.e. without updating the cached bool), I guess....
} | ||
|
||
/// Delete an extension from the registry and return it if it was present. | ||
pub fn remove_extension(&mut self, name: &ExtensionId) -> Option<Arc<Extension>> { | ||
self.0.remove(name) | ||
// Clear the valid flag so that the registry is re-validated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most nitty super-nit ever: these two lines after the removal, for consistency with the other operations :-) :-D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
returns the Option<Arc<Ext>>
, so moving it requires declaring local vars..
hugr-core/src/extension.rs
Outdated
/// A flag indicating whether the current set of extensions has been | ||
/// validated. | ||
/// | ||
/// This is used to avoid re-validating the extensions every time they are |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// This is used to avoid re-validating the extensions every time they are | |
/// This is used to avoid re-validating the extensions every time the registry is |
(I think - if you grab an extension out of the registry, you can use it without validating the registry; it's only when the registry itself is passed to validate a Hugr, say. Of course this is a subtlety of how we interpret "use" of an extension vs "use" of the regstry, so feel free to be more explicit - "every time a Hugr is validated against the registry", perhaps, although that's a bit more of a mouthful)
/// An Extension Registry containing no extensions. | ||
pub const EMPTY_REG: ExtensionRegistry = ExtensionRegistry(BTreeMap::new()); | ||
pub static EMPTY_REG: ExtensionRegistry = ExtensionRegistry { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
random driveby, feel free to ignore - could this could be a const fn
, to which you could add Extensions yourself? (and then static EMPTY_REG: ExtensionRegistry = ExtensionRegistry::new_empty();
or similar)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ExtensionRegistry now has internal mutability, so it should not be used as a const.
(it does compile, but using it just raises linting errors everywhere, with good reason)
update_type_row_exts(node, &mut db.other_outputs, extensions, used_extensions)?; | ||
for row in db.sum_rows.iter_mut() { | ||
update_type_row_exts(node, row, extensions, used_extensions)?; | ||
collect_type_row_exts(&db.inputs, &mut used, &mut missing); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmm, even when we have Cow<Signature>
? I mean, we are gonna be constructing that thing a few times, clearly it's gonna help to cache it in the op. OK don't add that for this PR, but if you're optimizing that anyway and could use that here...
@@ -213,7 +213,7 @@ impl Hugr { | |||
// | |||
// This is not something we want to expose it the API, so we manually | |||
// iterate instead of writing it as a method. | |||
for n in 0..self.node_count() { | |||
for n in 0..self.graph.node_capacity() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please do! The comment above does not explain (to me) why we are using node_capacity
]) | ||
.unwrap() | ||
]); | ||
reg.validate() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many examples here and elsewhere, I wonder if it's worth adding to ExtensionRegistry
fn validated(mut self) -> Result<Self, ....> {
self.validate()?;
self
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll call ExtRegistry::validate
on each Hugr::validate
as soon as #1738 gets merged, so this shouldn't be needed.
…1738) Closes #1613. Depends on #1739. Hugrs now keep an `Extensions` registry that is automatically computed when using the builder or deserializing from json (using the new `Hugr::load_json`). This set can contain unneeded extensions, but it should always be sufficient to validate the HUGR definition. Note that this is **not** runtime extensions (see #426, #1734). A big chunk of the diff is removing the extension registry when finishing building a hugr. The extension tracking is now done automatically while adding operations. drive-by: Remove unneeded `set_num_ports` call in `insert_hugr_internal`. BREAKING CHANGE: Removed `update_validate`. The hugr extensions should be resolved at load time, so we can use `validate` instead. BREAKING CHANGE: The builder `finish_hugr` function family no longer takes a registry as parameter, and the `_prelude` variants have been removed.
Adds methods
OpType::used_extensions(&self) -> Result<ExtensionRegistry, _>
Signature::used_extensions(&self) -> Result<ExtensionRegistry, _>
and tests these along with the code merged in feat!: Resolve OpaqueOps and CustomType extensions #1735.
Moves the code from #1735 into
resolution::types_mut
, and adds a (quite-similar) non-mutable version in::types
that only collects the extensions without modifying theOpType
.Fixes the resolution not exploring types inside a
CustomType
type arguments.drive-by: Implement
Display
,Serialize
, and::new
forExtensionRegistry
.drive-by:
ExtensionSet
should take ids by value when inserting.drive-by: Fix
Hugr::resolve_extension_defs
not scanning all the ops.These changes were extracted from #1738.
BREAKING CHANGE: Removed
ExtensionRegistry::try_new
. Usenew
instead, and callExtensionRegistry::validate
to validate.BREAKING CHANGE:
ExtensionSet::insert
andsingleton
take extension ids by value instead of cloning internally.