Integrate llvm-project at e9c9ee9fe694067ee96643d05d6ac378349386bb (#8585)

* Integrate llvm-project at e9c9ee9fe694067ee96643d05d6ac378349386bb

* Reset third_party/llvm-project: e9c9ee9fe694067ee96643d05d6ac378349386bb (2022-03-15 21:51:12 +0000): [libc][NFC] Fix typos and reduntent code triggering compiler warinings.

* Move MHLO and TF to matching commits

TF: 05f17fca35623f4ab6d275ed95f0e1363c939f73
MHLO: 57288f12595a2ee0488806672a42da59b1e56e13
Piper CL: 435187843

* Fixes for bump LLVM @5e8700ce8bf58bdf0a59eef99c85185a74177555

* Remove uses of `verifier`.

* Fix verification methods after signature change of custom verify methods.

* Fixup fallout from bufferization changes

https://reviews.llvm.org/D121361
https://reviews.llvm.org/D121519

* Fix verifiers of Flow and VM ops.

* Fix lit test.

* Update iree-dialects in integrations.

Co-authored-by: Nicolas Vasilache <ntv@google.com>
Co-authored-by: Stella Laurenzo <stellaraccident@gmail.com>
diff --git a/llvm-external-projects/iree-dialects/lib/Dialect/Input/InputDialect.cpp b/llvm-external-projects/iree-dialects/lib/Dialect/Input/InputDialect.cpp
index 060d308..a12a1b9 100644
--- a/llvm-external-projects/iree-dialects/lib/Dialect/Input/InputDialect.cpp
+++ b/llvm-external-projects/iree-dialects/lib/Dialect/Input/InputDialect.cpp
@@ -29,3 +29,41 @@
 #include "iree-dialects/Dialect/Input/InputOps.cpp.inc"
       >();
 }
+
+namespace mlir {
+namespace iree_compiler {
+namespace IREE {
+namespace Input {
+
+// ListType
+Type ListType::parse(AsmParser &parser) {
+  MLIRContext *ctxt = parser.getContext();
+  Type elementType;
+  if (parser.parseLess() || parser.parseType(elementType) ||
+      parser.parseGreater())
+    return Type();
+  return get(ctxt, elementType);
+}
+
+void ListType::print(AsmPrinter &printer) const {
+  printer << "<" << getElementType() << ">";
+}
+
+// PtrType
+Type PtrType::parse(AsmParser &parser) {
+  MLIRContext *ctxt = parser.getContext();
+  Type targetType;
+  if (parser.parseLess() || parser.parseType(targetType) ||
+      parser.parseGreater())
+    return Type();
+  return get(ctxt, targetType);
+}
+
+void PtrType::print(AsmPrinter &printer) const {
+  printer << "<" << getTargetType() << ">";
+}
+
+}  // namespace Input
+}  // namespace IREE
+}  // namespace iree_compiler
+}  // namespace mlir
diff --git a/llvm-external-projects/iree-dialects/lib/Dialect/LinalgExt/IR/LinalgExtOps.cpp b/llvm-external-projects/iree-dialects/lib/Dialect/LinalgExt/IR/LinalgExtOps.cpp
index cabc5c4..af9ae07 100644
--- a/llvm-external-projects/iree-dialects/lib/Dialect/LinalgExt/IR/LinalgExtOps.cpp
+++ b/llvm-external-projects/iree-dialects/lib/Dialect/LinalgExt/IR/LinalgExtOps.cpp
@@ -104,48 +104,49 @@
 //===----------------------------------------------------------------------===//
 // ScatterOp
 //===----------------------------------------------------------------------===//
