[llvm-branch-commits] [mlir] [mlir] Static assert on a single-result fold on an op without `OneResult` (PR #228746)

Victor Perez via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sat Oct 3 10:02:02 PDT 2026


https://github.com/victor-eds created https://github.com/llvm/llvm-project/pull/228746

`getFoldHookFn` selects the single-result `OpFoldResult fold(...)` form only if the op has the OneResult trait. On an op with a single result, but without this trait, `getFoldHookFn` ignores the fold method without a diagnostic. At the moment, only a hand-written declaration can cause the problem via a `fold` in `extraClassDeclaration`, or an op class written in C++.

Add a `static_assert` that rejects a single-result fold on an op without the OneResult trait.

No new test is added, as this would lead to a compile error. This is intended to prevent operations like this one, which are allowed, but can lead to unexpected behaviour, as canonicalize will never fold the op:

```console
$ mlir-opt two_results_single_fold.mlir -canonicalize
module {
  func.func @two_results_single_fold(%arg0: i32) -> (i32, i32) {
    %0:2 = "test.op_two_results_single_fold"(%arg0) : (i32) -> (i32, i32)
    return %0#0, %0#1 : i32, i32
  }
}
```

With this patch, such an op would not compile:

```console
mlir/include/mlir/IR/OpDefinition.h:1905:19: error: static assertion failed due to requirement 'hasOneResult || !hasSingleResultFold': a single-result `OpFoldResult fold(...)` requires the OneResult trait; ops with zero, several, or variadic results must use `LogicalResult fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &)`
```

Downstream impact:
- An op without the OneResult trait that defines a single-result `fold` no longer compiles. It must use the multi-result form  `LogicalResult fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &)`.

>From 6c1b819bd009d339eea30af95057db8cc081cfae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?V=C3=ADctor=20P=C3=A9rez=20Carrasco?=
 <victor.pc.upm at gmail.com>
Date: Fri, 2 Oct 2026 14:24:44 -0700
Subject: [PATCH] [mlir] Diagnose a single-result fold on an op without
 OneResult
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

`getFoldHookFn` selects the single-result `OpFoldResult fold(...)` form
only if the op has the OneResult trait. On an op without this trait,
the dispatch ignores such a fold without a diagnostic. ODS declares this
form only for an op with exactly one fixed result. So only a
hand-written declaration can cause the problem: a `fold` in
`extraClassDeclaration`, or an op class written in C++. No in-tree op
does this.

Add a `static_assert` that rejects a single-result fold on an op
without the OneResult trait.

No lit test can check a compile error. A scratch test op shows the bug:
it has two results and a single-result fold that prints a line and
returns its operand. Without this patch, the op compiles, and
canonicalize never calls the fold:

  $ mlir-opt two_results_single_fold.mlir -canonicalize
  module {
    func.func @two_results_single_fold(%arg0: i32) -> (i32, i32) {
      %0:2 = "test.op_two_results_single_fold"(%arg0) : (i32) -> (i32, i32)
      return %0#0, %0#1 : i32, i32
    }
  }

With this patch, the test op does not compile:

  mlir/include/mlir/IR/OpDefinition.h:1905:19: error: static assertion
  failed due to requirement 'hasOneResult || !hasSingleResultFold': a
  single-result `OpFoldResult fold(...)` requires the OneResult trait;
  ops with zero, several, or variadic results must use `LogicalResult
  fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &)`

Downstream impact:
- An op without the OneResult trait that defines a single-result `fold`
  no longer compiles. It must use the multi-result form
  `LogicalResult fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &)`.

Signed-off-by: Víctor Pérez Carrasco <victor.pc.upm at gmail.com>
---
 mlir/include/mlir/IR/OpDefinition.h | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/mlir/include/mlir/IR/OpDefinition.h b/mlir/include/mlir/IR/OpDefinition.h
index cb35ba7c8eaa2..783cd1359e970 100644
--- a/mlir/include/mlir/IR/OpDefinition.h
+++ b/mlir/include/mlir/IR/OpDefinition.h
@@ -1897,11 +1897,18 @@ class Op : public OpState, public Traits<ConcreteType>... {
 
   /// Return the internal implementations of each of the OperationName hooks.
   static constexpr FoldHookFn getFoldHookFn() {
+    constexpr bool hasOneResult = hasTrait<OpTrait::OneResult>();
+    constexpr bool hasSingleResultFold =
+        has_single_result_fold_v<ConcreteType> ||
+        has_fold_adaptor_single_result_v<ConcreteType>;
+    // Without this check, the dispatch below silently skips such a `fold`.
+    static_assert(hasOneResult || !hasSingleResultFold,
+                  "a single-result `OpFoldResult fold(...)` requires the "
+                  "OneResult trait; ops with zero, several, or variadic "
+                  "results must use `LogicalResult fold(FoldAdaptor, "
+                  "SmallVectorImpl<OpFoldResult> &)`");
     // If the operation is single result and defines a `fold` method.
-    if constexpr (llvm::is_one_of<OpTrait::OneResult<ConcreteType>,
-                                  Traits<ConcreteType>...>::value &&
-                  (has_single_result_fold_v<ConcreteType> ||
-                   has_fold_adaptor_single_result_v<ConcreteType>))
+    if constexpr (hasOneResult && hasSingleResultFold)
       return [](Operation *op, ArrayRef<Attribute> operands,
                 SmallVectorImpl<OpFoldResult> &results) {
         return foldSingleResultHook<ConcreteType>(op, operands, results);



More information about the llvm-branch-commits mailing list