Remove symbolic tensor support from TensorHandle

This code is no longer being used by swift, so it seems best to
remove it to simplify the uses of TensorHandle.

PiperOrigin-RevId: 281406036
Change-Id: Ib98a84cb767fe76eee6f41d7e86c02e2eee7e98f
diff --git a/tensorflow/c/c_api_experimental.cc b/tensorflow/c/c_api_experimental.cc
index 8cbf899..8fe5a20 100644
--- a/tensorflow/c/c_api_experimental.cc
+++ b/tensorflow/c/c_api_experimental.cc
@@ -520,12 +520,6 @@
 
 void TFE_TensorHandlePrintDebugString(TFE_TensorHandle* handle) {
   auto* status = TF_NewStatus();
-  if (!TFE_TensorHandleIsConcrete(handle)) {
-    VLOG(1) << "Symbolic tensor: " << handle;
-    TF_DeleteStatus(status);
-    return;
-  }
-
   TF_Tensor* t = TFE_TensorHandleResolve(handle, status);
   CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
 
@@ -813,204 +807,6 @@
   status->status = EnableCollectiveOps(server_def, ctx);
 }
 
-std::string tensorflow::getTF_OutputDebugString(TF_Output node) {
-  return absl::Substitute("TF_Output($0, $1)", node.oper, node.index);
-}
-
-using tensorflow::getTF_OutputDebugString;
-
-TFE_TensorHandle* TFE_NewTensorHandleFromTFOutput(TF_Output t,
-                                                  TF_DataType data_type) {
-  auto ret = new TFE_TensorHandle(t, data_type);
-  VLOG(1) << "Storing TFOutput " << getTF_OutputDebugString(t)
-          << " into tensor handle " << ret << " with internal handle "
-          << ret->handle;
-  return ret;
-}
-
-unsigned char TFE_TensorHandleIsConcrete(TFE_TensorHandle* handle) {
-  assert(handle->handle != nullptr);
-  return handle->handle->getSymbolicTensor() == nullptr;
-}
-
-TF_Output TFE_GetTFOutputFromTensorHandle(TFE_TensorHandle* handle,
-                                          TF_Status* status) {
-  if (TFE_TensorHandleIsConcrete(handle)) {
-    status->status =
-        tensorflow::errors::Internal("Not a symbolic tensor: ", handle);
-    return TF_Output{nullptr, -1};
-  }
-
-  auto* sym_tensor = handle->handle->getSymbolicTensor();
-  CHECK(sym_tensor != nullptr);
-  auto ret = TF_Output{sym_tensor->oper, sym_tensor->index};
-  VLOG(1) << "Retrieving " << getTF_OutputDebugString(ret)
-          << " from tensor handle " << handle;
-  CHECK_GE(sym_tensor->index, 0);
-  return ret;
-}
-
-TFE_TraceContext* TFE_NewTraceContext(TF_Graph* graph) {
-  return new TFE_TraceContext(graph);
-}
-
-void TFE_DeleteTraceContext(TFE_TraceContext* trace_ctx) { delete trace_ctx; }
-
-// If `handle` is already symbolic, return it. Otherwise map it to a new
-// symbolic tensor (a PlaceHolder op) and return that.
-static TF_Output getOrCreateSymbolicTensor(TFE_TraceContext* trace_ctx,
-                                           tensorflow::TensorHandle* handle,
-                                           TF_Status* status) {
-  VLOG(1) << "Getting symbolic tensor for input tensor handle " << handle
-          << ": " << handle->DebugString();
-
-  auto* sym_tensor = handle->getSymbolicTensor();
-  if (sym_tensor != nullptr) {
-    auto ret = TF_Output{sym_tensor->oper, sym_tensor->index};
-    VLOG(1) << "This handle is a symbolic tensor " << sym_tensor << ": "
-            << getTF_OutputDebugString(ret);
-    return ret;
-  }
-
-  auto find_it = trace_ctx->input_tensor_map.find(handle);
-  if (find_it != trace_ctx->input_tensor_map.end()) {
-    VLOG(1) << "There exists a map entry from this concrete tensor to: "
-            << getTF_OutputDebugString(find_it->second);
-    return find_it->second;
-  }
-
-  auto node_name = tensorflow::strings::StrCat("additional_input_",
-                                               trace_ctx->node_counter++);
-  VLOG(1) << "Adding a place holder node named " << node_name;
-  auto* desc =
-      TF_NewOperation(trace_ctx->graph, "Placeholder", node_name.c_str());
-  TF_SetAttrType(desc, "dtype",
-                 static_cast<TF_DataType>(handle->dtype) /*TF_FLOAT*/);
-  auto* result = TF_FinishOperation(desc, status);
-  if (!status->status.ok()) {
-    return TF_Output{nullptr, -1};
-  }
-
-  auto ret = TF_Output{result, 0};
-  VLOG(1) << "Creating a new map entry to map to: "
-          << getTF_OutputDebugString(ret);
-  trace_ctx->input_tensor_map[handle] = ret;
-  // `handle` could be destroyed before it's read from `input_tensor_map` (say
-  // during a subsequent TFE_FinalizeInputTensorsFromTraceContext() call), so we
-  // increment its ref count to extend its life span to that of `trace_ctx`.
-  handle->Ref();
-  VLOG(1) << "Ref count for handle " << handle
-          << " is 1?: " << handle->RefCountIsOne();
-  return ret;
-}
-
-TF_Operation* TFE_AddEagerOpToGraph(TFE_Op* op, TFE_TraceContext* trace_ctx,
-                                    TFE_TensorHandle** retvals,
-                                    int* num_retvals, TF_Status* status) {
-  VLOG(1) << "Calling TFE_AddEagerOpToGraph() with op " << op << ": "
-          << op->operation.DebugString();
-
-  const auto& op_type = op->operation.Name();
-  auto op_name =
-      tensorflow::strings::StrCat(op_type, "_", trace_ctx->node_counter++);
-  std::unique_ptr<TF_OperationDescription> desc(
-      TF_NewOperation(trace_ctx->graph, op_type.c_str(), op_name.c_str()));
-
-  VLOG(1) << "Adding attrs.";
-  tensorflow::AttrValueMap attrs;
-  op->operation.Attrs().FillAttrValueMap(&attrs);
-  for (const auto& attr : attrs) {
-    desc->node_builder.Attr(attr.first, attr.second);
-  }
-
-  VLOG(1) << "Adding inputs.";
-  const auto& inputs = op->operation.Inputs();
-  size_t inputIndex = 0;
-  const tensorflow::OpDef& op_def = desc->node_builder.op_def();
-  for (const tensorflow::OpDef::ArgDef& input_arg : op_def.input_arg()) {
-    if (input_arg.type_list_attr().empty() && input_arg.number_attr().empty()) {
-      auto symbolic_input =
-          getOrCreateSymbolicTensor(trace_ctx, inputs[inputIndex++], status);
-      if (!status->status.ok()) return nullptr;
-      TF_AddInput(desc.get(), symbolic_input);
-      continue;
-    }
-    size_t list_size = 0;
-    if (!input_arg.type_list_attr().empty()) {
-      const std::string& type_list_attr = input_arg.type_list_attr();
-      const auto& attr_value = attrs[type_list_attr];
-      CHECK(attr_value.value_case() == tensorflow::AttrValue::kList)
-          << "Type list attribute should be a list!";
-      list_size = attr_value.list().type_size();
-    } else {
-      CHECK(!input_arg.number_attr().empty());
-      const auto& attr_value = attrs[input_arg.number_attr()];
-      CHECK(attr_value.value_case() == tensorflow::AttrValue::kI)
-          << "Number attribute should be int!";
-      if (attr_value.i() < 0) {
-        status->status = tensorflow::errors::Internal(
-            "Number attribute for length should be >=0!");
-        return nullptr;
-      }
-      list_size = attr_value.i();
-    }
-    std::vector<TF_Output> list_inputs(list_size);
-    for (TF_Output& list_input : list_inputs) {
-      list_input =
-          getOrCreateSymbolicTensor(trace_ctx, inputs[inputIndex++], status);
-      if (!status->status.ok()) return nullptr;
-    }
-    TF_AddInputList(desc.get(), list_inputs.data(), list_inputs.size());
-  }
-
-  auto* graph_op = TF_FinishOperation(desc.release(), status);
-  if (!status->status.ok()) return nullptr;
-
-  VLOG(1) << "Op finalized; setting return tensors.";
-  *num_retvals = TF_OperationNumOutputs(graph_op);
-  VLOG(1) << "This op has " << *num_retvals << " outputs.";
-  for (int i = 0; i < *num_retvals; ++i) {
-    auto output = TF_Output{graph_op, i};
-    auto dtype = TF_OperationOutputType(output);
-    retvals[i] = TFE_NewTensorHandleFromTFOutput(output, dtype);
-  }
-  return graph_op;
-}
-
-int TFE_FinalizeInputTensorsFromTraceContext(TFE_TraceContext* trace_ctx) {
-  if (trace_ctx->input_tensors == nullptr) {
-    trace_ctx->input_tensors =
-        new std::vector<std::pair<tensorflow::TensorHandle*, TF_Output>>();
-    trace_ctx->input_tensors->reserve(trace_ctx->input_tensor_map.size());
-
-    for (auto input : trace_ctx->input_tensor_map) {
-      trace_ctx->input_tensors->emplace_back(input.first, input.second);
-    }
-  }
-  return trace_ctx->input_tensor_map.size();
-}
-
-TF_Output TFE_GetInputGraphNodeFromTraceContext(TFE_TraceContext* trace_ctx,
-                                                unsigned int idx) {
-  CHECK(trace_ctx->input_tensors != nullptr);
-  CHECK(trace_ctx->input_tensors->size() > idx);
-  return trace_ctx->input_tensors->at(idx).second;
-}
-
-TFE_TensorHandle* TFE_ConsumeInputConcreteTensorFromTraceContext(
-    TFE_TraceContext* trace_ctx, unsigned int idx) {
-  CHECK(trace_ctx->input_tensors != nullptr);
-  CHECK(trace_ctx->input_tensors->size() > idx);
-  auto* handle = trace_ctx->input_tensors->at(idx).first;
-  VLOG(1) << "Ref count for internal handle " << handle
-          << " is 1?: " << handle->RefCountIsOne();
-  handle->Ref();
-  auto* ret = new TFE_TensorHandle(handle);
-  VLOG(1) << "Returning a new tensor handle " << ret << ": "
-          << handle->DebugString();
-  return ret;
-}
-
 TF_ShapeAndTypeList* TF_NewShapeAndTypeList(int num_items) {
   TF_ShapeAndTypeList* result = new TF_ShapeAndTypeList;
   result->num_items = num_items;
diff --git a/tensorflow/c/c_api_experimental.h b/tensorflow/c/c_api_experimental.h
index 46ce86d..5fc260d 100644
--- a/tensorflow/c/c_api_experimental.h
+++ b/tensorflow/c/c_api_experimental.h
@@ -297,53 +297,6 @@
                                                    size_t proto_len,
                                                    TF_Status* status);
 
-// Create a symbolic tensor from the input graph node.
-TF_CAPI_EXPORT extern TFE_TensorHandle* TFE_NewTensorHandleFromTFOutput(
-    TF_Output t, TF_DataType data_type);
-
-// Returns 0 if the input tensor handle represents a symbolic tensor (i.e., a
-// graph node). Otherwise returns non-0.
-TF_CAPI_EXPORT extern unsigned char TFE_TensorHandleIsConcrete(
-    TFE_TensorHandle* handle);
-
-// If `handle` is a symbolic tensor, return the corresponding graph node
-// represented by TF_Output. Otherwise, return an error status.
-TF_CAPI_EXPORT extern TF_Output TFE_GetTFOutputFromTensorHandle(
-    TFE_TensorHandle* handle, TF_Status* status);
-
-typedef struct TFE_TraceContext TFE_TraceContext;
-
-// A trace context contains a trace graph, to which TFE_AddEagerOpToGraph()
-// calls add graph nodes as a way to symbolically execute the eager ops.
-//
-// It also contains a hash map from concrete input tensors to symbolic
-// tensors. That map will be used to create input tensors to the trace graph.
-TF_CAPI_EXPORT extern TFE_TraceContext* TFE_NewTraceContext(TF_Graph* graph);
-
-TF_CAPI_EXPORT extern void TFE_DeleteTraceContext(TFE_TraceContext* trace_ctx);
-
-// Symbolically executes `op`, by adding a corresponding node to the graph
-// associated with `trace_ctx`. This graph node outputs a set of symbolic
-// tensors in `retvals` and `num_retvals`. Returns the corresponding graph
-// operation on success, otherwise returns nullptr.
-TF_CAPI_EXPORT extern TF_Operation* TFE_AddEagerOpToGraph(
-    TFE_Op* op, TFE_TraceContext* trace_ctx, TFE_TensorHandle** retvals,
-    int* num_retvals, TF_Status* status);
-
-// Finalizes the trace graph and its inputs, and returns the number of inputs.
-// After this call, the next two APIs can be called to iterate over the input
-// tensors.
-TF_CAPI_EXPORT extern int TFE_FinalizeInputTensorsFromTraceContext(
-    TFE_TraceContext* trace_ctx);
-
-TF_CAPI_EXPORT extern TF_Output TFE_GetInputGraphNodeFromTraceContext(
-    TFE_TraceContext* trace_ctx, unsigned int idx);
-
-// Each input tensor should be consumed at most once.
-TF_CAPI_EXPORT extern TFE_TensorHandle*
-TFE_ConsumeInputConcreteTensorFromTraceContext(TFE_TraceContext* trace_ctx,
-                                               unsigned int idx);
-
 // Information about the shape of a Tensor and its type.
 struct TF_ShapeAndType {
   // Number of dimensions. -1 indicates unknown rank.
diff --git a/tensorflow/c/c_api_experimental_test.cc b/tensorflow/c/c_api_experimental_test.cc
index ed0ab7c..fa09f99 100644
--- a/tensorflow/c/c_api_experimental_test.cc
+++ b/tensorflow/c/c_api_experimental_test.cc
@@ -205,234 +205,6 @@
   TF_DeleteStatus(status);
 }
 
-TEST(CAPI_EXPERIMENTAL, SymbolicTensor) {
-  TF_Status* status = TF_NewStatus();
-  auto node = TF_Output{nullptr, 1};
-  auto* sym_handle = TFE_NewTensorHandleFromTFOutput(node, TF_FLOAT);
-  TFE_TensorHandlePrintDebugString(sym_handle);
-  CHECK_EQ(TFE_TensorHandleDataType(sym_handle), TF_FLOAT);
-  ASSERT_FALSE(TFE_TensorHandleIsConcrete(sym_handle));
-  auto same_node = TFE_GetTFOutputFromTensorHandle(sym_handle, status);
-  CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
-  ASSERT_EQ(same_node.oper, node.oper);
-  ASSERT_EQ(same_node.index, node.index);
-  TFE_DeleteTensorHandle(sym_handle);
-
-  TFE_TensorHandle* m = TestMatrixTensorHandle();
-  ASSERT_TRUE(TFE_TensorHandleIsConcrete(m));
-  (void)TFE_GetTFOutputFromTensorHandle(m, status);
-  CHECK_EQ(TF_INTERNAL, TF_GetCode(status)) << TF_Message(status);
-  TFE_DeleteTensorHandle(m);
-
-  TF_DeleteStatus(status);
-}
-
-class AddEagerOpToGraphTest : public ::testing::Test {
- protected:
-  AddEagerOpToGraphTest()
-      : status_(TF_NewStatus()),
-        eager_ctx_(nullptr),
-        graph_(TF_NewGraph()),
-        trace_ctx_(TFE_NewTraceContext(graph_)) {
-    TFE_ContextOptions* opts = TFE_NewContextOptions();
-    CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-    eager_ctx_ = TFE_NewContext(opts, status_);
-    CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-    TFE_DeleteContextOptions(opts);
-  }
-
-  ~AddEagerOpToGraphTest() override {
-    TFE_DeleteTraceContext(trace_ctx_);
-    TF_DeleteGraph(graph_);
-    TFE_DeleteContext(eager_ctx_);
-    TF_DeleteStatus(status_);
-  }
-
-  template <typename Callable>
-  void AddEagerOpToGraphAndCheck(TFE_Op* op, Callable checker) {
-    TFE_TensorHandle* retvals[5];
-    int num_retvals = 5;
-    // Symbolically execute this op, which adds a graph node to `trace_ctx_`.
-    TF_Operation* graph_op =
-        TFE_AddEagerOpToGraph(op, trace_ctx_, retvals, &num_retvals, status_);
-    CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-    CHECK_NOTNULL(graph_op);
-    // Check the expectations.
-    checker(graph_op);
-    for (int i = 0; i < num_retvals; ++i) {
-      TFE_DeleteTensorHandle(retvals[i]);
-    }
-  }
-
-  TF_Status* status_;
-  TFE_Context* eager_ctx_;
-  TF_Graph* graph_;
-  TFE_TraceContext* trace_ctx_;
-};
-
-TEST_F(AddEagerOpToGraphTest, DebugPrintAndSymbolicExecution) {
-  TFE_TensorHandle* m = TestMatrixTensorHandle();
-  TFE_Op* op = MatMulOp(eager_ctx_, m, m);
-
-  CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-  TFE_OpPrintDebugString(op);
-
-  TFE_TensorHandle* retvals[5];
-  int num_retvals = 5;
-  // Symbolically execute this op, which adds a graph node to `trace_ctx`.
-  TFE_AddEagerOpToGraph(op, trace_ctx_, retvals, &num_retvals, status_);
-  CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-
-  int num_inputs = TFE_FinalizeInputTensorsFromTraceContext(trace_ctx_);
-  CHECK_EQ(num_inputs, 1);
-  auto input_sym_tensor = TFE_GetInputGraphNodeFromTraceContext(trace_ctx_,
-                                                                /*idx*/ 0);
-
-  LOG(INFO) << tensorflow::getTF_OutputDebugString(input_sym_tensor);
-  auto handle = TFE_ConsumeInputConcreteTensorFromTraceContext(trace_ctx_,
-                                                               /*idx*/ 0);
-  TFE_TensorHandlePrintDebugString(handle);
-  TFE_DeleteTensorHandle(handle);
-
-  CHECK_EQ(num_retvals, 1);
-  CHECK_EQ(TFE_TensorHandleDataType(retvals[0]), TF_FLOAT);
-
-  TFE_DeleteTensorHandle(retvals[0]);
-  TFE_DeleteTensorHandle(m);
-  TFE_DeleteOp(op);
-}
-
-TEST_F(AddEagerOpToGraphTest, ValueAttributesArePreserved) {
-  // Create MinOp
-  TFE_TensorHandle* axis = TestAxisTensorHandle();
-  TFE_Op* op = MinOp(eager_ctx_, axis, axis);
-  CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-
-  // Check the attributes set by the call to MinOp above.
-  AddEagerOpToGraphAndCheck(op, [this, &axis](TF_Operation* graph_op) {
-    unsigned char value;
-    TF_OperationGetAttrBool(graph_op, "keep_dims", &value, status_);
-    CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-    CHECK_EQ(value, 1);
-    TF_DataType dtype;
-    TF_OperationGetAttrType(graph_op, "Tidx", &dtype, status_);
-    CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-    CHECK_EQ(dtype, TF_INT32);
-    TF_OperationGetAttrType(graph_op, "T", &dtype, status_);
-    CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-    CHECK_EQ(dtype, TFE_TensorHandleDataType(axis));
-  });
-  TFE_DeleteTensorHandle(axis);
-  TFE_DeleteOp(op);
-}
-
-TEST_F(AddEagerOpToGraphTest, ListAttributesArePreserved) {
-  // Create a "Squeeze" operator with list attributes.
-  TFE_TensorHandle* axis = TestAxisTensorHandle();
-  TFE_Op* squeeze = TFE_NewOp(eager_ctx_, "Squeeze", status_);
-  CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-  TFE_OpAddInput(squeeze, axis, status_);
-  TFE_OpSetAttrType(squeeze, "T", TF_INT32);
-  std::vector<int64_t> boundaries = {1, 2, 3, 4};
-  TFE_OpSetAttrIntList(squeeze, "squeeze_dims", boundaries.data(),
-                       boundaries.size());
-  // Check attributes are preserved.
-  AddEagerOpToGraphAndCheck(
-      squeeze, [this, &boundaries](TF_Operation* squeeze_graph_op) {
-        TF_DataType dtype;
-        TF_OperationGetAttrType(squeeze_graph_op, "T", &dtype, status_);
-        CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-        CHECK_EQ(dtype, TF_INT32);
-        std::unique_ptr<int64_t[]> list(new int64_t[boundaries.size()]);
-        TF_OperationGetAttrIntList(squeeze_graph_op, "squeeze_dims", list.get(),
-                                   boundaries.size(), status_);
-        CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-        EXPECT_TRUE(std::equal(list.get(), list.get() + boundaries.size(),
-                               boundaries.begin()));
-      });
-  TFE_DeleteTensorHandle(axis);
-  TFE_DeleteOp(squeeze);
-}
-
-TEST_F(AddEagerOpToGraphTest, ListInputsAreAddedCorrectly) {
-  TFE_TensorHandle* scalar = TestScalarTensorHandle(static_cast<float>(1));
-  TFE_Op* identityn = TFE_NewOp(eager_ctx_, "IdentityN", status_);
-  CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-  constexpr size_t kNumInputs = 3;
-  for (size_t i = 0; i < kNumInputs; ++i) {
-    TFE_OpAddInput(identityn, scalar, status_);
-  }
-  TF_DataType types[kNumInputs] = {TF_FLOAT, TF_FLOAT, TF_FLOAT};
-  TFE_OpSetAttrTypeList(identityn, "T", types, kNumInputs);
-  AddEagerOpToGraphAndCheck(
-      identityn, [this, kNumInputs](TF_Operation* graph_op) {
-        EXPECT_EQ(TF_OperationNumInputs(graph_op), kNumInputs);
-        EXPECT_EQ(TF_OperationInputListLength(graph_op, "input", status_),
-                  kNumInputs);
-        CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-        EXPECT_EQ(TF_OperationOutputListLength(graph_op, "output", status_),
-                  kNumInputs);
-        CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-      });
-  TFE_DeleteTensorHandle(scalar);
-  TFE_DeleteOp(identityn);
-}
-
-TEST_F(AddEagerOpToGraphTest, NumberAttributesAreHandledCorrectly) {
-  TFE_TensorHandle* matrix = TestMatrixTensorHandle();
-  TFE_TensorHandle* axis = TestAxisTensorHandle();
-  TFE_Op* concatv2 = TFE_NewOp(eager_ctx_, "ConcatV2", status_);
-  CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-  TFE_OpSetAttrType(concatv2, "T", TF_FLOAT);
-  TFE_OpSetAttrInt(concatv2, "N", 2);
-  TFE_OpSetAttrType(concatv2, "Tidx", TF_INT32);
-  constexpr size_t kNumInputs = 2;
-  for (size_t i = 0; i < kNumInputs; ++i) {
-    TFE_OpAddInput(concatv2, matrix, status_);
-    CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-  }
-  TFE_OpAddInput(concatv2, axis, status_);
-  CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-  AddEagerOpToGraphAndCheck(
-      concatv2, [this, kNumInputs](TF_Operation* graph_op) {
-        EXPECT_EQ(TF_OperationNumInputs(graph_op), kNumInputs + 1);
-        int64_t attrN;
-        TF_OperationGetAttrInt(graph_op, "N", &attrN, status_);
-        CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-        EXPECT_EQ(attrN, kNumInputs);
-        EXPECT_EQ(TF_OperationInputListLength(graph_op, "values", status_),
-                  kNumInputs);
-        CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-      });
-  TFE_DeleteTensorHandle(axis);
-  TFE_DeleteTensorHandle(matrix);
-  TFE_DeleteOp(concatv2);
-}
-
-TEST_F(AddEagerOpToGraphTest,
-       GeneratesInternalErrorsForInvalidNumberAttributes) {
-  TFE_TensorHandle* matrix = TestMatrixTensorHandle();
-  TFE_TensorHandle* axis = TestAxisTensorHandle();
-  int num_retvals = 5;
-  TFE_TensorHandle* retvals[5];
-
-  TFE_Op* concatv2 = TFE_NewOp(eager_ctx_, "ConcatV2", status_);
-  CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
-  TFE_OpSetAttrType(concatv2, "T", TF_FLOAT);
-  TFE_OpSetAttrInt(concatv2, "N", -1);
-  TFE_OpSetAttrType(concatv2, "Tidx", TF_INT32);
-
-  TF_Operation* graph_op = TFE_AddEagerOpToGraph(concatv2, trace_ctx_, retvals,
-                                                 &num_retvals, status_);
-  EXPECT_EQ(graph_op, nullptr);
-  EXPECT_EQ(status_->status.error_message(),
-            "Number attribute for length should be >=0!");
-
-  TFE_DeleteOp(concatv2);
-  TFE_DeleteTensorHandle(axis);
-  TFE_DeleteTensorHandle(matrix);
-}
-
 class ShapeInferenceTest : public ::testing::Test {
  protected:
   ShapeInferenceTest()
diff --git a/tensorflow/c/eager/c_api_internal.h b/tensorflow/c/eager/c_api_internal.h
index 56ee1e0..024ed66 100644
--- a/tensorflow/c/eager/c_api_internal.h
+++ b/tensorflow/c/eager/c_api_internal.h
@@ -103,12 +103,6 @@
   }
 
   tensorflow::TensorHandle* handle;
-
-  // Create a symbolic tensor.
-  TFE_TensorHandle(TF_Output t, TF_DataType dtype)
-      : handle(new tensorflow::TensorHandle(
-            tensorflow::OutputGraphNode{t.oper, t.index},
-            static_cast<tensorflow::DataType>(dtype))) {}
 };
 
 struct TFE_TensorDebugInfo {
@@ -285,26 +279,6 @@
                           const char* attr_name, TF_Status* status);
 }  // namespace tensorflow
 
-struct TFE_TraceContext {
-  TF_Graph* const graph;
-
-  unsigned int node_counter = 0;
-  // Each tensor handle will have its ref count incremented when it's added as a
-  // map key, and decremented when this object is destroyed.
-  std::map<tensorflow::TensorHandle*, TF_Output> input_tensor_map;
-  std::vector<std::pair<tensorflow::TensorHandle*, TF_Output>>* input_tensors =
-      nullptr;
-
-  explicit TFE_TraceContext(TF_Graph* graph) : graph(graph) {}
-
-  ~TFE_TraceContext() {
-    delete input_tensors;
-    for (auto input : input_tensor_map) {
-      input.first->Unref();
-    }
-  }
-};
-
 struct TFE_CancellationManager {
   tensorflow::CancellationManager cancellation_manager;
 };
diff --git a/tensorflow/core/common_runtime/eager/tensor_handle.cc b/tensorflow/core/common_runtime/eager/tensor_handle.cc
index 1be22c7..8838f17 100644
--- a/tensorflow/core/common_runtime/eager/tensor_handle.cc
+++ b/tensorflow/core/common_runtime/eager/tensor_handle.cc
@@ -261,23 +261,6 @@
 }
 #endif
 
-TensorHandle::TensorHandle(OutputGraphNode symbolic_tensor, DataType dtype)
-    : dtype(dtype),
-      device_(nullptr),
-      op_device_(nullptr),
-      resource_device_(nullptr),
-#if !defined(IS_MOBILE_PLATFORM)
-      remote_op_id_(kInvalidOpId),
-      remote_output_num_(kInvalidOutputNum),
-#endif
-      ctx_(nullptr),
-      is_remote_(false),
-      symbolic_tensor_(new OutputGraphNode(symbolic_tensor)) {
-  DVLOG(3) << "Creating Symbolic TensorHandle: " << this;
-  // Notify immediately since this handle is already ready.
-  is_ready_notification_.Notify();
-}
-
 bool TensorHandle::IsReady() {
   return is_ready_notification_.HasBeenNotified();
 }
@@ -666,11 +649,6 @@
 string TensorHandle::DebugString() const {
   DVLOG(1) << "Calling TensorHandle::DebugString() on " << this;
 
-  if (symbolic_tensor_) {
-    return absl::Substitute("TF_Output($0, $1)", symbolic_tensor_->oper,
-                            symbolic_tensor_->index);
-  }
-
   string out;
   strings::StrAppend(&out, "Device: ", device_ ? device_->DebugString() : "[]");
   // Consider supporting non-CPU tensors (when device_ is non-NULL) if needed.
diff --git a/tensorflow/core/common_runtime/eager/tensor_handle.h b/tensorflow/core/common_runtime/eager/tensor_handle.h
index d2be6a4..a8b05e3 100644
--- a/tensorflow/core/common_runtime/eager/tensor_handle.h
+++ b/tensorflow/core/common_runtime/eager/tensor_handle.h
@@ -54,17 +54,8 @@
 #include "tensorflow/core/public/session_options.h"
 #include "tensorflow/core/public/version.h"
 
-struct TF_Operation;
-
 namespace tensorflow {
 
-// This struct is isomorphic to TF_Output, but we cannot use the latter here due
-// to layering concerns (TF_Output is defined at the C API layer).
-struct OutputGraphNode {
-  TF_Operation* oper;
-  int index;  // The index of the output within oper.
-};
-
 // Associates a Tensor and a Device, used in the eager runtime. Internal version
 // of the TFE_TensorHandle struct and the python EagerTensor class
 // (unrelated to python TensorHandle).
@@ -120,9 +111,6 @@
       Device* device, EagerContext* ctx, TensorHandle** h);
 #endif  // IS_MOBILE_PLATFORM
 
-  // Symbolic tensor constructor.
-  TensorHandle(OutputGraphNode symbolic_tensor, DataType dtype);
-
   ~TensorHandle() override { DVLOG(3) << "Deleting TensorHandle " << this; }
 
   Status Tensor(const tensorflow::Tensor** t);
@@ -208,8 +196,6 @@
 
   bool IsRemote() const { return is_remote_; }
 
-  OutputGraphNode* getSymbolicTensor() const { return symbolic_tensor_.get(); }
-
   string DebugString() const;
 
   void SetResourceHandleDtypeAndShape(
@@ -239,8 +225,7 @@
   // Device in which the op producing this tensor was executed. Equals to
   // device_ for constant tensors.
   // Can be nullptr if the op producing this tensor was a function executed
-  // with function library runtime or if this tensor represents a symbolic
-  // tensor.
+  // with function library runtime.
   tensorflow::Device* const op_device_;
 
   // If the tensor dtype is DT_RESOURCE, resource_device_ holds the device
@@ -287,11 +272,6 @@
   Status is_poisoned_;
   const bool is_remote_;
 
-  // When non-NULL, this tensor handle instance represents a symbolic tensor
-  // (corresponding to a graph node), whose concrete value is to be produced by
-  // executing that graph node.
-  std::unique_ptr<OutputGraphNode> symbolic_tensor_;
-
   // If this TensorHandle 1) is a local tensor, and 2) is a resource handle or
   // refers to a remote resource handle, we store data types and shapes for
   // the underlying resource.