Skip to content

✨ Add OpenQASM-to-QC translation#1671

Open
J4MMlE wants to merge 23 commits into
munich-quantum-toolkit:more-controlfrom
J4MMlE:open-qasm
Open

✨ Add OpenQASM-to-QC translation#1671
J4MMlE wants to merge 23 commits into
munich-quantum-toolkit:more-controlfrom
J4MMlE:open-qasm

Conversation

@J4MMlE
Copy link
Copy Markdown
Contributor

@J4MMlE J4MMlE commented Apr 27, 2026

Description

This PR adds a direct OpenQASM-to-QC translation that bypasses the qc::QuantumComputation.

The new flow reuses the existing parser and walks the AST directly to emit QC dialect ops. Skipping QuantumComputation removes an intermediate step and enables previously unsupported QASM3 features, such as gate modifiers, classical computations, and control flow structures.

What works

  • All standard gates plus Qiskit-style MCX variants
  • Gate modifiers: ctrl @, negctrl @, inv @, nested combinations
  • Register declarations, qubit/bit allocation
  • Measure, reset, barrier
  • if/else over quantum statements
  • Hardware qubits convert to qc.static
  • Broadcasting (register-width gate calls)

Current limitations

  • Gate parameters must be compile-time constants
  • pow modifier is unsupported (but will/should be once ✨ Add power modifier to MLIR #1603 is merged)
  • Layout pragmas are unsupported (they have no equivalent in QC)

Checklist

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

@mergify mergify Bot added the conflict label Apr 27, 2026
@burgholzer burgholzer added feature New feature or request c++ Anything related to C++ code MLIR Anything related to MLIR labels Apr 28, 2026
@burgholzer burgholzer added this to the MLIR Support milestone Apr 28, 2026
@mergify mergify Bot removed the conflict label Apr 29, 2026
Comment thread mlir/tools/mqt-cc/mqt-cc.cpp Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

❌ Patch coverage is 70.96774% with 135 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp 70.9% 135 Missing ⚠️

📢 Thoughts on this report? Let us know!

Comment thread mlir/unittests/programs/qasm_programs/alloc_large_register.qasm Outdated
Comment thread mlir/tools/mqt-cc/mqt-cc.cpp
Comment thread mlir/unittests/programs/qasm_programs.cpp
@denialhaag denialhaag marked this pull request as ready for review May 21, 2026 16:09
@denialhaag
Copy link
Copy Markdown
Member

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added OpenQASM 3 to QC dialect translation functionality, enabling direct conversion of QASM3 programs including gates, measurements, resets, barriers, and measurement-driven conditionals.
  • Tests

    • Added comprehensive test suite for QASM3 translation covering single and multi-qubit gates, controlled variants, and conditional constructs.

Walkthrough

Adds a direct OpenQASM3 → MLIR QC dialect translator: public API header, visitor-based importer with gate dispatch, complete translation logic (gates, measurements, controls, compound gates, if/else), tool integration into mqt-cc, and parameterized unit tests with shared QASM fixtures.

Changes

QASM3-to-QC Direct Importer

Layer / File(s) Summary
Public API Declaration
mlir/include/mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h
Declares two translateQASM3ToQC overloads accepting filename or input stream, returning OwningOpRef<ModuleOp> with [[nodiscard]].
Gate Dispatch & Importer Implementation
mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
Adds static gate-name dispatch mapping QASM3 gates (and aliases) to QCProgramBuilder emission lambdas.
Importer Scaffolding & Declarations
mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
Defines importer state, qubit/classical register scopes, builtins, and AST visitation for version declarations and sized-type allocations.
Statement Translation & Measurement Wiring
mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
Translates gate-call statements with parameter const-eval, ctrl/negctrl/inv parsing, operand expansion, duplicate checks; emits MeasureOp for measurement assignments; lowers barrier/reset and if regions.
Emit Helpers & Compound Expansion
mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
Implements emitGate (negative-control bracketing, controlled/inversion handling) and inline expansion of compound/user-defined gates with local qubit bindings.
If-Region Helpers & Validation
mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
emitBlockStatements enforcement, translateCondition (single-bit/unary forms), lookupBitValue measured-before-use checks, operand/classical resolution and constant parsing helpers.
Public Implementations
mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
Public translateQASM3ToQC (stream + filename) parse via qasm3::Parser, run importer, print exceptions to llvm::errs(), and return ModuleOp or nullptr.
Build Configuration & Tool Integration
mlir/lib/Dialect/QC/Translation/CMakeLists.txt, mlir/tools/mqt-cc/mqt-cc.cpp
CMake adds TranslateQASM3ToQC.cpp and links MQT::CoreQASM; mqt-cc now calls mlir::qc::translateQASM3ToQC, standardizes llvm:: utilities, and adjusts dialect registration order.
Test Fixtures & Translation Validation
mlir/unittests/programs/qasm_programs.h, mlir/unittests/programs/qasm_programs.cpp, mlir/unittests/programs/CMakeLists.txt, mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp, mlir/unittests/Dialect/QC/Translation/CMakeLists.txt
Adds MLIRQASMPrograms exposing many QASM3 snippets and a parameterized GoogleTest that translates each snippet and asserts equivalence with a reference QC IR built by QCProgramBuilder.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • burgholzer
  • denialhaag

Poem

🐰 In circuits I hop, from parse to QC,
Gates, measures, and ifs — I translate with glee.
Controls get bracketed, compounds expand,
Tests march in line across this small land,
A rabbit's applause for the importer, whee!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description covers the main objective, implementation details, and supported features. However, the changelog entry checkbox is unchecked, indicating a required section is incomplete. Add entries to the changelog documenting this new feature and its capabilities, as indicated by the unchecked checkbox in the provided template.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add OpenQASM-to-QC translation' clearly summarizes the main change: introducing direct QASM-to-QC translation, which is the primary feature of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Infer (1.2.0)
mlir/tools/mqt-cc/mqt-cc.cpp

mlir/tools/mqt-cc/mqt-cc.cpp:11:10: fatal error: 'mlir/Compiler/CompilerPipeline.h' file not found
11 | #include "mlir/Compiler/CompilerPipeline.h"
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
mlir/tools/mqt-cc/mqt-cc.cpp:97:65-111:1: ERROR translating statement 'CompoundStmt'
Aborting translation of method 'loadMLIRFile' in file 'mlir/tools/mqt-cc/mqt-cc.cpp': "Assert_failure src/clang/cAst_utils.ml:249:53"
Uncaught Internal Error: "Assert_failure src/clang/cAst_utils.ml:249:53"
Error backtrace:
Raised at ClangFrontend__CAst_utils.get_decl_from_typ_ptr in file "src/clang/cAst_utils.ml", line 249, characters 53-65
Called from ClangFrontend__CTrans.CTrans_funct.get_destructor_decl_ref in file "src/clang/cTrans.ml", line 658, characters 12-59
Called from ClangFrontend__CTrans.CTrans_funct.destructor_calls.(fun) in file "src/clang/cTrans.ml", line 2048, characters 12-69
Called from Base__List.rev_filter_map.loop in file "src/list.ml", line 944, characters 13-1

... [truncated 2200 characters] ...

od in file "src/clang/cFrontend_decl.ml" (inlined), line 54, characters 4-52
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.function_decl in file "src/clang/cFrontend_decl.ml", line 90, characters 12-151
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.translate_one_declaration in file "src/clang/cFrontend_decl.ml", line 453, characters 10-56
Called from Stdlib__List.iter in file "list.ml", line 110, characters 12-15
Called from Stdlib__List.iter in file "list.ml" (inlined), line 110, characters 17-25
Called from Base__List0.iter in file "src/list0.ml" (inlined), line 25, characters 16-35
Called from ClangFrontend__CFrontend.compute_icfg in file "src/clang/cFrontend.ml", line 28, characters 6-130
Called from ClangFrontend__Capture.run_clang_frontend in file "s

mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp

mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp:11:10: fatal error: 'mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h' file not found
11 | #include "mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h"
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp:248:23-282:3: ERROR translating statement 'CompoundStmt'
Aborting translation of method 'mlir::qc::anonymous_namespace_mlir_lib_Dialect_QC_Translation_TranslateQASM3ToQC.cpp::MLIRQasmImporter::initBuiltins' in file 'mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp': "Assert_failure src/clang/cAst_utils.ml:249:53"
Aborting translation of method 'mlir::qc::anonymous_namespace_mlir_lib_Dialect_QC_Translation_TranslateQASM3ToQC.cpp::MLIRQasmImporter::MLIRQasmImporter' in file 'mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp': "Assert_failure src/clang/cAst_utils.ml:249:53"
Uncaught Internal Error: "Assert_failure src/clang/cAst_util

... [truncated 2200 characters] ...

decl_funct.add_method.f in file "src/clang/cFrontend_decl.ml", line 48, characters 12-91
Called from ClangFrontend__CFrontend_errors.protect in file "src/clang/cFrontend_errors.ml", line 37, characters 6-10
Re-raised at IStdlib__IExn.reraise_if in file "src/istd/IExn.ml" (inlined), line 18, characters 15-63
Called from ClangFrontend__CFrontend_errors.protect in file "src/clang/cFrontend_errors.ml", line 48, characters 6-141
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.add_method in file "src/clang/cFrontend_decl.ml" (inlined), line 54, characters 4-52
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.process_method_decl.add_method_if_create_procdesc in file "src/clang/cFrontend_decl.ml" (inlined), line 123, characters 16-158
Called from ClangFrontend__CFronte

mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp

mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp:11:10: fatal error: 'TestCaseUtils.h' file not found
11 | #include "TestCaseUtils.h"
| ^~~~~~~~~~~~~~~~~
1 error generated.
mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp:71:50-99:1: ERROR translating statement 'CompoundStmt'
Aborting translation of method 'TEST_P' in file 'mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp': "Assert_failure src/clang/cAst_utils.ml:249:53"
Uncaught Internal Error: "Assert_failure src/clang/cAst_utils.ml:249:53"
Error backtrace:
Raised at ClangFrontend__CAst_utils.get_decl_from_typ_ptr in file "src/clang/cAst_utils.ml", line 249, characters 53-65
Called from ClangFrontend__CTrans.CTrans_funct.get_destructor_decl_ref in file "src/clang/cTrans.ml", line 658, characters 12-59
Called from ClangFrontend__CTrans.CTrans_funct.destructor_calls.(fun) in file "src/clang/cTrans.ml", line 2048, characters 12-69
Called from Base__List.rev_filter_map.loo

... [truncated 2200 characters] ...

tend__CFrontend_decl.CFrontend_decl_funct.add_method in file "src/clang/cFrontend_decl.ml" (inlined), line 54, characters 4-52
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.function_decl in file "src/clang/cFrontend_decl.ml", line 90, characters 12-151
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.translate_one_declaration in file "src/clang/cFrontend_decl.ml", line 453, characters 10-56
Called from Stdlib__List.iter in file "list.ml", line 110, characters 12-15
Called from Stdlib__List.iter in file "list.ml" (inlined), line 110, characters 17-25
Called from Base__List0.iter in file "src/list0.ml" (inlined), line 25, characters 16-35
Called from ClangFrontend__CFrontend.compute_icfg in file "src/clang/cFrontend.ml", line 28, characters 6-130
Called from Cl


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mlir/tools/mqt-cc/mqt-cc.cpp (1)

142-146: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Remove duplicate MemRefDialect registration.

memref::MemRefDialect and mlir::memref::MemRefDialect are the same dialect here, so this registers it twice. Keep a single insertion to avoid redundant setup and future confusion.

Suggested diff
   registry.insert<func::FuncDialect>();
   registry.insert<memref::MemRefDialect>();
   registry.insert<scf::SCFDialect>();
   registry.insert<LLVM::LLVMDialect>();
-  registry.insert<mlir::memref::MemRefDialect>();
   registry.insert<qtensor::QTensorDialect>();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/tools/mqt-cc/mqt-cc.cpp` around lines 142 - 146, The code registers the
same dialect twice via registry.insert<memref::MemRefDialect>() and
registry.insert<mlir::memref::MemRefDialect>(); remove one of these duplicate
registry.insert calls (keep either the unqualified memref::MemRefDialect or the
fully-qualified mlir::memref::MemRefDialect for clarity) so the dialect is
registered only once and the subsequent
registry.insert<qtensor::QTensorDialect>() remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mlir/include/mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h`:
- Around line 28-32: Add a Doxygen comment for the stream overload
translateQASM3ToQC(MLIRContext* context, std::istream& input) matching the
filename overload: describe the function purpose, document parameters (context
and input), state the error/reporting behavior (errors are reported via
llvm::errs()), and specify the return contract (returns an OwningOpRef<ModuleOp>
or nullptr on failure). Place the comment immediately above the second
declaration so the stream-variant’s contract is clear to callers.

In `@mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp`:
- Around line 891-909: The helper function convertComparisonKind is currently
dead code because the BinaryExpression branch in translateCondition throws
unconditionally; either remove convertComparisonKind to avoid unusedFunction
warnings or rewire translateCondition's BinaryExpression case to call
convertComparisonKind (and handle result) so register comparisons are actually
translated; locate convertComparisonKind and the BinaryExpression branch in
translateCondition and implement one of the two fixes consistently (delete the
unused static function and its declaration, or replace the throw in
translateCondition's BinaryExpression path with logic that maps
::qc::ComparisonKind via convertComparisonKind and uses the resulting
arith::CmpIPredicate).
- Around line 587-610: The modifier-controls loop iterates ctrlIdx and indexes
expandedOperands without checking bounds, which can UB if there are fewer
operands; add a guard inside the for (const auto& [n, positive] : ctrlSpec) /
inner for (size_t i = 0; i < n; ++i, ++ctrlIdx) before accessing
expandedOperands[ctrlIdx] to verify ctrlIdx < expandedOperands.size() and that
expandedOperands[ctrlIdx].size() == 1, and if not throw a qasm3::CompilerError
(using stmt->debugInfo) with a clear message (similar to the existing implicit
OQ2 control check) so posControls/negControls pushes are safe.
- Around line 666-678: The loop uses concat<const Value>(iterQubits,
posControls, negControls) which applies a const qualifier to the MLIR handle
type; change the call to concat<Value>(...) (drop the const) so the range yields
plain Value handles and matches project conventions—update the concat call where
seen (llvm::SmallDenseSet<Value> seen) and the subsequent for (auto q : ...)
loop that precedes emitGate(dispIt->second, iterQubits, params, posControls,
negControls, invert).
- Around line 825-837: The unary-negation branch currently assumes
unaryExpr->operand is an IndexedIdentifier and passes the result of
std::dynamic_pointer_cast<qasm3::IndexedIdentifier>(unaryExpr->operand) directly
to lookupBitValue, which can yield nullptr and cause a crash; update this branch
to check that idExpr is non-null and if it is null throw a CompilerError (or use
the project's existing error reporting) with a clear message (e.g., "unsupported
operand for unary negation: expected IndexedIdentifier") instead of calling
lookupBitValue; reference UnaryExpression, IndexedIdentifier, lookupBitValue and
the CompilerError path when locating and changing the code.
- Around line 742-766: The compound-gate body loop in the bodyFn lambda silently
drops unhandled statement kinds; update the for-loop over gate.body (inside
bodyFn) to add a terminal else branch that emits a clear diagnostic/throws
(using the statement's debugInfo when available) rather than ignoring unknown
types, so any unrecognized bodyStmt triggers a failure; locate the bodyFn lambda
and add the else to complement the existing
GateCallStatement/BarrierStatement/ResetStatement branches (which call
applyGateCallStatement, resolveGateOperandInScope, builder.barrier,
builder.reset) and produce a descriptive error/exception mentioning the
unexpected statement kind and debugInfo.
- Around line 312-331: The default branch of the switch on sizedTy->type
currently does nothing and can silently skip allocating registers (leaving
declarations populated but no register in qubitRegisters or classicalRegisters);
update the default case inside the switch (the branch handling sizedTy->type) to
explicitly fail fast for unsupported sized types (e.g., Float, Angle) by
reporting a clear error via the translation context/builder error API (or
throwing/returning an error) that includes the identifier and the unsupported
type, so unsupported declarations are rejected immediately instead of causing
later failures.

---

Outside diff comments:
In `@mlir/tools/mqt-cc/mqt-cc.cpp`:
- Around line 142-146: The code registers the same dialect twice via
registry.insert<memref::MemRefDialect>() and
registry.insert<mlir::memref::MemRefDialect>(); remove one of these duplicate
registry.insert calls (keep either the unqualified memref::MemRefDialect or the
fully-qualified mlir::memref::MemRefDialect for clarity) so the dialect is
registered only once and the subsequent
registry.insert<qtensor::QTensorDialect>() remains unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 49e31367-7ed5-4787-8afb-2adf9d0d361d

📥 Commits

Reviewing files that changed from the base of the PR and between e8b1bb8 and 87601be.

📒 Files selected for processing (10)
  • mlir/include/mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h
  • mlir/lib/Dialect/QC/Translation/CMakeLists.txt
  • mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
  • mlir/tools/mqt-cc/mqt-cc.cpp
  • mlir/unittests/Dialect/QC/Translation/CMakeLists.txt
  • mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp
  • mlir/unittests/programs/CMakeLists.txt
  • mlir/unittests/programs/qasm_programs.cpp
  • mlir/unittests/programs/qasm_programs.h
  • test/circuits/bell.qasm
💤 Files with no reviewable changes (1)
  • test/circuits/bell.qasm

Comment thread mlir/include/mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h
Comment thread mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
Comment thread mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
Comment thread mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
Comment thread mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp Outdated
Comment thread mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
Comment thread mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp Outdated
@denialhaag
Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp (1)

369-406: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

visitGateStatement silently accepts user-defined gates that later fail with a misleading "Unknown gate" error.

For a non-opaque, non-standard gate declaration, this method validates paramNames/qubitNames into locals that are never stored, and never inserts the gate into gates. When the same gate is invoked, applyGateCallStatement (line 533-534) raises Unknown gate '<name>', which is misleading — the gate is in fact a compound gate that was intentionally dropped (per the "Drop support for compound gates for now" commit).

Either fail fast here with a clear diagnostic, or remove the dead local validation. The former gives users a useful error pointing at the declaration site.

♻️ Suggested change — fail fast with a clear message
   void
   visitGateStatement(std::shared_ptr<qasm3::GateDeclaration> stmt) override {
     auto id = stmt->identifier;
     if (stmt->isOpaque) {
       if (!gates.contains(id)) {
         throw qasm3::CompilerError("Unsupported opaque gate '" + id + "'.",
                                    stmt->debugInfo);
       }
       return;
     }
     if (gates.contains(id)) {
       if (std::dynamic_pointer_cast<qasm3::StandardGate>(gates[id])) {
         return; // ignore redeclaration of standard gate
       }
       throw qasm3::CompilerError("Gate '" + id + "' already declared.",
                                  stmt->debugInfo);
     }
-    std::vector<std::string> paramNames;
-    for (const auto& p : stmt->parameters->identifiers) {
-      if (std::ranges::find(paramNames, p->identifier) != paramNames.end()) {
-        throw qasm3::CompilerError("Parameter '" + p->identifier +
-                                       "' already declared in gate '" + id +
-                                       "'.",
-                                   stmt->debugInfo);
-      }
-      paramNames.push_back(p->identifier);
-    }
-    std::vector<std::string> qubitNames;
-    for (const auto& q : stmt->qubits->identifiers) {
-      if (std::ranges::find(qubitNames, q->identifier) != qubitNames.end()) {
-        throw qasm3::CompilerError("Qubit '" + q->identifier +
-                                       "' already declared in gate '" + id +
-                                       "'.",
-                                   stmt->debugInfo);
-      }
-      qubitNames.push_back(q->identifier);
-    }
+    throw qasm3::CompilerError(
+        "User-defined (compound) gate '" + id +
+            "' is not supported by direct MLIR import.",
+        stmt->debugInfo);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp` around lines 369 -
406, The visitGateStatement function currently validates parameters/qubits for
non-opaque, non-standard gate declarations but never inserts the gate into the
gates map, causing later "Unknown gate" errors; change visitGateStatement to
fail fast for such compound (non-opaque, non-standard) gate declarations by
throwing a qasm3::CompilerError (use stmt->debugInfo) with a clear message like
"Compound gates are not supported: '<id>'" instead of silently dropping them
(refer to visitGateStatement and applyGateCallStatement to locate the flow), so
users get a direct diagnostic at the declaration site.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp`:
- Around line 369-406: The visitGateStatement function currently validates
parameters/qubits for non-opaque, non-standard gate declarations but never
inserts the gate into the gates map, causing later "Unknown gate" errors; change
visitGateStatement to fail fast for such compound (non-opaque, non-standard)
gate declarations by throwing a qasm3::CompilerError (use stmt->debugInfo) with
a clear message like "Compound gates are not supported: '<id>'" instead of
silently dropping them (refer to visitGateStatement and applyGateCallStatement
to locate the flow), so users get a direct diagnostic at the declaration site.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4f89c1f1-14f7-475f-bfe6-57b1e1604f05

📥 Commits

Reviewing files that changed from the base of the PR and between 87601be and bebb1f7.

📒 Files selected for processing (3)
  • mlir/include/mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h
  • mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
  • mlir/tools/mqt-cc/mqt-cc.cpp

@denialhaag denialhaag force-pushed the open-qasm branch 2 times, most recently from 78a4271 to 4ec599d Compare May 21, 2026 19:45
@denialhaag
Copy link
Copy Markdown
Member

Follow-ups are collected in #1733.

@denialhaag
Copy link
Copy Markdown
Member

denialhaag commented May 22, 2026

Thanks a lot for your work here, @J4MMlE! I have mainly added tests and reduced the PR's footprint to only support concepts we can currently test. I have added the relevant commits (3b8a228 and 4ec599d) to their respective issues.

I'd say this is ready for a review now. 😌

@denialhaag denialhaag force-pushed the open-qasm branch 2 times, most recently from e9ab789 to ad5a0af Compare June 2, 2026 18:29
@denialhaag denialhaag changed the base branch from main to more-control June 2, 2026 18:29
@denialhaag
Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

♻️ Duplicate comments (1)
mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp (1)

761-783: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Still missing a fail-fast path for unsupported compound-gate body statements.

bodyFn handles gate calls, barriers, and resets, then falls through. Any other parsed statement kind is silently ignored, which changes the gate semantics without a diagnostic instead of failing translation.

♻️ Proposed fix
         } else if (const auto reset =
                        std::dynamic_pointer_cast<qasm3::ResetStatement>(
                            bodyStmt)) {
           for (auto q : resolveGateOperandInScope(reset->gate, localScope,
                                                   reset->debugInfo)) {
             builder.reset(q);
           }
+        } else {
+          throw qasm3::CompilerError(
+              "Unsupported statement in compound gate body.",
+              bodyStmt ? bodyStmt->debugInfo : debugInfo);
         }
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp` around lines 761 -
783, The loop over gate.body (iterating bodyStmt) currently handles
GateCallStatement, BarrierStatement, and ResetStatement and silently ignores any
other statement kinds; update the loop to add an explicit fail-fast branch for
unsupported/unknown statement types: when bodyStmt is not a GateCallStatement,
BarrierStatement, or ResetStatement, emit a clear translation error or abort
translation (e.g., use the existing translation error reporting mechanism) that
includes the gate name and the unexpected statement kind; reference the existing
symbols bodyStmt, gate.body, applyGateCallStatement, resolveGateOperandInScope,
builder.barrier, and builder.reset when locating where to insert this diagnostic
so unhandled compound-gate body statements no longer get dropped silently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp`:
- Around line 734-742: The loop that builds targetsMap misuses an iterator after
targets.push_back, causing UB: when it == targets.end() you must not call
std::distance on the invalidated iterator. Fix the inner loop in
TranslateQASM3ToQC.cpp that iterates over gateOperands/gate.targetNames by
computing the index safely—either recompute the iterator/index after push_back
(e.g., find again) or use the new index directly (targets.size() - 1) when you
just pushed the target—and then push that index into
targetsMap[gate.targetNames[i]] instead of using the old it-based distance.
- Around line 816-828: emitBlockStatements currently only accepts
GateCallStatement and throws for any other quantum statements (breaking if/else
branches that contain reset, barrier, etc.); update this function to dispatch on
the actual statement subtype instead of only GateCallStatement: detect
ResetStatement, BarrierStatement (and any other quantum statement types the
translator supports) via dynamic_pointer_cast and call the corresponding
lowering helpers (e.g. applyResetStatement, applyBarrierStatement) or refactor
to a single applyStatement dispatcher that handles all supported
qasm3::Statement subclasses; keep using the same debugInfo and qubitRegisters
context when invoking these helpers so branch bodies lower identically to
top-level code.
- Around line 303-349: Move handling of compile-time-only declarations before
performing runtime allocations: after computing sizedTy and size, check
stmt->isConst first and if true, handle only the allowed initializer case (same
measure initializer logic using visitMeasureAssignment with lhsId) then return
without calling builder.allocQubitRegister or builder.allocClassicalBitRegister;
otherwise continue into the existing switch on sizedTy->type (which uses
allocQubitRegister, allocClassicalBitRegister, qubitRegisters,
classicalRegisters) and keep the remaining initializer error handling intact.

In `@mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp`:
- Around line 101-352: Add a test case exercising the negative-control modifier
`negctrl @` so the `emitGate` negative-control bracketing path is covered:
create a QASM fixture (e.g. `singleNegControlledX` and optionally a mixed
`ctrl`+`negctrl` fixture) and add corresponding entries to the test
instantiation using the existing test harness `QASM3TranslationTestCase` with
the builder reference (e.g. add QASM3TranslationTestCase{"SingleNegControlledX",
qasm::singleNegControlledX, MQT_NAMED_BUILDER(qc::singleNegControlledX)} and/or
a mixed case name) so the translation for `negctrl` is validated by the unit
suite.
- Around line 343-352: The five test entries
(QASM3TranslationTestCase{"CtrlTwo", "CtrlTwoMixed", "SimpleIf", "IfTwoQubits",
"IfElse"}) use fully-qualified mlir::qc:: but the rest of the file uses the
unqualified qc:: thanks to using namespace mlir;. Replace mlir::qc::ctrlTwo,
mlir::qc::ctrlTwoMixed, mlir::qc::simpleIf, mlir::qc::ifTwoQubits, and
mlir::qc::ifElse with qc::ctrlTwo, qc::ctrlTwoMixed, qc::simpleIf,
qc::ifTwoQubits, and qc::ifElse respectively so the QASM3TranslationTestCase
entries and the MQT_NAMED_BUILDER calls are consistent with the surrounding
code.

In `@mlir/unittests/programs/CMakeLists.txt`:
- Around line 9-11: MLIRQASMPrograms is missing the project compile/warning
options because it doesn't use MQT::ProjectOptions; call
mqt_mlir_target_use_project_options(MLIRQASMPrograms) (which applies
MQT::ProjectOptions privately to the target) or otherwise link
MQT::ProjectOptions PRIVATE to MLIRQASMPrograms similar to
MLIRQuantumComputationPrograms so the options propagate at build time; apply the
same change for the other program targets MLIRQCPrograms, MLIRQCOPrograms, and
MLIRQIRPrograms for consistency.

---

Duplicate comments:
In `@mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp`:
- Around line 761-783: The loop over gate.body (iterating bodyStmt) currently
handles GateCallStatement, BarrierStatement, and ResetStatement and silently
ignores any other statement kinds; update the loop to add an explicit fail-fast
branch for unsupported/unknown statement types: when bodyStmt is not a
GateCallStatement, BarrierStatement, or ResetStatement, emit a clear translation
error or abort translation (e.g., use the existing translation error reporting
mechanism) that includes the gate name and the unexpected statement kind;
reference the existing symbols bodyStmt, gate.body, applyGateCallStatement,
resolveGateOperandInScope, builder.barrier, and builder.reset when locating
where to insert this diagnostic so unhandled compound-gate body statements no
longer get dropped silently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 143f39d4-1c86-4b0c-adbf-4013a109ac54

📥 Commits

Reviewing files that changed from the base of the PR and between bebb1f7 and 9a04b30.

📒 Files selected for processing (10)
  • mlir/include/mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h
  • mlir/lib/Dialect/QC/Translation/CMakeLists.txt
  • mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp
  • mlir/tools/mqt-cc/mqt-cc.cpp
  • mlir/unittests/Dialect/QC/Translation/CMakeLists.txt
  • mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp
  • mlir/unittests/programs/CMakeLists.txt
  • mlir/unittests/programs/qasm_programs.cpp
  • mlir/unittests/programs/qasm_programs.h
  • test/circuits/bell.qasm
💤 Files with no reviewable changes (1)
  • test/circuits/bell.qasm

Comment on lines +303 to +349
const auto ty = std::get<1>(stmt->type);
const auto sizedTy =
std::dynamic_pointer_cast<qasm3::DesignatedType<uint64_t>>(ty);
if (!sizedTy) {
throw qasm3::CompilerError("Only sized types are supported.",
stmt->debugInfo);
}
const auto size = static_cast<int64_t>(sizedTy->getDesignator());

switch (sizedTy->type) {
case qasm3::Qubit: {
auto reg = builder.allocQubitRegister(size);
SmallVector<Value> qubits;
qubits.reserve(static_cast<size_t>(size));
for (int64_t i = 0; i < size; ++i) {
qubits.push_back(reg[static_cast<size_t>(i)]);
}
qubitRegisters[id] = std::move(qubits);
break;
}
case qasm3::Bit:
case qasm3::Int:
case qasm3::Uint: {
classicalRegisters[id] = builder.allocClassicalBitRegister(size, id);
break;
}
default:
throw qasm3::CompilerError("Unsupported declaration type.",
stmt->debugInfo);
}

// Handle initializer (measure only)
if (stmt->expression) {
const auto& expr = stmt->expression->expression;
if (const auto measureExpr =
std::dynamic_pointer_cast<qasm3::MeasureExpression>(expr)) {
auto lhsId = std::make_shared<qasm3::IndexedIdentifier>(id);
visitMeasureAssignment(lhsId, measureExpr, stmt->debugInfo);
return;
}
if (stmt->isConst) {
return; // nothing to emit
}
throw qasm3::CompilerError(
"Only measure statements are supported as initializers.",
stmt->debugInfo);
}
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle const declarations before the runtime-allocation switch.

The current order forces every declaration through runtime storage lowering first. That means const bit/int/uint declarations still emit QC classical registers, and const float/angle declarations are rejected by the default arm even though they only need the const-eval/type-check environments. Compile-time-only declarations should bypass allocQubitRegister/allocClassicalBitRegister entirely.

♻️ Proposed fix
     const auto size = static_cast<int64_t>(sizedTy->getDesignator());
 
+    if (stmt->isConst) {
+      return;
+    }
+
     switch (sizedTy->type) {
     case qasm3::Qubit: {
       auto reg = builder.allocQubitRegister(size);
@@
-      if (stmt->isConst) {
-        return; // nothing to emit
-      }
       throw qasm3::CompilerError(
           "Only measure statements are supported as initializers.",
           stmt->debugInfo);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp` around lines 303 -
349, Move handling of compile-time-only declarations before performing runtime
allocations: after computing sizedTy and size, check stmt->isConst first and if
true, handle only the allowed initializer case (same measure initializer logic
using visitMeasureAssignment with lhsId) then return without calling
builder.allocQubitRegister or builder.allocClassicalBitRegister; otherwise
continue into the existing switch on sizedTy->type (which uses
allocQubitRegister, allocClassicalBitRegister, qubitRegisters,
classicalRegisters) and keep the remaining initializer error handling intact.

Comment on lines +734 to +742
for (size_t i = 0; i < gate.targetNames.size(); ++i) {
for (auto target : gateOperands[i]) {
auto* it = llvm::find(targets, target);
if (it == targets.end()) {
targets.push_back(target);
}
targetsMap[gate.targetNames[i]].push_back(
std::distance(targets.begin(), it));
}
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.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Don't reuse end() after push_back when building the compound-gate target map.

When a target is first seen, it == targets.end(). targets.push_back(target) invalidates that iterator, so std::distance(targets.begin(), it) is UB. This can miswire the local-scope qubit mapping for compound-gate expansion.

🛠️ Proposed fix
     for (size_t i = 0; i < gate.targetNames.size(); ++i) {
       for (auto target : gateOperands[i]) {
-        auto* it = llvm::find(targets, target);
-        if (it == targets.end()) {
+        auto* it = llvm::find(targets, target);
+        size_t index = 0;
+        if (it == targets.end()) {
+          index = targets.size();
           targets.push_back(target);
+        } else {
+          index = static_cast<size_t>(std::distance(targets.begin(), it));
         }
-        targetsMap[gate.targetNames[i]].push_back(
-            std::distance(targets.begin(), it));
+        targetsMap[gate.targetNames[i]].push_back(index);
       }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp` around lines 734 -
742, The loop that builds targetsMap misuses an iterator after
targets.push_back, causing UB: when it == targets.end() you must not call
std::distance on the invalidated iterator. Fix the inner loop in
TranslateQASM3ToQC.cpp that iterates over gateOperands/gate.targetNames by
computing the index safely—either recompute the iterator/index after push_back
(e.g., find again) or use the new index directly (targets.size() - 1) when you
just pushed the target—and then push that index into
targetsMap[gate.targetNames[i]] instead of using the old it-based distance.

Comment on lines +816 to +828
void emitBlockStatements(
const std::vector<std::shared_ptr<qasm3::Statement>>& statements,
const std::shared_ptr<qasm3::DebugInfo>& debugInfo) {
for (const auto& statement : statements) {
auto gateCall =
std::dynamic_pointer_cast<qasm3::GateCallStatement>(statement);
if (gateCall == nullptr) {
throw qasm3::CompilerError(
"Only quantum statements are supported in if/else blocks.",
debugInfo);
}
applyGateCallStatement(gateCall, qubitRegisters);
}
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

if branches are narrower than the rest of the translator.

This helper only accepts GateCallStatement, so branch bodies containing supported quantum statements like reset and barrier still fail here. That breaks the PR’s “if/else over quantum statements” contract even though those statements already lower correctly at top level.

♻️ Proposed fix
   void emitBlockStatements(
       const std::vector<std::shared_ptr<qasm3::Statement>>& statements,
       const std::shared_ptr<qasm3::DebugInfo>& debugInfo) {
     for (const auto& statement : statements) {
-      auto gateCall =
-          std::dynamic_pointer_cast<qasm3::GateCallStatement>(statement);
-      if (gateCall == nullptr) {
+      if (const auto gateCall =
+              std::dynamic_pointer_cast<qasm3::GateCallStatement>(statement)) {
+        applyGateCallStatement(gateCall, qubitRegisters);
+      } else if (const auto barrier =
+                     std::dynamic_pointer_cast<qasm3::BarrierStatement>(
+                         statement)) {
+        SmallVector<Value> qubits;
+        for (const auto& gate : barrier->gates) {
+          auto resolved = resolveGateOperand(gate, barrier->debugInfo);
+          qubits.append(resolved.begin(), resolved.end());
+        }
+        builder.barrier(qubits);
+      } else if (const auto reset =
+                     std::dynamic_pointer_cast<qasm3::ResetStatement>(
+                         statement)) {
+        for (auto q : resolveGateOperand(reset->gate, reset->debugInfo)) {
+          builder.reset(q);
+        }
+      } else {
         throw qasm3::CompilerError(
             "Only quantum statements are supported in if/else blocks.",
-            debugInfo);
+            statement ? statement->debugInfo : debugInfo);
       }
-      applyGateCallStatement(gateCall, qubitRegisters);
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QC/Translation/TranslateQASM3ToQC.cpp` around lines 816 -
828, emitBlockStatements currently only accepts GateCallStatement and throws for
any other quantum statements (breaking if/else branches that contain reset,
barrier, etc.); update this function to dispatch on the actual statement subtype
instead of only GateCallStatement: detect ResetStatement, BarrierStatement (and
any other quantum statement types the translator supports) via
dynamic_pointer_cast and call the corresponding lowering helpers (e.g.
applyResetStatement, applyBarrierStatement) or refactor to a single
applyStatement dispatcher that handles all supported qasm3::Statement
subclasses; keep using the same debugInfo and qubitRegisters context when
invoking these helpers so branch bodies lower identically to top-level code.

Comment on lines +101 to +352
INSTANTIATE_TEST_SUITE_P(
QASM3TranslationProgramsTest, QASM3TranslationTest,
testing::Values(

QASM3TranslationTestCase{"AllocQubit", qasm::allocQubit,
MQT_NAMED_BUILDER(qc::allocQubit)},
QASM3TranslationTestCase{"AllocQubitRegister", qasm::allocQubitRegister,
MQT_NAMED_BUILDER(qc::allocQubitRegister)},
QASM3TranslationTestCase{
"AllocMultipleQubitRegisters", qasm::allocMultipleQubitRegisters,
MQT_NAMED_BUILDER(qc::allocMultipleQubitRegisters)},
QASM3TranslationTestCase{"AllocLargeRegister", qasm::allocLargeRegister,
MQT_NAMED_BUILDER(qc::allocLargeRegister)},
QASM3TranslationTestCase{
"SingleMeasurementToSingleBit", qasm::singleMeasurementToSingleBit,
MQT_NAMED_BUILDER(qc::singleMeasurementToSingleBit)},
QASM3TranslationTestCase{
"RepeatedMeasurementToSameBit", qasm::repeatedMeasurementToSameBit,
MQT_NAMED_BUILDER(qc::repeatedMeasurementToSameBit)},
QASM3TranslationTestCase{
"RepeatedMeasurementToDifferentBits",
qasm::repeatedMeasurementToDifferentBits,
MQT_NAMED_BUILDER(qc::repeatedMeasurementToDifferentBits)},
QASM3TranslationTestCase{
"MultipleClassicalRegistersAndMeasurements",
qasm::multipleClassicalRegistersAndMeasurements,
MQT_NAMED_BUILDER(qc::multipleClassicalRegistersAndMeasurements)},
QASM3TranslationTestCase{
"ResetQubitAfterSingleOp", qasm::resetQubitAfterSingleOp,
MQT_NAMED_BUILDER(qc::resetQubitAfterSingleOp)},
QASM3TranslationTestCase{
"ResetMultipleQubitsAfterSingleOp",
qasm::resetMultipleQubitsAfterSingleOp,
MQT_NAMED_BUILDER(qc::resetMultipleQubitsAfterSingleOp)},
QASM3TranslationTestCase{
"RepeatedResetAfterSingleOp", qasm::repeatedResetAfterSingleOp,
MQT_NAMED_BUILDER(qc::repeatedResetAfterSingleOp)},
QASM3TranslationTestCase{"GlobalPhase", qasm::globalPhase,
MQT_NAMED_BUILDER(qc::globalPhase)},
QASM3TranslationTestCase{"Identity", qasm::identity,
MQT_NAMED_BUILDER(qc::identity)},
QASM3TranslationTestCase{
"SingleControlledIdentity", qasm::singleControlledIdentity,
MQT_NAMED_BUILDER(qc::singleControlledIdentity)},
QASM3TranslationTestCase{
"MultipleControlledIdentity", qasm::multipleControlledIdentity,
MQT_NAMED_BUILDER(qc::multipleControlledIdentity)},
QASM3TranslationTestCase{"X", qasm::x, MQT_NAMED_BUILDER(qc::x)},
QASM3TranslationTestCase{"SingleControlledX", qasm::singleControlledX,
MQT_NAMED_BUILDER(qc::singleControlledX)},
QASM3TranslationTestCase{"MultipleControlledX",
qasm::multipleControlledX,
MQT_NAMED_BUILDER(qc::multipleControlledX)},
QASM3TranslationTestCase{"Y", qasm::y, MQT_NAMED_BUILDER(qc::y)},
QASM3TranslationTestCase{"SingleControlledY", qasm::singleControlledY,
MQT_NAMED_BUILDER(qc::singleControlledY)},
QASM3TranslationTestCase{"MultipleControlledY",
qasm::multipleControlledY,
MQT_NAMED_BUILDER(qc::multipleControlledY)},
QASM3TranslationTestCase{"Z", qasm::z, MQT_NAMED_BUILDER(qc::z)},
QASM3TranslationTestCase{"SingleControlledZ", qasm::singleControlledZ,
MQT_NAMED_BUILDER(qc::singleControlledZ)},
QASM3TranslationTestCase{"MultipleControlledZ",
qasm::multipleControlledZ,
MQT_NAMED_BUILDER(qc::multipleControlledZ)},
QASM3TranslationTestCase{"H", qasm::h, MQT_NAMED_BUILDER(qc::h)},
QASM3TranslationTestCase{"SingleControlledH", qasm::singleControlledH,
MQT_NAMED_BUILDER(qc::singleControlledH)},
QASM3TranslationTestCase{"MultipleControlledH",
qasm::multipleControlledH,
MQT_NAMED_BUILDER(qc::multipleControlledH)},
QASM3TranslationTestCase{"S", qasm::s, MQT_NAMED_BUILDER(qc::s)},
QASM3TranslationTestCase{"SingleControlledS", qasm::singleControlledS,
MQT_NAMED_BUILDER(qc::singleControlledS)},
QASM3TranslationTestCase{"MultipleControlledS",
qasm::multipleControlledS,
MQT_NAMED_BUILDER(qc::multipleControlledS)},
QASM3TranslationTestCase{"Sdg", qasm::sdg, MQT_NAMED_BUILDER(qc::sdg)},
QASM3TranslationTestCase{"SingleControlledSdg",
qasm::singleControlledSdg,
MQT_NAMED_BUILDER(qc::singleControlledSdg)},
QASM3TranslationTestCase{"MultipleControlledSdg",
qasm::multipleControlledSdg,
MQT_NAMED_BUILDER(qc::multipleControlledSdg)},
QASM3TranslationTestCase{"T", qasm::t_, MQT_NAMED_BUILDER(qc::t_)},
QASM3TranslationTestCase{"SingleControlledT", qasm::singleControlledT,
MQT_NAMED_BUILDER(qc::singleControlledT)},
QASM3TranslationTestCase{"MultipleControlledT",
qasm::multipleControlledT,
MQT_NAMED_BUILDER(qc::multipleControlledT)},
QASM3TranslationTestCase{"Tdg", qasm::tdg, MQT_NAMED_BUILDER(qc::tdg)},
QASM3TranslationTestCase{"SingleControlledTdg",
qasm::singleControlledTdg,
MQT_NAMED_BUILDER(qc::singleControlledTdg)},
QASM3TranslationTestCase{"MultipleControlledTdg",
qasm::multipleControlledTdg,
MQT_NAMED_BUILDER(qc::multipleControlledTdg)},
QASM3TranslationTestCase{"SX", qasm::sx, MQT_NAMED_BUILDER(qc::sx)},
QASM3TranslationTestCase{"SingleControlledSX", qasm::singleControlledSx,
MQT_NAMED_BUILDER(qc::singleControlledSx)},
QASM3TranslationTestCase{"MultipleControlledSX",
qasm::multipleControlledSx,
MQT_NAMED_BUILDER(qc::multipleControlledSx)},
QASM3TranslationTestCase{"SXdg", qasm::sxdg,
MQT_NAMED_BUILDER(qc::sxdg)},
QASM3TranslationTestCase{"SingleControlledSXdg",
qasm::singleControlledSxdg,
MQT_NAMED_BUILDER(qc::singleControlledSxdg)},
QASM3TranslationTestCase{"MultipleControlledSXdg",
qasm::multipleControlledSxdg,
MQT_NAMED_BUILDER(qc::multipleControlledSxdg)},
QASM3TranslationTestCase{"RX", qasm::rx, MQT_NAMED_BUILDER(qc::rx)},
QASM3TranslationTestCase{"SingleControlledRX", qasm::singleControlledRx,
MQT_NAMED_BUILDER(qc::singleControlledRx)},
QASM3TranslationTestCase{"MultipleControlledRX",
qasm::multipleControlledRx,
MQT_NAMED_BUILDER(qc::multipleControlledRx)},
QASM3TranslationTestCase{"RY", qasm::ry, MQT_NAMED_BUILDER(qc::ry)},
QASM3TranslationTestCase{"SingleControlledRY", qasm::singleControlledRy,
MQT_NAMED_BUILDER(qc::singleControlledRy)},
QASM3TranslationTestCase{"MultipleControlledRY",
qasm::multipleControlledRy,
MQT_NAMED_BUILDER(qc::multipleControlledRy)},
QASM3TranslationTestCase{"RZ", qasm::rz, MQT_NAMED_BUILDER(qc::rz)},
QASM3TranslationTestCase{"SingleControlledRZ", qasm::singleControlledRz,
MQT_NAMED_BUILDER(qc::singleControlledRz)},
QASM3TranslationTestCase{"MultipleControlledRZ",
qasm::multipleControlledRz,
MQT_NAMED_BUILDER(qc::multipleControlledRz)},
QASM3TranslationTestCase{"P", qasm::p, MQT_NAMED_BUILDER(qc::p)},
QASM3TranslationTestCase{"SingleControlledP", qasm::singleControlledP,
MQT_NAMED_BUILDER(qc::singleControlledP)},
QASM3TranslationTestCase{"MultipleControlledP",
qasm::multipleControlledP,
MQT_NAMED_BUILDER(qc::multipleControlledP)},
QASM3TranslationTestCase{"R", qasm::r, MQT_NAMED_BUILDER(qc::r)},
QASM3TranslationTestCase{"SingleControlledR", qasm::singleControlledR,
MQT_NAMED_BUILDER(qc::singleControlledR)},
QASM3TranslationTestCase{"MultipleControlledR",
qasm::multipleControlledR,
MQT_NAMED_BUILDER(qc::multipleControlledR)},
QASM3TranslationTestCase{"U2", qasm::u2, MQT_NAMED_BUILDER(qc::u2)},
QASM3TranslationTestCase{"SingleControlledU2", qasm::singleControlledU2,
MQT_NAMED_BUILDER(qc::singleControlledU2)},
QASM3TranslationTestCase{"MultipleControlledU2",
qasm::multipleControlledU2,
MQT_NAMED_BUILDER(qc::multipleControlledU2)},
QASM3TranslationTestCase{"U", qasm::u, MQT_NAMED_BUILDER(qc::u)},
QASM3TranslationTestCase{"SingleControlledU", qasm::singleControlledU,
MQT_NAMED_BUILDER(qc::singleControlledU)},
QASM3TranslationTestCase{"MultipleControlledU",
qasm::multipleControlledU,
MQT_NAMED_BUILDER(qc::multipleControlledU)},
QASM3TranslationTestCase{"SWAP", qasm::swap,
MQT_NAMED_BUILDER(qc::swap)},
QASM3TranslationTestCase{"SingleControlledSWAP",
qasm::singleControlledSwap,
MQT_NAMED_BUILDER(qc::singleControlledSwap)},
QASM3TranslationTestCase{"MultipleControlledSWAP",
qasm::multipleControlledSwap,
MQT_NAMED_BUILDER(qc::multipleControlledSwap)},
QASM3TranslationTestCase{"iSWAP", qasm::iswap,
MQT_NAMED_BUILDER(qc::iswap)},
QASM3TranslationTestCase{"SingleControllediSWAP",
qasm::singleControlledIswap,
MQT_NAMED_BUILDER(qc::singleControlledIswap)},
QASM3TranslationTestCase{
"MultipleControllediSWAP", qasm::multipleControlledIswap,
MQT_NAMED_BUILDER(qc::multipleControlledIswap)},
QASM3TranslationTestCase{"InverseISWAP", qasm::inverseIswap,
MQT_NAMED_BUILDER(qc::inverseIswap)},
QASM3TranslationTestCase{
"InverseMultiControlledISWAP", qasm::inverseMultipleControlledIswap,
MQT_NAMED_BUILDER(qc::inverseMultipleControlledIswap)},
QASM3TranslationTestCase{"DCX", qasm::dcx, MQT_NAMED_BUILDER(qc::dcx)},
QASM3TranslationTestCase{"SingleControlledDCX",
qasm::singleControlledDcx,
MQT_NAMED_BUILDER(qc::singleControlledDcx)},
QASM3TranslationTestCase{"MultipleControlledDCX",
qasm::multipleControlledDcx,
MQT_NAMED_BUILDER(qc::multipleControlledDcx)},
QASM3TranslationTestCase{"ECR", qasm::ecr, MQT_NAMED_BUILDER(qc::ecr)},
QASM3TranslationTestCase{"SingleControlledECR",
qasm::singleControlledEcr,
MQT_NAMED_BUILDER(qc::singleControlledEcr)},
QASM3TranslationTestCase{"MultipleControlledECR",
qasm::multipleControlledEcr,
MQT_NAMED_BUILDER(qc::multipleControlledEcr)},
QASM3TranslationTestCase{"RXX", qasm::rxx, MQT_NAMED_BUILDER(qc::rxx)},
QASM3TranslationTestCase{"SingleControlledRXX",
qasm::singleControlledRxx,
MQT_NAMED_BUILDER(qc::singleControlledRxx)},
QASM3TranslationTestCase{"MultipleControlledRXX",
qasm::multipleControlledRxx,
MQT_NAMED_BUILDER(qc::multipleControlledRxx)},
QASM3TranslationTestCase{"TripleControlledRXX",
qasm::tripleControlledRxx,
MQT_NAMED_BUILDER(qc::tripleControlledRxx)},
QASM3TranslationTestCase{"RYY", qasm::ryy, MQT_NAMED_BUILDER(qc::ryy)},
QASM3TranslationTestCase{"SingleControlledRYY",
qasm::singleControlledRyy,
MQT_NAMED_BUILDER(qc::singleControlledRyy)},
QASM3TranslationTestCase{"MultipleControlledRYY",
qasm::multipleControlledRyy,
MQT_NAMED_BUILDER(qc::multipleControlledRyy)},
QASM3TranslationTestCase{"RZX", qasm::rzx, MQT_NAMED_BUILDER(qc::rzx)},
QASM3TranslationTestCase{"SingleControlledRZX",
qasm::singleControlledRzx,
MQT_NAMED_BUILDER(qc::singleControlledRzx)},
QASM3TranslationTestCase{"MultipleControlledRZX",
qasm::multipleControlledRzx,
MQT_NAMED_BUILDER(qc::multipleControlledRzx)},
QASM3TranslationTestCase{"RZZ", qasm::rzz, MQT_NAMED_BUILDER(qc::rzz)},
QASM3TranslationTestCase{"SingleControlledRZZ",
qasm::singleControlledRzz,
MQT_NAMED_BUILDER(qc::singleControlledRzz)},
QASM3TranslationTestCase{"MultipleControlledRZZ",
qasm::multipleControlledRzz,
MQT_NAMED_BUILDER(qc::multipleControlledRzz)},
QASM3TranslationTestCase{"XXPlusYY", qasm::xxPlusYY,
MQT_NAMED_BUILDER(qc::xxPlusYY)},
QASM3TranslationTestCase{
"SingleControlledXXPlusYY", qasm::singleControlledXxPlusYY,
MQT_NAMED_BUILDER(qc::singleControlledXxPlusYY)},
QASM3TranslationTestCase{
"MultipleControlledXXPlusYY", qasm::multipleControlledXxPlusYY,
MQT_NAMED_BUILDER(qc::multipleControlledXxPlusYY)},
QASM3TranslationTestCase{"XXMinusYY", qasm::xxMinusYY,
MQT_NAMED_BUILDER(qc::xxMinusYY)},
QASM3TranslationTestCase{
"SingleControlledXXMinusYY", qasm::singleControlledXxMinusYY,
MQT_NAMED_BUILDER(qc::singleControlledXxMinusYY)},
QASM3TranslationTestCase{
"MultipleControlledXXMinusYY", qasm::multipleControlledXxMinusYY,
MQT_NAMED_BUILDER(qc::multipleControlledXxMinusYY)},
QASM3TranslationTestCase{"Barrier", qasm::barrier,
MQT_NAMED_BUILDER(qc::barrier)},
QASM3TranslationTestCase{"BarrierTwoQubits", qasm::barrierTwoQubits,
MQT_NAMED_BUILDER(qc::barrierTwoQubits)},
QASM3TranslationTestCase{"BarrierMultipleQubits",
qasm::barrierMultipleQubits,
MQT_NAMED_BUILDER(qc::barrierMultipleQubits)},
QASM3TranslationTestCase{"CtrlTwo", qasm::ctrlTwo,
MQT_NAMED_BUILDER(mlir::qc::ctrlTwo)},
QASM3TranslationTestCase{"CtrlTwoMixed", qasm::ctrlTwoMixed,
MQT_NAMED_BUILDER(mlir::qc::ctrlTwoMixed)},
QASM3TranslationTestCase{"SimpleIf", qasm::simpleIf,
MQT_NAMED_BUILDER(mlir::qc::simpleIf)},
QASM3TranslationTestCase{"IfTwoQubits", qasm::ifTwoQubits,
MQT_NAMED_BUILDER(mlir::qc::ifTwoQubits)},
QASM3TranslationTestCase{"IfElse", qasm::ifElse,
MQT_NAMED_BUILDER(mlir::qc::ifElse)}));
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.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Missing coverage for negctrl @. The PR lists negctrl @ among supported modifiers, but no fixture/case exercises it (only ctrl, ctrl(n), and inv). Consider adding a negative-control case (e.g. singleNegControlledX / mixed ctrl+negctrl) so the negative-control bracketing path in emitGate is validated.

I can draft the QASM fixture plus the matching QCProgramBuilder reference if helpful. If this is intentionally deferred to #1733, feel free to dismiss.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp` around
lines 101 - 352, Add a test case exercising the negative-control modifier
`negctrl @` so the `emitGate` negative-control bracketing path is covered:
create a QASM fixture (e.g. `singleNegControlledX` and optionally a mixed
`ctrl`+`negctrl` fixture) and add corresponding entries to the test
instantiation using the existing test harness `QASM3TranslationTestCase` with
the builder reference (e.g. add QASM3TranslationTestCase{"SingleNegControlledX",
qasm::singleNegControlledX, MQT_NAMED_BUILDER(qc::singleNegControlledX)} and/or
a mixed case name) so the translation for `negctrl` is validated by the unit
suite.

Comment on lines +343 to +352
QASM3TranslationTestCase{"CtrlTwo", qasm::ctrlTwo,
MQT_NAMED_BUILDER(mlir::qc::ctrlTwo)},
QASM3TranslationTestCase{"CtrlTwoMixed", qasm::ctrlTwoMixed,
MQT_NAMED_BUILDER(mlir::qc::ctrlTwoMixed)},
QASM3TranslationTestCase{"SimpleIf", qasm::simpleIf,
MQT_NAMED_BUILDER(mlir::qc::simpleIf)},
QASM3TranslationTestCase{"IfTwoQubits", qasm::ifTwoQubits,
MQT_NAMED_BUILDER(mlir::qc::ifTwoQubits)},
QASM3TranslationTestCase{"IfElse", qasm::ifElse,
MQT_NAMED_BUILDER(mlir::qc::ifElse)}));
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.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Inconsistent namespace qualification. These five cases use mlir::qc:: while every other case (lines 105-342) uses qc::. Since using namespace mlir; is in effect, qc:: resolves identically — align these for consistency.

♻️ Proposed normalization
-                                 MQT_NAMED_BUILDER(mlir::qc::ctrlTwo)},
+                                 MQT_NAMED_BUILDER(qc::ctrlTwo)},
...
-                                 MQT_NAMED_BUILDER(mlir::qc::ifElse)}));
+                                 MQT_NAMED_BUILDER(qc::ifElse)}));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
QASM3TranslationTestCase{"CtrlTwo", qasm::ctrlTwo,
MQT_NAMED_BUILDER(mlir::qc::ctrlTwo)},
QASM3TranslationTestCase{"CtrlTwoMixed", qasm::ctrlTwoMixed,
MQT_NAMED_BUILDER(mlir::qc::ctrlTwoMixed)},
QASM3TranslationTestCase{"SimpleIf", qasm::simpleIf,
MQT_NAMED_BUILDER(mlir::qc::simpleIf)},
QASM3TranslationTestCase{"IfTwoQubits", qasm::ifTwoQubits,
MQT_NAMED_BUILDER(mlir::qc::ifTwoQubits)},
QASM3TranslationTestCase{"IfElse", qasm::ifElse,
MQT_NAMED_BUILDER(mlir::qc::ifElse)}));
QASM3TranslationTestCase{"CtrlTwo", qasm::ctrlTwo,
MQT_NAMED_BUILDER(qc::ctrlTwo)},
QASM3TranslationTestCase{"CtrlTwoMixed", qasm::ctrlTwoMixed,
MQT_NAMED_BUILDER(qc::ctrlTwoMixed)},
QASM3TranslationTestCase{"SimpleIf", qasm::simpleIf,
MQT_NAMED_BUILDER(qc::simpleIf)},
QASM3TranslationTestCase{"IfTwoQubits", qasm::ifTwoQubits,
MQT_NAMED_BUILDER(qc::ifTwoQubits)},
QASM3TranslationTestCase{"IfElse", qasm::ifElse,
MQT_NAMED_BUILDER(qc::ifElse)}));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp` around
lines 343 - 352, The five test entries (QASM3TranslationTestCase{"CtrlTwo",
"CtrlTwoMixed", "SimpleIf", "IfTwoQubits", "IfElse"}) use fully-qualified
mlir::qc:: but the rest of the file uses the unqualified qc:: thanks to using
namespace mlir;. Replace mlir::qc::ctrlTwo, mlir::qc::ctrlTwoMixed,
mlir::qc::simpleIf, mlir::qc::ifTwoQubits, and mlir::qc::ifElse with
qc::ctrlTwo, qc::ctrlTwoMixed, qc::simpleIf, qc::ifTwoQubits, and qc::ifElse
respectively so the QASM3TranslationTestCase entries and the MQT_NAMED_BUILDER
calls are consistent with the surrounding code.

Comment on lines +9 to +11
add_library(MLIRQASMPrograms qasm_programs.cpp)
target_sources(MLIRQASMPrograms PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES
qasm_programs.h)
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how ProjectOptions is defined/propagated and how sibling Programs libs pull it in.
fd -t f 'CMakeLists.txt' | xargs rg -n 'MQT::ProjectOptions|add_library\(MLIRProjectOptions|ProjectOptions' -C2
# Check whether the QC builder lib links ProjectOptions PUBLIC (transitive to MLIRQCPrograms)
ast-grep --pattern 'target_link_libraries($$$ProjectOptions$$$)'

Repository: munich-quantum-toolkit/core

Length of output: 2712


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the full unittests programs CMakeLists around the referenced lines
nl -ba mlir/unittests/programs/CMakeLists.txt | sed -n '1,120p'

# Find where MLIRQASMPrograms is defined/linked
rg -n "MLIRQASMPrograms|mqt_mlir_apply_target_options|mqt_mlir_target_use_project_options|target_link_libraries\\(MLIR" mlir/unittests -S

# Show definitions of the helper in case unittests use it indirectly
nl -ba mlir/CMakeLists.txt | sed -n '1,120p'

Repository: munich-quantum-toolkit/core

Length of output: 114


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Print the unittests programs CMakeLists with line numbers (avoid `nl`)
cat -n mlir/unittests/programs/CMakeLists.txt | sed -n '1,160p'

# 2) Locate how MLIRQASMPrograms is defined/linked and whether helper functions are used
rg -n "MLIRQASMPrograms|mqt_mlir_apply_target_options|mqt_mlir_target_use_project_options|target_link_libraries\\(" mlir/unittests/programs -S

# 3) Show the relevant helper functions in mlir/CMakeLists.txt
cat -n mlir/CMakeLists.txt | sed -n '1,120p'

Repository: munich-quantum-toolkit/core

Length of output: 4099


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate where the builder targets are defined and check their link dependencies
rg -n "add_library\\(MLIRQCProgramBuilder\\b|add_library\\(MLIRQCOProgramBuilder\\b|add_library\\(MLIRIRProgramBuilder\\b|add_library\\(MLIR.*ProgramBuilder\\b" mlir -S

# Show the relevant CMakeLists sections around those target definitions
# (Using rg to capture nearby target_link_libraries/MQT::ProjectOptions)
for t in MLIRQCProgramBuilder MLIRQCOProgramBuilder MLIRIRProgramBuilder; do
  echo "---- $t ----"
  rg -n "target_link_libraries\\([^\\)]*\\b$t\\b|\\b$t\\b\\)" mlir -S --glob 'CMakeLists.txt'
  rg -n "target_link_libraries\\(.*MQT::ProjectOptions|MQT::ProjectOptions" mlir -S --glob 'CMakeLists.txt' | head -n 200
done

Repository: munich-quantum-toolkit/core

Length of output: 53


🏁 Script executed:

#!/bin/bash
set -uo pipefail

# 1) Find the exact CMake file locations that define the program builder targets
rg -n --glob 'CMakeLists.txt' "add_library\\(\\s*(MLIRQCProgramBuilder|MLIRQCOProgramBuilder|MLIRIRProgramBuilder)\\b" mlir -S

# 2) For each known builder target name, print surrounding lines that include its definition + link libraries / project options usage
for t in MLIRQCProgramBuilder MLIRQCOProgramBuilder MLIRIRProgramBuilder; do
  echo "==== Around add_library($t) ===="
  rg -n --glob 'CMakeLists.txt' "add_library\\(\\s*${t}\\b" mlir -S -C 5 || true

  echo "==== Search target_link_libraries(...$t...) / $t ... MQT::ProjectOptions ===="
  rg -n --glob 'CMakeLists.txt' "target_link_libraries\\([^\\)]*\\b${t}\\b|${t}[^\\n]*MQT::ProjectOptions|mqt_mlir_target_use_project_options\\(\\s*${t}\\s*\\)" mlir -S || true

  echo
done

# 3) Also check for helper calls on obj.<target> or generic project option apply for these builder names
rg -n --glob 'CMakeLists.txt' "mqt_mlir_(apply_target_options|target_use_project_options)\\(\\s*(MLIRQCProgramBuilder|MLIRQCOProgramBuilder|MLIRIRProgramBuilder)\\s*\\)" mlir -S || true

Repository: munich-quantum-toolkit/core

Length of output: 1370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant builder CMakeLists with line numbers around the helper call
echo "=== QC Builder CMakeLists ==="
cat -n mlir/lib/Dialect/QC/Builder/CMakeLists.txt | sed -n '1,120p'

echo "=== QCO Builder CMakeLists ==="
cat -n mlir/lib/Dialect/QCO/Builder/CMakeLists.txt | sed -n '1,140p'

# Locate IR builder definition/usage
echo "=== Find MLIRIRProgramBuilder occurrences ==="
rg -n "MLIRIRProgramBuilder" mlir -S || true

# If IR builder is defined in a CMakeLists, print that file around the definition + link statements
# (try common pattern: add_library(<name> ... ) )
rg -n --glob 'CMakeLists.txt' "add_library\\(\\s*MLIRIRProgramBuilder\\b" mlir -S || true

Repository: munich-quantum-toolkit/core

Length of output: 2156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "MLIRIRProgramBuilder" -S . || true

rg -n "IRProgramBuilder\\.cpp|QIRProgramBuilder\\.cpp|qir_programs|qir.*ProgramBuilder" -S mlir || true

# Also look for add_mlir_library / add_library definitions for IR program builder candidates
rg -n --glob 'CMakeLists.txt' "add_mlir_library\\(\\s*([^\\s\\)]+ProgramBuilder)\\b" mlir -S || true

Repository: munich-quantum-toolkit/core

Length of output: 1140


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== QIR Builder CMakeLists ==="
cat -n mlir/lib/Dialect/QIR/Builder/CMakeLists.txt | sed -n '1,120p' || true

echo
echo "=== Occurrences of MLIRQASMPrograms in CMake ==="
rg -n "MLIRQASMPrograms" -S . --glob 'CMakeLists.txt' || true

echo
echo "=== Any helper applying project options to MLIRQASMPrograms ==="
rg -n "mqt_mlir_(apply_target_options|target_use_project_options)\\(\\s*MLIRQASMPrograms\\s*\\)|MQT::ProjectOptions.*MLIRQASMPrograms|MLIRQASMPrograms.*MQT::ProjectOptions" -S . --glob 'CMakeLists.txt' || true

echo
echo "=== Occurrences of MLIRQIRProgramBuilder project options usage ==="
rg -n "MLIRQIRProgramBuilder" -S mlir/lib --glob 'CMakeLists.txt' || true

Repository: munich-quantum-toolkit/core

Length of output: 1915


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for applying project options directly to the unittests program targets
rg -n --glob 'CMakeLists.txt' "mqt_mlir_target_use_project_options\\(\\s*(MLIRQASMPrograms|MLIRQCPrograms|MLIRQCOPrograms|MLIRQIRPrograms|MLIRQuantumComputationPrograms)\\s*\\)" mlir -S || true

# Search for apply_target_options usage on these
rg -n --glob 'CMakeLists.txt' "mqt_mlir_apply_target_options\\(\\s*(MLIRQASMPrograms|MLIRQCPrograms|MLIRQCOPrograms|MLIRQIRPrograms)\\s*\\)" mlir -S || true

# Search for any direct target_link_libraries(... MQT::ProjectOptions ...) on those unittests program targets
rg -n --glob 'CMakeLists.txt' "target_link_libraries\\([^\\)]*(MLIRQASMPrograms|MLIRQCPrograms|MLIRQCOPrograms|MLIRQIRPrograms)[^\\)]*MQT::ProjectOptions" mlir -S || true

Repository: munich-quantum-toolkit/core

Length of output: 53


MLIRQASMPrograms doesn’t get MQT::ProjectOptions (and isn’t consistent with other MLIR*Programs) — Only MLIRQuantumComputationPrograms links MQT::ProjectOptions; the *ProgramBuilder targets apply MQT::ProjectOptions with mqt_mlir_target_use_project_options(...), which links it PRIVATE to the builder target, so those compile/warning options don’t propagate to MLIRQASMPrograms.

🔧 Proposed addition
 add_library(MLIRQASMPrograms qasm_programs.cpp)
+target_link_libraries(MLIRQASMPrograms PRIVATE MQT::ProjectOptions)
 target_sources(MLIRQASMPrograms PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES
                                        qasm_programs.h)

Also consider doing the same for MLIRQCPrograms, MLIRQCOPrograms, and MLIRQIRPrograms for consistency.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
add_library(MLIRQASMPrograms qasm_programs.cpp)
target_sources(MLIRQASMPrograms PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES
qasm_programs.h)
add_library(MLIRQASMPrograms qasm_programs.cpp)
target_link_libraries(MLIRQASMPrograms PRIVATE MQT::ProjectOptions)
target_sources(MLIRQASMPrograms PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES
qasm_programs.h)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/unittests/programs/CMakeLists.txt` around lines 9 - 11, MLIRQASMPrograms
is missing the project compile/warning options because it doesn't use
MQT::ProjectOptions; call mqt_mlir_target_use_project_options(MLIRQASMPrograms)
(which applies MQT::ProjectOptions privately to the target) or otherwise link
MQT::ProjectOptions PRIVATE to MLIRQASMPrograms similar to
MLIRQuantumComputationPrograms so the options propagate at build time; apply the
same change for the other program targets MLIRQCPrograms, MLIRQCOPrograms, and
MLIRQIRPrograms for consistency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Anything related to C++ code feature New feature or request MLIR Anything related to MLIR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants