Change promote-resources-to-args pass to emit all the unexpected users for
VarHandleOp.

Modify an existing test to test the output.

PiperOrigin-RevId: 302350400
Change-Id: I0cb30a477c13629b20172b77e1e6340392f8fd5b
diff --git a/tensorflow/compiler/mlir/tensorflow/tests/promote_resources_to_args.mlir b/tensorflow/compiler/mlir/tensorflow/tests/promote_resources_to_args.mlir
index dcf2127..db15aeb 100644
--- a/tensorflow/compiler/mlir/tensorflow/tests/promote_resources_to_args.mlir
+++ b/tensorflow/compiler/mlir/tensorflow/tests/promote_resources_to_args.mlir
@@ -297,8 +297,9 @@
 // Tests VarHandleOp has users that are not removed.
 
 func @main() -> tensor<i1> {
-  // expected-error@+1 {{expects no uses}}
+  // expected-error@+1 {{expects no uses but used by operations: tf.UnknownOp, tf.VarIsInitializedOp}}
   %0 = "tf.VarHandleOp"() {container = "", shape = "tfshape$", shared_name = "x"} : () -> tensor<!tf.resource<tensor<f32>>>
   %1 = "tf.VarIsInitializedOp"(%0) : (tensor<!tf.resource<tensor<f32>>>) -> tensor<i1>
+  %2 = "tf.UnknownOp"(%0) : (tensor<!tf.resource<tensor<f32>>>) -> tensor<i1>
   return %1 : tensor<i1>
 }
diff --git a/tensorflow/compiler/mlir/tensorflow/transforms/promote_resources_to_args.cc b/tensorflow/compiler/mlir/tensorflow/transforms/promote_resources_to_args.cc
index e69ac6a..a0a5217 100644
--- a/tensorflow/compiler/mlir/tensorflow/transforms/promote_resources_to_args.cc
+++ b/tensorflow/compiler/mlir/tensorflow/transforms/promote_resources_to_args.cc
@@ -36,6 +36,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/PointerUnion.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Casting.h"
 #include "mlir/Dialect/StandardOps/IR/Ops.h"  // TF:llvm-project
@@ -210,8 +211,21 @@
   for (Operation& op : llvm::make_early_inc_range(function.front())) {
     auto var_handle_op = llvm::dyn_cast<TF::VarHandleOp>(op);
     if (!var_handle_op) continue;
-    if (!var_handle_op.use_empty())
-      return var_handle_op.emitOpError() << "expects no uses";
+    if (!var_handle_op.use_empty()) {
+      // SmallSet will use a vector when there is only one element and use
+      // std::set when there are more than one elements. This ensures that
+      // the operations in the error message are ordered.
+      llvm::SmallSet<std::string, 2> unique_operations;
+      llvm::for_each(
+          var_handle_op.getOperation()->getUsers(), [&](Operation* user) {
+            unique_operations.insert(user->getName().getStringRef().str());
+          });
+
+      return var_handle_op.emitOpError(
+                 "expects no uses but used by operations: ")
+             << llvm::join(unique_operations.begin(), unique_operations.end(),
+                           ", ");
+    }
 
     op.erase();
   }