Skip to content

Restrict unsafe blocks to single statements where possible.#15806

Open
Cryoris wants to merge 10 commits into
Qiskit:mainfrom
Cryoris:unsafe-surgeon-mode
Open

Restrict unsafe blocks to single statements where possible.#15806
Cryoris wants to merge 10 commits into
Qiskit:mainfrom
Cryoris:unsafe-surgeon-mode

Conversation

@Cryoris

@Cryoris Cryoris commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Following up on #15106 (comment) by reducing the sizes of unsafe statements in the codebase.

Details and comments

To see which part of the code is unsafe, it is good to restrict unsafe blocks to the smallest sensible code unit. Clippy also has a feature to check this, cargo clippy -- -W clippy::multiple_unsafe_ops_per_block. This commit reduces some pretty large unsafe blocks to smaller ones to improve our code structure. Clippy actually flags any snippet that has more than a single statement, but I left unsafe blocks in place where every single statement was actually unsafe.

To see which part of the code is unsafe, it is good to restrict `unsafe` blocks to the smallest sensible code unit. Clippy also has a feature to check this, `cargo clippy -- -W clippy::multiple_unsafe_ops_per_block`. This commit reduces some pretty large unsafe blocks to smaller ones to improve our code structure. Clippy actually flags _any_ snippet that has more than a single statement, but I left unsafe blocks in place where every single statement was actually unsafe.
@Cryoris Cryoris added this to the 2.5.0 milestone Mar 13, 2026
@Cryoris Cryoris requested a review from a team as a code owner March 13, 2026 12:52
@Cryoris Cryoris added type: qa Issues and PRs that relate to testing and code quality Changelog: None Do not include in the GitHub Release changelog. labels Mar 13, 2026
@qiskit-bot

Copy link
Copy Markdown
Collaborator

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core

@gadial gadial left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall looks good to me. Left minor comments.

@@ -1569,26 +1568,22 @@ pub unsafe extern "C" fn qk_target_op_clear(op: *mut CTargetOp) {

// SAFETY: As per documentation, data from pointers contained in CTargetOp
// originates from rust code and are constructed internally with vecs and CStrings.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does this cover the mut_ptr_as_ref(op) operation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It does not! Let me adjust 🙂

Comment thread crates/cext/src/dag.rs Outdated
@coveralls

coveralls commented Apr 29, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 27691072097

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage increased (+0.03%) to 87.559%

Details

  • Coverage increased (+0.03%) from the base build.
  • Patch coverage: 26 uncovered changes across 2 files (1008 of 1034 lines covered, 97.49%).
  • 46 coverage regressions across 7 files.

Uncovered Changes

File Changed Covered %
crates/cext/src/transpiler/transpile_function.rs 24 0 0.0%
crates/cext/src/dag.rs 24 22 91.67%
Total (9 files) 1034 1008 97.49%

Coverage Regressions

46 previously-covered lines in 7 files lost coverage.

File Lines Losing Coverage Coverage
crates/transpiler/src/passes/sabre/route.rs 21 95.02%
crates/qasm2/src/parse.rs 12 97.15%
crates/circuit/src/parameter/symbol_expr.rs 4 74.1%
crates/qasm2/src/lex.rs 4 91.77%
crates/transpiler/src/passes/sabre/layer.rs 3 97.89%
crates/cext/src/transpiler/target.rs 1 89.44%
crates/qasm2/src/expr.rs 1 93.88%

Coverage Stats

Coverage Status
Relevant Lines: 127300
Covered Lines: 111462
Line Coverage: 87.56%
Coverage Strength: 1009044.54 hits per line

💛 - Coveralls

Comment thread crates/cext/src/circuit.rs Outdated
let _ = unsafe { CString::from_raw(inst.name) };
inst.name = std::ptr::null_mut();
}
inst.name = std::ptr::null_mut();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Isn't this causing a memory leak now because we're no longer dropping the existing string pointer? Don't we need rust to take ownership of the string again so it frees the allocation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh this looks like a merge gone wrong, I didn't mean to remove that... thanks for catching that!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Now, you definitely want to address this comment.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Addressed in 17d41e5.

let target = if target.is_null() {
None
} else {
Some(unsafe { const_ptr_as_ref(target) })

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

On the other places you moved the safety comment inside the else body to be with the reduced scope unsafe calls.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Cryoris, a reminder to address this comment.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Addressed in 17d41e5.

pub unsafe extern "C" fn qk_neighbors_is_all_to_all(neighbors: *const CNeighbors) -> bool {
// SAFETY: per documentation, `neighbors` points to a valid initialized `CNeighbors`.
unsafe { (*neighbors).neighbors.is_null() && (*neighbors).partition.is_null() }
let neighbors = unsafe { const_ptr_as_ref(neighbors) };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Doesn't this change the requirements for neighbors? This function asserts alignment while it is not documented as being required for this function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm.. isn't that an oversight from before, though? I thought that the pointer needed to be aligned to be dereferenced safely.

@alexanderivrii alexanderivrii left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks Julien! Please address Matthew's comments, especially the one about memory leak (with the merge gone wrong).

Running cargo clippy -- -W clippy::multiple_unsafe_ops_per_block (as you suggest in the PR summary) flags other potential spots for improvement, but we can handle them in a follow-up.

Comment on lines +1637 to +1645
if gate.num_params() == 0 {
// if there's no parameters, params is NULL and it is not safe to call from_raw_parts
smallvec![]
} else {
// SAFETY: Per the documentation, params is readable for num_params elements of f64
unsafe { ::std::slice::from_raw_parts(params, gate.num_params() as usize) }
.iter()
.map(|p| Param::Float(*p))
.collect()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice! (Arguably, slightly outside of the original scope of the PR, but I am fine with this).

let target = if target.is_null() {
None
} else {
Some(unsafe { const_ptr_as_ref(target) })

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Cryoris, a reminder to address this comment.

Comment on lines +457 to +458
// SAFETY: Per documentation, the params points is compatible with the gate and safe to read.
let params = unsafe { parse_params(gate, params) };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like this refactoring of the params code, using an already existing function instead. But just pointing out that this technically more than a 1-line change which you promised before I agreed to review :).

Comment thread crates/cext/src/circuit.rs Outdated
let _ = unsafe { CString::from_raw(inst.name) };
inst.name = std::ptr::null_mut();
}
inst.name = std::ptr::null_mut();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Now, you definitely want to address this comment.

@alexanderivrii alexanderivrii requested a review from mtreinish June 17, 2026 09:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Changelog: None Do not include in the GitHub Release changelog. type: qa Issues and PRs that relate to testing and code quality

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

7 participants