Skip to content

✨ Add OpenQASM-to-QC translation#1671

Closed
J4MMlE wants to merge 25 commits into
munich-quantum-toolkit:more-controlfrom
J4MMlE:open-qasm
Closed

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

Conversation

@J4MMlE

@J4MMlE J4MMlE commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

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

codecov Bot commented Apr 29, 2026

Copy link
Copy Markdown

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 Outdated
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

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

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.

@coderabbitai coderabbitai Bot 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.

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

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor
✅ 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.

@coderabbitai coderabbitai Bot 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.

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

denialhaag commented May 22, 2026

Copy link
Copy Markdown
Member

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. 😌

@mergify mergify Bot removed the conflict label Jun 9, 2026
@mergify mergify Bot added the conflict label Jun 9, 2026
@denialhaag denialhaag deleted the branch munich-quantum-toolkit:more-control June 10, 2026 23:39
@denialhaag denialhaag closed this Jun 10, 2026
@mergify mergify Bot removed the conflict label Jun 10, 2026
@denialhaag

denialhaag commented Jun 11, 2026

Copy link
Copy Markdown
Member

This PR was automatically closed when #1751 was merged. Sorry about that. I'll open up a new PR.

@denialhaag

Copy link
Copy Markdown
Member

The new PR is #1780.

denialhaag added a commit that referenced this pull request Jun 12, 2026
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