Skip to content

feat: update passes to use PassScope where non-breaking#2836

Merged
acl-cqc merged 58 commits intomainfrom
acl/pass-scopes
Mar 6, 2026
Merged

feat: update passes to use PassScope where non-breaking#2836
acl-cqc merged 58 commits intomainfrom
acl/pass-scopes

Conversation

@acl-cqc
Copy link
Contributor

@acl-cqc acl-cqc commented Jan 23, 2026

Follow-up to #2772 with non-breaking updates to ConstFoldPass, DeadCodeElimPass, NormalizeCFGsPass, RedundantOrderEdgesPass, RemoveDeadFuncsPass, ReplaceTypes, UntuplePass. (Breaking updates to follow in #2871.)

Generally this means keeping pre-existing pass configurating methods but deprecating, and storing such config in an Either<PassScope, old-config>, so we can drop the old config in time.

Also deprecated toplevel functions constant_fold_pass, remove_dead_funcs and monomorphize.

@acl-cqc acl-cqc changed the title WIP: update passes to use PassScope following #2772 WIP: update passes to use PassScope Jan 23, 2026
@codecov
Copy link

codecov bot commented Jan 23, 2026

Codecov Report

❌ Patch coverage is 80.16194% with 49 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.78%. Comparing base (dc94210) to head (79641a5).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
hugr-passes/src/untuple.rs 72.58% 16 Missing and 1 partial ⚠️
hugr-passes/src/normalize_cfgs.rs 44.44% 14 Missing and 1 partial ⚠️
hugr-passes/src/monomorphize.rs 77.77% 1 Missing and 3 partials ⚠️
hugr-passes/src/replace_types.rs 66.66% 4 Missing ⚠️
hugr-passes/src/dead_funcs.rs 96.00% 3 Missing ⚠️
hugr-passes/src/composable.rs 66.66% 2 Missing ⚠️
hugr-passes/src/const_fold.rs 91.66% 1 Missing and 1 partial ⚠️
hugr-passes/src/dead_code.rs 88.23% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2836      +/-   ##
==========================================
- Coverage   83.83%   83.78%   -0.05%     
==========================================
  Files         269      269              
  Lines       54021    54206     +185     
  Branches    47974    48159     +185     
