[Mlir-commits] [mlir] [mlir][transform] Reject non-transform ops in transform.foreach body (PR #236566)

Kevin Jay Chang llvmlistbot at llvm.org
Wed Oct 28 14:01:20 PDT 2026


https://github.com/kevinjaychang created https://github.com/llvm/llvm-project/pull/236566

ForeachOp::getEffects cast every body op to TransformOpInterface, so a non-transform op in the body (e.g. arith.constant) hit an assertion during verification. Use dyn_cast in getEffects, and reject such ops in ForeachOp::verify with the same diagnostic transform.sequence emits.

Fixes #234637

>From 46d9f86643c69c7a5f63714d85beb816dbebdbb0 Mon Sep 17 00:00:00 2001
From: Kevin <kevinjaychang2003 at gmail.com>
Date: Wed, 28 Oct 2026 13:42:32 -0700
Subject: [PATCH] [mlir][transform] Reject non-transform ops in
 transform.foreach body

ForeachOp::getEffects cast every body op to TransformOpInterface, so a
non-transform op in the body (e.g. arith.constant) hit an assertion
during verification. Use dyn_cast in getEffects, and reject such ops in
ForeachOp::verify with the same diagnostic transform.sequence emits.

Fixes #234637
---
 .../lib/Dialect/Transform/IR/TransformOps.cpp | 20 +++++++++++++++----
 mlir/test/Dialect/Transform/ops-invalid.mlir  | 17 +++++++++++++++-
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/mlir/lib/Dialect/Transform/IR/TransformOps.cpp b/mlir/lib/Dialect/Transform/IR/TransformOps.cpp
index e40f3b7885c273..ccc086c6e93a10 100644
--- a/mlir/lib/Dialect/Transform/IR/TransformOps.cpp
+++ b/mlir/lib/Dialect/Transform/IR/TransformOps.cpp
@@ -1677,8 +1677,8 @@ void transform::ForeachOp::getEffects(
        llvm::zip(getTargetsMutable(), getBody().front().getArguments())) {
     BlockArgument blockArgument = blockArg;
     if (any_of(getBody().front().without_terminator(), [&](Operation &op) {
-          return isHandleConsumed(blockArgument,
-                                  cast<TransformOpInterface>(&op));
+          auto transformOp = dyn_cast<TransformOpInterface>(&op);
+          return transformOp && isHandleConsumed(blockArgument, transformOp);
         })) {
       consumesHandle(target, effects);
     } else {
@@ -1687,11 +1687,13 @@ void transform::ForeachOp::getEffects(
   }
 
   if (any_of(getBody().front().without_terminator(), [&](Operation &op) {
-        return doesModifyPayload(cast<TransformOpInterface>(&op));
+        auto transformOp = dyn_cast<TransformOpInterface>(&op);
+        return transformOp && doesModifyPayload(transformOp);
       })) {
     modifiesPayload(effects);
   } else if (any_of(getBody().front().without_terminator(), [&](Operation &op) {
-               return doesReadPayload(cast<TransformOpInterface>(&op));
+               auto transformOp = dyn_cast<TransformOpInterface>(&op);
+               return transformOp && doesReadPayload(transformOp);
              })) {
     onlyReadsPayload(effects);
   }
@@ -1754,6 +1756,16 @@ LogicalResult transform::ForeachOp::verify() {
                          "operands to have the same op/value/param type");
   }
 
+  for (Operation &child : getBody().front().without_terminator()) {
+    if (!isa<TransformOpInterface>(child)) {
+      InFlightDiagnostic diag =
+          emitOpError()
+          << "expected children ops to implement TransformOpInterface";
+      diag.attachNote(child.getLoc()) << "op without interface";
+      return diag;
+    }
+  }
+
   return success();
 }
 
diff --git a/mlir/test/Dialect/Transform/ops-invalid.mlir b/mlir/test/Dialect/Transform/ops-invalid.mlir
index 585736d30269e7..4f9eba16cd32e8 100644
--- a/mlir/test/Dialect/Transform/ops-invalid.mlir
+++ b/mlir/test/Dialect/Transform/ops-invalid.mlir
@@ -339,6 +339,21 @@ transform.sequence failures(propagate) {
 
 // -----
 
+module attributes {transform.with_named_sequence} {
+  transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
+    // expected-error @below {{expected children ops to implement TransformOpInterface}}
+    transform.foreach %arg0 : !transform.any_op {
+    ^bb0(%arg1: !transform.any_op):
+      // expected-note @below {{op without interface}}
+      %c0 = arith.constant 0 : i32
+      transform.yield
+    }
+    transform.yield
+  }
+}
+
+// -----
+
 transform.sequence failures(suppress) {
 ^bb0(%arg0: !transform.any_op):
   // expected-error @below {{TransformOpInterface requires memory effects on operands to be specified}}
@@ -496,7 +511,7 @@ module {
 module attributes { transform.with_named_sequence} {
   transform.sequence failures(suppress) {
   ^bb0(%arg0: !transform.any_op):
-    // expected-error @below {{op symbol's parent must have the SymbolTable trai}}
+    // expected-error @below {{op symbol's parent must have the SymbolTable trait}}
     transform.named_sequence @nested() {
       transform.yield
     }



More information about the Mlir-commits mailing list