Background
PR #4409 introduced a static isBoundHandle walk in lib/Frontend/nvqpp/ConvertExpr.cpp that diagnoses
bool b = h; when h was default-constructed. The check is intentionally narrow and has two known unsoundness gaps, documented inline as "defer to a follow-up.
Gap 1: function arguments
__qpu__ bool f(cudaq::measure_handle h) { return h; }
isBoundHandle short-circuits on BlockArgument SSA values (if (!load) return true;). The bridge silently accepts the discriminate even though the caller may pass a default-constructed handle. Solving requires either per-callee verification at every call site that the argument is bound, or a runtime sentinel check at every quake.discriminate.
Gap 2: aggregate / array per-element tracking
For measure_handle hs[N]; and struct Holder { measure_handle h; };, any store anywhere into the base alloca is treated as binding regardless of which element / member is loaded. Per-cc.compute_ptr-index reasoning needs SSA-value index resolution, deferred from PR #4409.
Background
PR #4409 introduced a static
isBoundHandlewalk inlib/Frontend/nvqpp/ConvertExpr.cppthat diagnosesbool b = h;whenhwas default-constructed. The check is intentionally narrow and has two known unsoundness gaps, documented inline as "defer to a follow-up.Gap 1: function arguments
isBoundHandleshort-circuits onBlockArgumentSSA values (if (!load) return true;). The bridge silently accepts the discriminate even though the caller may pass a default-constructed handle. Solving requires either per-callee verification at every call site that the argument is bound, or a runtime sentinel check at everyquake.discriminate.Gap 2: aggregate / array per-element tracking
For
measure_handle hs[N];andstruct Holder { measure_handle h; };, any store anywhere into the base alloca is treated as binding regardless of which element / member is loaded. Per-cc.compute_ptr-index reasoning needs SSA-value index resolution, deferred from PR #4409.