==========================================
+ Hits        45286    45414     +128     
- Misses       6320     6378      +58     
+ Partials     2415     2414       -1     
Flag Coverage Δ
python 88.65% <ø> (ø)
rust 83.16% <80.16%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@acl-cqc acl-cqc force-pushed the acl/pass-scopes branch 2 times, most recently from 05cf563 to dfe5791 Compare February 11, 2026 15:18
@acl-cqc acl-cqc changed the title WIP: update passes to use PassScope feat: update passes to use PassScope where non-breaking Feb 11, 2026
@acl-cqc acl-cqc requested a review from aborgna-q February 11, 2026 15:27
@acl-cqc acl-cqc marked this pull request as ready for review March 5, 2026 17:47
@acl-cqc acl-cqc requested a review from a team as a code owner March 5, 2026 17:47
Comment on lines 105 to 111
/// Create a new untuple pass with the given configuration
#[must_use]
pub fn new_scoped(scope: PassScope) -> Self {
Self {
scope: Either::Left(scope),
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? It's the same as
UntuplePass::default().with_scope(...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, indeed. I think it's "nice" to have everywhere, but maybe inconsistent to have it here and not everywhere else. I'll remove it here?

Comment on lines +914 to +917
/// Sets the scope within which the pass will operate. Note that this pass respects
/// neither [PassScope::preserve_interface] nor [PassScope::recursive] as the former
/// would be contrary to the goals of the pass and non-recursion generally leads to
/// invalid Hugrs. Hence, really only the [PassScope::root] affects the pass.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense, but does it mean we need to adjust the PassScope definition somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PassScope specifically says recursive==False is only a hint (recursive==True is a requirement, and we do that). So I think we're ok (within bounds of the contract), and it's merely good practice to note what we do here.

preserve_interface does mention that it only really applies to optimization passes I think, and this is not one. Have a look, perhaps it's not strong enough?

Copy link
Collaborator

@aborgna-q aborgna-q Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for recursive I see no issue.

preserve_interface is a bit iffy, but I think it works if it's only for optimizations.
I'd add a note in the scope definitions making that explicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See here:

/// Run the pass on the whole Hugr, regardless of the entrypoint.
///
/// For lowering passes, signature changes etc. should be applied across the hugr.
///
/// For optimization passes, the inner [Preserve] details which parts must
/// have their interface preserved.

But I'll add a couple more notes


use super::{ComposablePass, IfThen, ValidatePassError, ValidatingPass, validate_if_test};

pub(crate) fn run_validating<P: ComposablePass<H>, H: HugrMut>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, this could be outside test, even public; it's a simple-enough utility function

@acl-cqc acl-cqc requested a review from aborgna-q March 6, 2026 12:59
@acl-cqc acl-cqc added this to the hugr-rs 0.25.X milestone Mar 6, 2026
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[expect(deprecated)]
pub use monomorphize::{MonomorphizePass, mangle_name, monomorphize};

@acl-cqc acl-cqc enabled auto-merge March 6, 2026 16:54
@acl-cqc acl-cqc added this pull request to the merge queue Mar 6, 2026
Merged via the queue into main with commit c774c48 Mar 6, 2026
31 checks passed
@acl-cqc acl-cqc deleted the acl/pass-scopes branch March 6, 2026 17:01
@hugrbot hugrbot mentioned this pull request Mar 6, 2026
github-merge-queue bot pushed a commit that referenced this pull request Mar 6, 2026
## 🤖 New release

* `hugr-model`: 0.25.6 -> 0.25.7 (✓ API compatible changes)
* `hugr-core`: 0.25.6 -> 0.25.7 (✓ API compatible changes)
* `hugr-llvm`: 0.25.6 -> 0.25.7 (✓ API compatible changes)
* `hugr-passes`: 0.25.6 -> 0.25.7 (✓ API compatible changes)
* `hugr-persistent`: 0.4.6 -> 0.4.7 (✓ API compatible changes)
* `hugr`: 0.25.6 -> 0.25.7 (✓ API compatible changes)
* `hugr-cli`: 0.25.6 -> 0.25.7 (✓ API compatible changes)

<details><summary><i><b>Changelog</b></i></summary><p>

## `hugr-model`

<blockquote>

##
[0.25.6](hugr-model-v0.25.5...hugr-model-v0.25.6)
- 2026-02-20

### New Features

- Remove size limitation for binary envelopes
([#2880](#2880))
</blockquote>

## `hugr-core`

<blockquote>

##
[0.25.7](hugr-core-v0.25.6...hugr-core-v0.25.7)
- 2026-03-06

### Documentation

- added examples in docs srtring
([#2920](#2920))
</blockquote>

## `hugr-llvm`

<blockquote>

##
[0.25.6](hugr-llvm-v0.25.5...hugr-llvm-v0.25.6)
- 2026-02-20

### New Features

- Add error context when lowering hugrs to LLVM
([#2869](#2869))
</blockquote>

## `hugr-passes`

<blockquote>

##
[0.25.7](hugr-passes-v0.25.6...hugr-passes-v0.25.7)
- 2026-03-06

### Documentation

- added examples in docs srtring
([#2920](#2920))

### New Features

- Define pass application scopes
([#2772](#2772))
- Modify dead code elimination pass to remove unreachable basic blocks
([#2884](#2884))
- Add non-generic `with_scope` method for composable passes
([#2910](#2910))
- update passes to use PassScope where non-breaking
([#2836](#2836))
</blockquote>

## `hugr-persistent`

<blockquote>

##
[0.4.0](hugr-persistent-v0.3.4...hugr-persistent-v0.4.0)
- 2025-12-22

### New Features

- [**breaking**] Remove `RootCheckable`
([#2704](#2704))
- [**breaking**] Bump MSRV to Rust 1.89
([#2747](#2747))
- [**breaking**] Type-safe access for node metadata
([#2755](#2755))

### Refactor

- [**breaking**] Remove multiple deprecated definitions
([#2751](#2751))
</blockquote>

## `hugr`

<blockquote>

##
[0.25.7](hugr-v0.25.6...hugr-v0.25.7)
- 2026-03-06

### Documentation

- added examples in docs srtring
([#2920](#2920))

### New Features

- Define pass application scopes
([#2772](#2772))
- Modify dead code elimination pass to remove unreachable basic blocks
([#2884](#2884))
- Add non-generic `with_scope` method for composable passes
([#2910](#2910))
- update passes to use PassScope where non-breaking
([#2836](#2836))
</blockquote>

## `hugr-cli`

<blockquote>

##
[0.25.6](hugr-cli-v0.25.5...hugr-cli-v0.25.6)
- 2026-02-20

### New Features

- Add s expression format to envelope formats
([#2864](#2864))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
@hugrbot hugrbot mentioned this pull request Mar 9, 2026
github-merge-queue bot pushed a commit that referenced this pull request Mar 11, 2026
…_scope (#2871)

Following #2836, the remaining passes, where breakage was inevitable -
mostly passes that were an empty struct (without any previous config).

Non-local-edges stuff needed some API changes where I did some
deprecation, but we could just break.

RedundantOrderEdgesPass I've removed the old config methods - we should
perhaps deprecate first, but some breakage seems inevitable as the empty
struct derived Copy.

LocalizeEdges I've deprecated+moved top-level `ensure_no_nonlocal_edges`
into a struct method `check_no_nonlocal_edges`, as IIUC it's used, but
I've removed top-level `nonlocal_edges` altogether, it doesn't seem to
be used.

Also remove the default impl for `ComposablePass::with_scope_internal`,
to check we got 'em all.

BREAKING CHANGE: InlineDFGsPass, LocalizeEdgesPass, MonomorphizePass,
RedundantOrderEdgesPass must be constructed via ::default(); remove
RedundantOrderEdgesPass::recursive and don't derive Copy;
ComposablePass::with_scope_internal requires impl; remove toplevel `fn
nonlocal_edges`
github-merge-queue bot pushed a commit to Quantinuum/tket2 that referenced this pull request Mar 23, 2026
Adds support for `PassScope`s in the pass definitions. See
Quantinuum/hugr#2772.

~Requires a hugr release with the `PassScope` definition and
Quantinuum/hugr#2910

~Many of these also call other passes from `hugr_passes`. While we pass
the scope config along, proper support requires
Quantinuum/hugr#2836 and
Quantinuum/hugr#2871

This is a rust-only change, the python interface will follow up.

BREAKING CHANGE: Multiple unit-like pass structs must now be constructed
using a `::default()` call instead.
BREAKING CHANGE: `QSystemPass` is now a `ComposablePass`. Import the
trait to call `run`.
BREAKING CHANGE: `QSystemPass` no longer implements `Copy`.
BREAKING CHANGE: Renamed
`tket_qsystem::extension::qsystem::lower_tket_op` to `lower_tket_ops`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants