Implement operands for the lower and upper bounds of the for statement.

This revamps implementation of the loop bounds in the ForStmt, using general representation that supports operands. The frequent case of constant bounds is supported
via special access methods.

This also includes:
- Operand iterators for the Statement class.
- OpPointer::is() method to query the class of the Operation.
- Support for the bound shorthand notation parsing and printing.
- Validity checks for the bound operands used as dim ids and symbols

I didn't mean this CL to be so large. It just happened this way, as one thing led to another.

PiperOrigin-RevId: 210204858
diff --git a/lib/IR/SSAValue.cpp b/lib/IR/SSAValue.cpp
index 50f7fb0..04de6db 100644
--- a/lib/IR/SSAValue.cpp
+++ b/lib/IR/SSAValue.cpp
@@ -1,4 +1,4 @@
-//===- Instructions.cpp - MLIR CFGFunction Instruction Classes ------------===//
+//===- SSAValue.cpp - MLIR SSAValue Classes ------------===//
 //
 // Copyright 2019 The MLIR Authors.
 //
@@ -17,6 +17,7 @@
 
 #include "mlir/IR/SSAValue.h"
 #include "mlir/IR/Instructions.h"
+#include "mlir/IR/StandardOps.h"
 #include "mlir/IR/Statements.h"
 using namespace mlir;
 
@@ -43,3 +44,43 @@
     return stmt;
   return nullptr;
 }
+
+//===----------------------------------------------------------------------===//
+// MLValue implementation.
+//===----------------------------------------------------------------------===//
+
+// MLValue can be used a a dimension id if it is valid as a symbol, or
+// it is an induction variable, or it is a result of affine apply operation
+// with dimension id arguments.
+bool MLValue::isValidDim() const {
+  if (auto *stmt = getDefiningStmt()) {
+    // Top level statement or constant operation is ok.
+    if (stmt->getParentStmt() == nullptr || stmt->is<ConstantOp>())
+      return true;
+    // Affine apply operation is ok if all of its operands are ok.
+    if (auto op = stmt->getAs<AffineApplyOp>())
+      return op->isValidDim();
+    return false;
+  }
+  // This value is either a function argument or an induction variable. Both are
+  // ok.
+  return true;
+}
+
+// MLValue can be used as a symbol if it is a constant, or it is defined at
+// the top level, or it is a result of affine apply operation with symbol
+// arguments.
+bool MLValue::isValidSymbol() const {
+  if (auto *stmt = getDefiningStmt()) {
+    // Top level statement or constant operation is ok.
+    if (stmt->getParentStmt() == nullptr || stmt->is<ConstantOp>())
+      return true;
+    // Affine apply operation is ok if all of its operands are ok.
+    if (auto op = stmt->getAs<AffineApplyOp>())
+      return op->isValidSymbol();
+    return false;
+  }
+  // This value is either a function argument or an induction variable.
+  // Function argument is ok, induction variable is not.
+  return isa<MLFuncArgument>(this);
+}