-static LogicalResult verifyScatterOp(ScatterOp op) {
-  if (op.inputs().size() != 2) {
-    return op.emitOpError("expected two input operands");
+LogicalResult ScatterOp::verify() {
+  Operation *op = getOperation();
+  if (inputs().size() != 2) {
+    return op->emitOpError("expected two input operands");
   }
-  if (op.outputs().size() != 1) {
-    return op.emitOpError("expected one output operand");
+  if (outputs().size() != 1) {
+    return op->emitOpError("expected one output operand");
   }
   auto checkDimensionsMatch = [&](ShapedType t1, ShapedType t2, unsigned dim) {
     return t1.getShape()[dim] == t2.getShape()[dim];
   };
 
-  auto indicesType = op.getIndicesType();
+  auto indicesType = getIndicesType();
   if (indicesType.getRank() != 2 ||
       !indicesType.getElementType().isInteger(32)) {
-    return op.emitOpError(
+    return op->emitOpError(
         "expected indices to be of rank 2 of i32 element type");
   }
-  auto indexDepth = op.getIndexDepth();
+  auto indexDepth = getIndexDepth();
   if (indexDepth == ShapedType::kDynamicSize) {
-    return op.emitOpError("expected index depth is static");
+    return op->emitOpError("expected index depth is static");
   }
 
   // The first dimension of the indices should match the first dimension of the
   // output. They indicate to the number of updates.
-  auto updateType = op.getUpdateType();
+  auto updateType = getUpdateType();
   if (updateType.getRank() < 1) {
-    return op.emitOpError("expected update value to be at least rank 1");
+    return op->emitOpError("expected update value to be at least rank 1");
   }
   if (!checkDimensionsMatch(indicesType, updateType, 0)) {
-    return op.emitOpError(
+    return op->emitOpError(
         "mismatch in shape of indices and update value at dim#0");
   }
-  auto originalType = op.getOriginalType();
+  auto originalType = getOriginalType();
   if (updateType.getRank() - 1 > originalType.getRank()) {
-    return op.emitOpError(
+    return op->emitOpError(
         "update value rank exceeds the rank of the original value");
   }
 
   // indexDepth + update dims should cover the original dims. The first dim of
   // update is the number of updates.
   if (originalType.getRank() > indexDepth + updateType.getRank() - 1) {
-    return op.emitOpError(
+    return op->emitOpError(
         "index depth and update value does not cover rank of original value");
   }
 
@@ -160,7 +161,7 @@
     int64_t updateDim = std::get<1>(it);
     if (updateType.getDimSize(updateDim) !=
         originalType.getDimSize(originalDim)) {
-      return op.emitOpError("mismatch in shape of update value dim#")
+      return op->emitOpError("mismatch in shape of update value dim#")
              << updateDim << " and original value at dim#" << originalDim;
     }
   }
@@ -174,36 +175,36 @@
     int64_t updateDim = std::get<1>(it);
     if (updateType.getDimSize(updateDim) >
         originalType.getDimSize(originalDim)) {
-      return op.emitOpError("indexed shape of update value dim#")
+      return op->emitOpError("indexed shape of update value dim#")
              << updateDim << " exceeds original value at dim#" << originalDim
              << " " << updateType.getDimSize(updateDim) << " "
              << originalType.getDimSize(originalDim);
     }
   }
 
-  Region &region = op.region();
+  Region &region = this->region();
   Block *body = &region.front();
   if (body->getNumArguments() != 2) {
-    return op.emitOpError("expected region to have two arguments");
+    return op->emitOpError("expected region to have two arguments");
   }
   Type arg0Type = body->getArgument(0).getType();
   Type arg1Type = body->getArgument(1).getType();
   if (!arg0Type.isIntOrFloat() || !arg1Type.isIntOrFloat()) {
-    return op.emitOpError(
+    return op->emitOpError(
         "expected region to have scalar argument of integer or float types");
   }
   if (arg0Type != updateType.getElementType()) {
-    return op.emitOpError("mismatch in argument 0 of region ")
+    return op->emitOpError("mismatch in argument 0 of region ")
            << arg0Type << " and element type of update value "
            << updateType.getElementType();
   }
   if (arg1Type != originalType.getElementType()) {
-    return op.emitOpError("mismatch in argument 1 of region ")
+    return op->emitOpError("mismatch in argument 1 of region ")
            << arg1Type << " and element type of original value "
            << originalType.getElementType();
   }
   if (arg0Type != arg1Type) {
-    return op.emitOpError("mismatch in region argument types ")
+    return op->emitOpError("mismatch in region argument types ")
            << arg0Type << " and " << arg1Type;
   }
   auto yieldOp = cast<IREE::LinalgExt::YieldOp>(body->getTerminator());
@@ -354,44 +355,45 @@
 // SortOp
 //===----------------------------------------------------------------------===//
 
-static LogicalResult verifySortOp(SortOp op) {
-  if (op.getNumInputs()) {
-    return op.emitOpError("does not expect to take any inputs");
+LogicalResult SortOp::verify() {
+  Operation *op = getOperation();
+  if (getNumInputs()) {
+    return op->emitOpError("does not expect to take any inputs");
   }
-  if (op.getNumOutputs() == 0) {
-    return op.emitOpError("expected at least one `outs` operand");
+  if (getNumOutputs() == 0) {
+    return op->emitOpError("expected at least one `outs` operand");
   }
 
-  Block &block = op.region().front();
-  size_t numOutputs = op.getNumOutputs();
+  Block &block = region().front();
+  size_t numOutputs = getNumOutputs();
   if (block.getNumArguments() != 2 * numOutputs) {
-    return op.emitOpError("region block should have ")
+    return op->emitOpError("region block should have ")
            << 2 * numOutputs << " arguments";
   }
 
-  int64_t rank = op.getOperandRank();
-  int sortDim = op.dimension();
+  int64_t rank = getOperandRank();
+  int sortDim = dimension();
   if (sortDim < 0 || sortDim >= rank) {
-    return op.emitOpError("dimension must be within (0, ") << rank << "]";
+    return op->emitOpError("dimension must be within (0, ") << rank << "]";
   }
 
-  ArrayRef<int64_t> shape = op.getOperandShape();
-  for (auto indexedOperand : llvm::enumerate(op.outputs())) {
+  ArrayRef<int64_t> shape = getOperandShape();
+  for (auto indexedOperand : llvm::enumerate(outputs())) {
     int index = indexedOperand.index();
-    auto operandType = op.getOperandType(index);
+    auto operandType = getOperandType(index);
     if (operandType.getRank() != rank) {
-      return op.emitOpError("expected operand ")
+      return op->emitOpError("expected operand ")
              << index << " to be rank " << rank << ", same as other operands";
     }
     if (operandType.getShape() != shape) {
-      return op.emitOpError("expected operand ")
+      return op->emitOpError("expected operand ")
              << index << " to have same shape as other operands";
     }
     Type elemType = operandType.getElementType();
     for (int i : {2 * index, 2 * index + 1}) {
       Type argType = block.getArgument(i).getType();
       if (argType != elemType) {
-        return op.emitOpError("region block argument #")
+        return op->emitOpError("region block argument #")
                << i << " should be of type " << elemType << " but got "
                << argType;
       }
@@ -400,11 +402,11 @@
 
   auto yieldOp = cast<YieldOp>(block.getTerminator());
   if (yieldOp.getNumOperands() != 1) {
-    return op.emitOpError("should yield exactly one operand");
+    return op->emitOpError("should yield exactly one operand");
   }
   auto ty = yieldOp.getOperand(0).getType().dyn_cast<IntegerType>();
   if (!ty || ty.getWidth() != 1) {
-    return op.emitOpError("should yield i1 type");
+    return op->emitOpError("should yield i1 type");
   }
 
   return success();
@@ -560,26 +562,28 @@
 // FftOp
 //===----------------------------------------------------------------------===//
 
-static LogicalResult verifyFftOp(FftOp op) {
-  auto length = op.getFftLength();
+LogicalResult FftOp::verify() {
+  Operation *op = getOperation();
+  auto length = getFftLength();
   // After tiling, it could be dynamic shape. (Because
   // subview/subtensor does not inference the type correctly
   // on (1 << x)) cases).
   if (length == ShapedType::kDynamicSize) return success();
   if (length & (length - 1)) {
-    return op.emitOpError("only powers of 2 are handled currently");
+    return op->emitOpError("only powers of 2 are handled currently");
   }
-  if (!op.getNumInputs() || !op.isScalar(op.getInputOperand(0))) {
-    return op.emitOpError("expected to carry `stage` input");
+  if (!getNumInputs() || !isScalar(getInputOperand(0))) {
+    return op->emitOpError("expected to carry `stage` input");
   }
-  if (op.getNumInputs() != 1) {
-    if (op.getNumInputs() != 3 || op.isScalar(op.getInputOperand(1)) ||
-        op.isScalar(op.getInputOperand(2))) {
-      return op.emitOpError("expected to carry real and imag coeff inputs");
+  if (getNumInputs() != 1) {
+    if (getNumInputs() != 3 || isScalar(getInputOperand(1)) ||
+        isScalar(getInputOperand(2))) {
+      return op->emitOpError("expected to carry real and imag coeff inputs");
     }
   }
-  if (op.getNumOutputs() != 2) {
-    return op.emitOpError("expected outputs to be real and imag tensor/memref");
+  if (getNumOutputs() != 2) {
+    return op->emitOpError(
+        "expected outputs to be real and imag tensor/memref");
   }
   return success();
 }
@@ -810,34 +814,35 @@
 // ScanOp
 //===----------------------------------------------------------------------===//
 
-static LogicalResult verifyScanOp(ScanOp op) {
-  if (op.getNumInputs() != 1) {
-    return op.emitOpError("expected one input operands");
+LogicalResult ScanOp::verify() {
+  Operation *op = getOperation();
+  if (getNumInputs() != 1) {
+    return op->emitOpError("expected one input operands");
   }
-  if (op.getNumOutputs() != 2) {
-    return op.emitOpError("expected two output operands");
+  if (getNumOutputs() != 2) {
+    return op->emitOpError("expected two output operands");
   }
-  if (!op.input().getType().isa<ShapedType>()) {
-    return op.emitOpError("expected first input element type to be shaped");
+  if (!input().getType().isa<ShapedType>()) {
+    return op->emitOpError("expected first input element type to be shaped");
   }
-  auto accumulatorType = op.accumulator().getType().cast<ShapedType>();
-  auto inputType = op.input().getType().cast<ShapedType>();
-  auto outputType = op.output().getType().cast<ShapedType>();
+  auto accumulatorType = accumulator().getType().cast<ShapedType>();
+  auto inputType = input().getType().cast<ShapedType>();
+  auto outputType = output().getType().cast<ShapedType>();
   ArrayRef<int64_t> inputShapes = inputType.getShape();
   ArrayRef<int64_t> outputShapes = outputType.getShape();
   if (accumulatorType.getElementType() != inputType.getElementType()) {
-    return op.emitOpError(
+    return op->emitOpError(
         "expected input/accumulator element types to be identical");
   }
   ArrayRef<int64_t> accumulatorShape = accumulatorType.getShape();
   int64_t accumulatorRank = accumulatorType.getRank();
   if (accumulatorRank != inputType.getRank() - 1) {
-    return op.emitOpError(
+    return op->emitOpError(
         "expected accumulator rank to be equal to input rank - 1");
   }
   SmallVector<int64_t> expectedAccumulatorShape;
   for (int i = 0; i < inputType.getRank(); i++) {
-    if (i != op.dimension()) expectedAccumulatorShape.push_back(inputShapes[i]);
+    if (i != dimension()) expectedAccumulatorShape.push_back(inputShapes[i]);
   }
   if (llvm::any_of(llvm::zip(expectedAccumulatorShape, accumulatorShape),
                    [](std::tuple<int64_t, int64_t> s) {
@@ -845,14 +850,14 @@
                             std::get<1>(s) != ShapedType::kDynamicSize &&
                             std::get<0>(s) != std::get<1>(s);
                    })) {
-    return op.emitOpError("incompatible input/accumulator shapes");
+    return op->emitOpError("incompatible input/accumulator shapes");
   }
   if (inputType.getElementType() != outputType.getElementType()) {
-    return op.emitOpError(
+    return op->emitOpError(
         "expected input/output element types to be identical");
   }
   if (inputShapes.size() != outputShapes.size()) {
-    return op.emitOpError("expected input/output to have identical ranks");
+    return op->emitOpError("expected input/output to have identical ranks");
   }
   if (llvm::any_of(llvm::zip(inputShapes, outputShapes),
                    [](std::tuple<int64_t, int64_t> s) {
@@ -860,7 +865,7 @@
                             std::get<1>(s) != ShapedType::kDynamicSize &&
                             std::get<0>(s) != std::get<1>(s);
                    })) {
-    return op.emitOpError("incompatible input/output shapes");
+    return op->emitOpError("incompatible input/output shapes");
   }
   return success();
 }
@@ -1042,23 +1047,24 @@
 // ReverseOp
 //===----------------------------------------------------------------------===//
 
-static LogicalResult verifyReverseOp(ReverseOp op) {
-  if (op.getNumInputs() != 1) {
-    return op.emitOpError("expected exactly one input");
+LogicalResult ReverseOp::verify() {
+  Operation *op = getOperation();
+  if (getNumInputs() != 1) {
+    return op->emitOpError("expected exactly one input");
   }
-  if (op.getNumOutputs() != 1) {
-    return op.emitOpError("expected exactly one output");
+  if (getNumOutputs() != 1) {
+    return op->emitOpError("expected exactly one output");
   }
-  auto inputType = op.input().getType().cast<ShapedType>();
-  auto outputType = op.output().getType().cast<ShapedType>();
+  auto inputType = input().getType().cast<ShapedType>();
+  auto outputType = output().getType().cast<ShapedType>();
   if (inputType.getElementType() != outputType.getElementType()) {
-    return op.emitOpError(
+    return op->emitOpError(
         "expected input/output element types to be identical");
   }
   ArrayRef<int64_t> inputShapes = inputType.getShape();
   ArrayRef<int64_t> outputShapes = outputType.getShape();
   if (inputShapes.size() != outputShapes.size()) {
-    return op.emitOpError("expexted input/output to have identical ranks");
+    return op->emitOpError("expexted input/output to have identical ranks");
   }
   if (llvm::any_of(llvm::zip(inputShapes, outputShapes),
                    [](std::tuple<int64_t, int64_t> s) {
@@ -1066,18 +1072,18 @@
                             std::get<1>(s) != ShapedType::kDynamicSize &&
                             std::get<0>(s) != std::get<1>(s);
                    })) {
-    return op.emitOpError("incompatible input/output shapes");
+    return op->emitOpError("incompatible input/output shapes");
   }
 
-  int64_t rank = op.getOperandRank();
+  int64_t rank = getOperandRank();
   llvm::SmallSetVector<int64_t, 4> s;
-  for (auto dim : op.dims()) {
+  for (auto dim : dims()) {
     if (dim < 0 || dim >= rank) {
-      return op.emitOpError("all the dimensions must be within [0, ")
+      return op->emitOpError("all the dimensions must be within [0, ")
              << rank << ")";
     }
     if (s.contains(dim)) {
-      return op.emitOpError("expected dimensions numbers are all unique");
+      return op->emitOpError("expected dimensions numbers are all unique");
     }
     s.insert(dim);
   }
diff --git a/llvm-external-projects/iree-dialects/lib/Dialect/PyDM/IR/PyDMDialect.cpp b/llvm-external-projects/iree-dialects/lib/Dialect/PyDM/IR/PyDMDialect.cpp
index 1915da7..82381bc 100644
--- a/llvm-external-projects/iree-dialects/lib/Dialect/PyDM/IR/PyDMDialect.cpp
+++ b/llvm-external-projects/iree-dialects/lib/Dialect/PyDM/IR/PyDMDialect.cpp
@@ -33,7 +33,10 @@
 using PyBoolType = PYDM::BoolType;
 using PyConstantOp = PYDM::ConstantOp;
 using PyIntegerType = PYDM::IntegerType;
+using PyListType = PYDM::ListType;
 using PyRealType = PYDM::RealType;
+using PyObjectType = PYDM::ObjectType;
+using PyUnionType = PYDM::UnionType;
 
 void IREEPyDMDialect::initialize() {
   addTypes<
@@ -115,6 +118,49 @@
   return emitError() << "unsupported python integer bit width: " << w;
 }
 
+Type PyIntegerType::parse(mlir::AsmParser &parser) {
+  MLIRContext *ctxt = parser.getContext();
+  auto emitError = [&]() -> InFlightDiagnostic {
+    return parser.emitError(parser.getCurrentLocation());
+  };
+  // Weak
+  if (failed(parser.parseOptionalLess())) return get(ctxt);
+  // AP
+  if (succeeded(parser.parseOptionalStar())) {
+    if (failed(parser.parseGreater())) return Type();
+    return get(ctxt, None);
+  }
+
+  // Explicit
+  bool isSigned;
+  if (succeeded(parser.parseOptionalKeyword("unsigned"))) {
+    isSigned = false;
+  } else {
+    isSigned = true;
+  }
+
+  int width;
+  if (failed(parser.parseInteger(width))) return Type();
+  if (failed(parser.parseGreater())) return Type();
+  if (!isSigned) width = -width;
+  return getChecked(emitError, ctxt, width);
+}
+
+void PyIntegerType::print(mlir::AsmPrinter &printer) const {
+  auto w = getImpl()->bitWidth;
+  if (w) {
+    printer << "<";
+    if (*w == 0) {
+      printer << "*";
+    } else if (*w > 0) {
+      printer << *w;
+    } else {
+      printer << "unsigned " << (-*w);
+    }
+    printer << ">";
+  }
+}
+
 BuiltinTypeCode PYDM::IntegerType::getTypeCode() const {
   return static_cast<BuiltinTypeCode>(
       makeNumericTypeCode(*getNumericCategory(), *getNumericSubTypeCode()));
@@ -170,6 +216,57 @@
 }
 
 // ListType
+void PyListType::print(mlir::AsmPrinter &printer) const {
+  if (getImpl()->uniformElementType ||
+      getImpl()->storageClass != CollectionStorageClass::Boxed) {
+    printer << "<";
+    switch (getImpl()->storageClass) {
+      case CollectionStorageClass::Boxed:
+        printer << "boxed";
+        break;
+      case CollectionStorageClass::Empty:
+        printer << "empty";
+        break;
+      case CollectionStorageClass::Unboxed:
+        printer << "unboxed";
+        break;
+    }
+
+    if (getImpl()->uniformElementType) {
+      printer << ",";
+      printer << getImpl()->uniformElementType;
+    }
+    printer << ">";
+  }
+}
+
+Type PyListType::parse(mlir::AsmParser &parser) {
+  MLIRContext *ctxt = parser.getContext();
+  if (parser.parseOptionalLess())
+    return get(ctxt, CollectionStorageClass::Boxed, nullptr);
+
+  Type t;
+  StringRef storageClassKeyword;
+  if (parser.parseKeyword(&storageClassKeyword)) return Type();
+  if (parser.parseComma()) return Type();
+  if (parser.parseType(t)) return Type();
+  if (parser.parseGreater()) return Type();
+
+  CollectionStorageClass storageClass;
+  if (storageClassKeyword == "boxed")
+    storageClass = CollectionStorageClass::Boxed;
+  else if (storageClassKeyword == "empty")
+    storageClass = CollectionStorageClass::Empty;
+  else if (storageClassKeyword == "unboxed")
+    storageClass = CollectionStorageClass::Unboxed;
+  else {
+    parser.emitError(parser.getCurrentLocation(),
+                     "expected one of 'boxed', 'empty', 'unboxed'");
+    return Type();
+  }
+  return get(ctxt, storageClass, t);
+}
+
 StringRef PYDM::ListType::getPythonTypeName() const { return "list"; }
 
 BuiltinTypeCode PYDM::NoneType::getTypeCode() const {
@@ -206,6 +303,26 @@
 StringRef PYDM::NoneType::getPythonTypeName() const { return "None"; }
 
 // ObjectType
+void PyObjectType::print(mlir::AsmPrinter &printer) const {
+  if (getImpl()->primitiveType)
+    printer << "<" << getImpl()->primitiveType << ">";
+}
+
+Type PyObjectType::parse(mlir::AsmParser &parser) {
+  MLIRContext *ctxt = parser.getContext();
+  if (parser.parseOptionalLess()) return get(ctxt, nullptr);
+
+  Type t;
+  if (parser.parseType(t)) return Type();
+  if (parser.parseGreater()) return Type();
+  if (auto primitiveType = t.dyn_cast<PrimitiveType>())
+    return get(ctxt, primitiveType);
+  else {
+    parser.emitError(parser.getNameLoc(), "expected a primitive type");
+    return Type();
+  }
+}
+
 BuiltinTypeCode PYDM::ObjectType::getTypeCode() const {
   return BuiltinTypeCode::Object;
 }
@@ -222,6 +339,26 @@
 }
 
 // RealType
+void PyRealType::print(mlir::AsmPrinter &printer) const {
+  auto ft = getImpl()->floatType;
+  if (ft) printer << "<" << ft << ">";
+}
+
+Type PyRealType::parse(mlir::AsmParser &parser) {
+  MLIRContext *ctxt = parser.getContext();
+
+  auto emitError = [&]() -> InFlightDiagnostic {
+    return parser.emitError(parser.getCurrentLocation());
+  };
+  // Weak
+  if (failed(parser.parseOptionalLess())) return get(ctxt);
+  // Explicit
+  FloatType subType;
+  if (failed(parser.parseType(subType))) return Type();
+  if (failed(parser.parseGreater())) return Type();
+  return getChecked(emitError, ctxt, subType);
+}
+
 LogicalResult PYDM::RealType::verify(
     function_ref<InFlightDiagnostic()> emitError, FloatType floatType) {
   if (!floatType) return success();
@@ -295,6 +432,26 @@
 // Union type implementation
 //------------------------------------------------------------------------------
 
+void PyUnionType::print(mlir::AsmPrinter &printer) const {
+  llvm::interleaveComma(getAlternatives(), printer);
+}
+
+Type PyUnionType::parse(mlir::AsmParser &parser) {
+  MLIRContext *ctxt = parser.getContext();
+  if (parser.parseOptionalLess()) return get(ctxt, {});
+
+  SmallVector<::mlir::Type> alternatives;
+
+  do {
+    Type type;
+    if (parser.parseType(type)) return Type();
+    alternatives.push_back(type);
+  } while (succeeded(parser.parseOptionalComma()));
+
+  return getChecked([&]() { return parser.emitError(parser.getNameLoc()); },
+                    ctxt, alternatives);
+}
+
 LogicalResult PYDM::UnionType::verify(
     llvm::function_ref<InFlightDiagnostic()> emitError,
     ArrayRef<Type> alternatives) {
diff --git a/llvm-external-projects/iree-dialects/lib/Dialect/PyDM/IR/PyDMOps.cpp b/llvm-external-projects/iree-dialects/lib/Dialect/PyDM/IR/PyDMOps.cpp
index cbb07b8..2010688 100644
--- a/llvm-external-projects/iree-dialects/lib/Dialect/PyDM/IR/PyDMOps.cpp
+++ b/llvm-external-projects/iree-dialects/lib/Dialect/PyDM/IR/PyDMOps.cpp
@@ -29,8 +29,6 @@
 using PyCallOp = PYDM::CallOp;
 using PyFuncOp = PYDM::FuncOp;
 
-static LogicalResult verify(Operation *) { return success(); }
-
 //===----------------------------------------------------------------------===//
 // Utilities
 //===----------------------------------------------------------------------===//
@@ -439,9 +437,9 @@
 
 ::llvm::StringRef FunctionalIfOp::getDefaultDialect() { return "iree_pydm"; }
 
-static LogicalResult verify(FunctionalIfOp op) {
-  if (op.getNumResults() != 0 && op.elseRegion().empty())
-    return op.emitOpError("must have an else block if defining values");
+LogicalResult FunctionalIfOp::verify() {
+  if (getNumResults() != 0 && elseRegion().empty())
+    return emitOpError("must have an else block if defining values");
   return success();
 }
 
@@ -562,39 +560,34 @@
       p, *this, fnType.getInputs(), /*isVariadic=*/false, fnType.getResults());
 }
 
-static LogicalResult verify(PyFuncOp op) {
-  // TODO: Enforce invariants.
-  return success();
-}
-
 //===----------------------------------------------------------------------===//
 // MakeListOp
 //===----------------------------------------------------------------------===//
 
-static LogicalResult verify(MakeListOp op) {
-  auto listType = op.list().getType().cast<ListType>();
+LogicalResult MakeListOp::verify() {
+  auto listType = list().getType().cast<ListType>();
   switch (listType.getStorageClass()) {
     case CollectionStorageClass::Boxed:
-      for (auto element : op.elements()) {
+      for (auto element : elements()) {
         if (!element.getType().isa<ObjectType>()) {
-          return op.emitOpError() << "making a list with boxed storage class "
-                                     "must have object elements. Got: "
-                                  << element.getType();
+          return emitOpError() << "making a list with boxed storage class "
+                                  "must have object elements. Got: "
+                               << element.getType();
         }
       }
       break;
     case CollectionStorageClass::Unboxed:
-      for (auto element : op.elements()) {
+      for (auto element : elements()) {
         if (element.getType().isa<ObjectType>()) {
-          return op.emitOpError() << "making a list with unboxed storage class "
-                                     "must not have object elements. Got: "
-                                  << element.getType();
+          return emitOpError() << "making a list with unboxed storage class "
+                                  "must not have object elements. Got: "
+                               << element.getType();
         }
       }
       break;
     case CollectionStorageClass::Empty:
-      if (!op.elements().empty()) {
-        return op.emitOpError()
+      if (!elements().empty()) {
+        return emitOpError()
                << "making a list with empty storage class must have zero "
                   "elements";
       }