diff --git a/mlir/include/mlir/Dialect/SCF/SCFOps.td b/mlir/include/mlir/Dialect/SCF/SCFOps.td
--- a/mlir/include/mlir/Dialect/SCF/SCFOps.td
+++ b/mlir/include/mlir/Dialect/SCF/SCFOps.td
@@ -36,8 +36,12 @@
   let parser = [{ return ::parse$cppClass(parser, result); }];
 }
 
-def ConditionOp : SCF_Op<"condition",
-                         [HasParent<"WhileOp">, NoSideEffect, Terminator]> {
+def ConditionOp : SCF_Op<"condition", [
+  HasParent<"WhileOp">,
+  DeclareOpInterfaceMethods<RegionBranchTerminatorOpInterface>,
+  NoSideEffect,
+  Terminator
+]> {
   let summary = "loop continuation condition";
   let description = [{
     This operation accepts the continuation (i.e., inverse of exit) condition
diff --git a/mlir/include/mlir/Interfaces/ControlFlowInterfaces.h b/mlir/include/mlir/Interfaces/ControlFlowInterfaces.h
--- a/mlir/include/mlir/Interfaces/ControlFlowInterfaces.h
+++ b/mlir/include/mlir/Interfaces/ControlFlowInterfaces.h
@@ -86,6 +86,32 @@
   ValueRange inputs;
 };
 
+//===----------------------------------------------------------------------===//
+// RegionBranchTerminatorOpInterface
+//===----------------------------------------------------------------------===//
+
+/// Returns true if the given operation is either annotated with the
+/// `ReturnLike` trait or implements the `RegionBranchTerminatorOpInterface`.
+bool isRegionReturnLike(Operation *operation);
+
+/// Returns the mutable operands that are passed to the region with the given
+/// `regionIndex`. If the operation does not implement the
+/// `RegionBranchTerminatorOpInterface` and is not marked as `ReturnLike`, the
+/// result will be `llvm::None`. In all other cases, the resulting
+/// `OperandRange` represents all operands that are passed to the specified
+/// successor region. If `regionIndex` is `llvm::None`, all operands that are
+/// passed to the parent operation will be returned.
+Optional<MutableOperandRange>
+getMutableRegionBranchSuccessorOperands(Operation *operation,
+                                        Optional<unsigned> regionIndex);
+
+/// Returns the read only operands that are passed to the region with the given
+/// `regionIndex`. See `getMutableRegionBranchSuccessorOperands` for more
+/// information.
+Optional<OperandRange>
+getRegionBranchSuccessorOperands(Operation *operation,
+                                 Optional<unsigned> regionIndex);
+
 //===----------------------------------------------------------------------===//
 // ControlFlow Traits
 //===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Interfaces/ControlFlowInterfaces.td b/mlir/include/mlir/Interfaces/ControlFlowInterfaces.td
--- a/mlir/include/mlir/Interfaces/ControlFlowInterfaces.td
+++ b/mlir/include/mlir/Interfaces/ControlFlowInterfaces.td
@@ -34,7 +34,8 @@
         successor are non-materialized values, i.e. they are internal to the
         operation.
       }],
-      "Optional<MutableOperandRange>", "getMutableSuccessorOperands",
+      "::llvm::Optional<::mlir::MutableOperandRange>",
+      "getMutableSuccessorOperands",
       (ins "unsigned":$index)
     >,
     InterfaceMethod<[{
@@ -43,11 +44,12 @@
         successor are non-materialized values, i.e. they are internal to the
         operation.
       }],
-      "Optional<OperandRange>", "getSuccessorOperands",
+      "::llvm::Optional<::mlir::OperandRange>", "getSuccessorOperands",
       (ins "unsigned":$index), [{}], [{
         ConcreteOp *op = static_cast<ConcreteOp *>(this);
         auto operands = op->getMutableSuccessorOperands(index);
-        return operands ? Optional<OperandRange>(*operands) : llvm::None;
+        return operands ? ::llvm::Optional<::mlir::OperandRange>(*operands)
+                        : ::llvm::None;
       }]
     >,
     InterfaceMethod<[{
@@ -55,24 +57,25 @@
         some successor, or None if `operandIndex` isn't a successor operand
         index.
       }],
-      "Optional<BlockArgument>", "getSuccessorBlockArgument",
+      "::llvm::Optional<::mlir::BlockArgument>", "getSuccessorBlockArgument",
       (ins "unsigned":$operandIndex), [{
-        Operation *opaqueOp = $_op;
+        ::mlir::Operation *opaqueOp = $_op;
         for (unsigned i = 0, e = opaqueOp->getNumSuccessors(); i != e; ++i) {
-          if (Optional<BlockArgument> arg = detail::getBranchSuccessorArgument(
-                $_op.getSuccessorOperands(i), operandIndex,
-                opaqueOp->getSuccessor(i)))
+          if (::llvm::Optional<::mlir::BlockArgument> arg =
+              detail::getBranchSuccessorArgument(
+                  $_op.getSuccessorOperands(i), operandIndex,
+                  opaqueOp->getSuccessor(i)))
             return arg;
         }
-        return llvm::None;
+        return ::llvm::None;
       }]
     >,
     InterfaceMethod<[{
         Returns the successor that would be chosen with the given constant
         operands. Returns nullptr if a single successor could not be chosen.
       }],
-      "Block *", "getSuccessorForOperands",
-      (ins "ArrayRef<Attribute>":$operands), [{}],
+      "::mlir::Block *", "getSuccessorForOperands",
+      (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands), [{}],
       /*defaultImplementation=*/[{ return nullptr; }]
     >
   ];
@@ -80,11 +83,13 @@
   let verify = [{
     auto concreteOp = cast<ConcreteOp>($_op);
     for (unsigned i = 0, e = $_op->getNumSuccessors(); i != e; ++i) {
-      Optional<OperandRange> operands = concreteOp.getSuccessorOperands(i);
-      if (failed(detail::verifyBranchSuccessorOperands($_op, i, operands)))
-        return failure();
+      ::llvm::Optional<::mlir::OperandRange> operands =
+          concreteOp.getSuccessorOperands(i);
+      if (failed(
+          ::mlir::detail::verifyBranchSuccessorOperands($_op, i, operands)))
+        return ::mlir::failure();
     }
-    return success();
+    return ::mlir::success();
   }];
 }
 
@@ -107,10 +112,10 @@
         operation by `getSuccessorRegions`. These operands should correspond 1-1
         with the successor inputs specified in `getSuccessorRegions`.
       }],
-      "OperandRange", "getSuccessorEntryOperands",
+      "::mlir::OperandRange", "getSuccessorEntryOperands",
       (ins "unsigned":$index), [{}], /*defaultImplementation=*/[{
         auto operandEnd = this->getOperation()->operand_end();
-        return OperandRange(operandEnd, operandEnd);
+        return ::mlir::OperandRange(operandEnd, operandEnd);
       }]
     >,
     InterfaceMethod<[{
@@ -128,8 +133,9 @@
         successor region must be non-empty.
       }],
       "void", "getSuccessorRegions",
-      (ins "Optional<unsigned>":$index, "ArrayRef<Attribute>":$operands,
-           "SmallVectorImpl<RegionSuccessor> &":$regions)
+      (ins "::llvm::Optional<unsigned>":$index,
+           "::llvm::ArrayRef<::mlir::Attribute>":$operands,
+           "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions)
     >,
     InterfaceMethod<[{
         Populates countPerRegion with the number of times this operation will
@@ -143,37 +149,92 @@
         operand is not a constant.
       }],
       "void", "getNumRegionInvocations",
-      (ins "ArrayRef<Attribute>":$operands,
-           "SmallVectorImpl<int64_t> &":$countPerRegion), [{}],
+      (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands,
+           "::llvm::SmallVectorImpl<int64_t> &":$countPerRegion), [{}],
       /*defaultImplementation=*/[{
         unsigned numRegions = this->getOperation()->getNumRegions();
         assert(countPerRegion.empty());
-        countPerRegion.resize(numRegions, kUnknownNumRegionInvocations);
+        countPerRegion.resize(numRegions,
+                              ::mlir::kUnknownNumRegionInvocations);
       }]
     >
   ];
 
   let verify = [{
-    static_assert(!ConcreteOp::template hasTrait<OpTrait::ZeroRegion>(),
-                  "expected operation to have non-zero regions");
+    static_assert(
+        !ConcreteOp::template hasTrait<::mlir::OpTrait::ZeroRegion>(),
+        "expected operation to have non-zero regions");
     return success();
   }];
 
   let extraClassDeclaration = [{
     /// Convenience helper in case none of the operands is known.
-    void getSuccessorRegions(Optional<unsigned> index,
-                             SmallVectorImpl<RegionSuccessor> &regions) {
-       SmallVector<Attribute, 2> nullAttrs(getOperation()->getNumOperands());
-       getSuccessorRegions(index, nullAttrs, regions);
+    void getSuccessorRegions(
+        Optional<unsigned> index,
+        ::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &regions) {
+      ::llvm::SmallVector<Attribute, 2> nullAttrs(
+          getOperation()->getNumOperands());
+      getSuccessorRegions(index, nullAttrs, regions);
     }
 
     /// Verify types along control flow edges described by this interface.
-    static LogicalResult verifyTypes(Operation *op) {
-      return detail::verifyTypesAlongControlFlowEdges(op);
+    static ::mlir::LogicalResult verifyTypes(::mlir::Operation *op) {
+      return ::mlir::detail::verifyTypesAlongControlFlowEdges(op);
     }
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// RegionBranchTerminatorOpInterface
+//===----------------------------------------------------------------------===//
+
+def RegionBranchTerminatorOpInterface :
+  OpInterface<"RegionBranchTerminatorOpInterface"> {
+  let description = [{
+    This interface provides information for branching terminator operations
+    in the presence of a parent RegionBranchOpInterface implementation. It
+    specifies which operands are passed to which successor region.
+  }];
+  let cppNamespace = "::mlir";
+
+  let methods = [
+    InterfaceMethod<[{
+        Returns a mutable range of operands that are semantically "returned" by
+        passing them to the region successor given by `index`.  If `index` is
+        None, this function returns the operands that are passed as a result to
+        the parent operation.
+      }],
+      "::mlir::MutableOperandRange", "getMutableSuccessorOperands",
+      (ins "::llvm::Optional<unsigned>":$index)
+    >,
+    InterfaceMethod<[{
+        Returns a range of operands that are semantically "returned" by passing
+        them to the region successor given by `index`.  If `index` is None, this
+        function returns the operands that are passed as a result to the parent
+        operation.
+      }],
+      "::mlir::OperandRange", "getSuccessorOperands",
+      (ins "::llvm::Optional<unsigned>":$index), [{}],
+      /*defaultImplementation=*/[{
+        ConcreteOp *op = static_cast<ConcreteOp *>(this);
+        return op->getMutableSuccessorOperands(index);
+      }]
+    >
+  ];
+
+  let verify = [{
+    static_assert(
+        ConcreteOp::template hasTrait<::mlir::OpTrait::IsTerminator>(),
+        "expected operation to be a terminator");
+    static_assert(ConcreteOp::template hasTrait<::mlir::OpTrait::ZeroResult>(),
+                  "expected operation to have zero results");
+    static_assert(
+        ConcreteOp::template hasTrait<::mlir::OpTrait::ZeroSuccessor>(),
+        "expected operation to have zero successors");
+    return ::mlir::success();
+  }];
+}
+
 //===----------------------------------------------------------------------===//
 // ControlFlow Traits
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp b/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
--- a/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
+++ b/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
@@ -74,12 +74,14 @@
   };
 
   // Check branches from the parent operation.
+  Optional<unsigned> regionIndex;
   if (region) {
+    // Determine the actual region number from the passed region.
+    regionIndex = region->getRegionNumber();
     if (Optional<unsigned> operandIndex =
             getOperandIndexIfPred(/*predIndex=*/llvm::None)) {
       collectUnderlyingAddressValues(
-          branch.getSuccessorEntryOperands(
-              region->getRegionNumber())[*operandIndex],
+          branch.getSuccessorEntryOperands(*regionIndex)[*operandIndex],
           maxDepth, visited, output);
     }
   }
@@ -89,9 +91,14 @@
     if (Optional<unsigned> operandIndex = getOperandIndexIfPred(i)) {
       for (Block &block : op->getRegion(i)) {
         Operation *term = block.getTerminator();
-        if (term->hasTrait<OpTrait::ReturnLike>()) {
-          collectUnderlyingAddressValues(term->getOperand(*operandIndex),
-                                         maxDepth, visited, output);
+        // Try to determine possible region-branch successor operands for the
+        // current region.
+        auto successorOperands =
+            getRegionBranchSuccessorOperands(term, regionIndex);
+        if (successorOperands.hasValue()) {
+          collectUnderlyingAddressValues(
+              successorOperands.getValue()[*operandIndex], maxDepth, visited,
+              output);
         } else if (term->getNumSuccessors()) {
           // Otherwise, if this terminator may exit the region we can't make
           // any assumptions about which values get passed.
diff --git a/mlir/lib/Analysis/BufferViewFlowAnalysis.cpp b/mlir/lib/Analysis/BufferViewFlowAnalysis.cpp
--- a/mlir/lib/Analysis/BufferViewFlowAnalysis.cpp
+++ b/mlir/lib/Analysis/BufferViewFlowAnalysis.cpp
@@ -101,12 +101,20 @@
       regionInterface.getSuccessorRegions(region.getRegionNumber(),
                                           successorRegions);
       for (RegionSuccessor &successorRegion : successorRegions) {
+        // Determine the current region index (if any).
+        Optional<unsigned> regionIndex;
+        if (successorRegion.getSuccessor())
+          regionIndex = successorRegion.getSuccessor()->getRegionNumber();
         // Iterate over all immediate terminator operations and wire the
         // successor inputs with the operands of each terminator.
         for (Block &block : region) {
           for (Operation &operation : block) {
-            if (operation.hasTrait<OpTrait::ReturnLike>())
-              registerDependencies(operation.getOperands(),
+            // Try to get all region branch successor operands and wire them
+            // with the successor inputs.
+            auto successorOperands =
+                getRegionBranchSuccessorOperands(&operation, regionIndex);
+            if (successorOperands.hasValue())
+              registerDependencies(successorOperands.getValue(),
                                    successorRegion.getSuccessorInputs());
           }
         }
diff --git a/mlir/lib/Analysis/DataFlowAnalysis.cpp b/mlir/lib/Analysis/DataFlowAnalysis.cpp
--- a/mlir/lib/Analysis/DataFlowAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlowAnalysis.cpp
@@ -562,26 +562,28 @@
     if (regionSuccessors.empty())
       return;
 
+    // Try to get "region-like" successor operands if possible in order to
+    // propagate the operand states to the successors.
+    if (isRegionReturnLike(op))
+      return visitRegionSuccessors(
+          parentOp, regionSuccessors, [&](Optional<unsigned> regionIndex) {
+            // Determine the individual region successor operands for the given
+            // region index (if any).
+            return *getRegionBranchSuccessorOperands(op, regionIndex);
+          });
+
     // If this terminator is not "region-like", conservatively mark all of the
     // successor values as having reached the pessimistic fixpoint.
-    if (!op->hasTrait<OpTrait::ReturnLike>()) {
-      for (auto &it : regionSuccessors) {
-        // If the successor is a region, mark the entry block as executable so
-        // that we visit operations defined within. If the successor is the
-        // parent operation, we simply mark the control flow results as having
-        // reached the pessimistic state.
-        if (Region *region = it.getSuccessor())
-          markEntryBlockExecutable(region, /*markPessimisticFixpoint=*/true);
-        else
-          markAllPessimisticFixpointAndVisitUsers(it.getSuccessorInputs());
-      }
-      return;
+    for (auto &it : regionSuccessors) {
+      // If the successor is a region, mark the entry block as executable so
+      // that we visit operations defined within. If the successor is the
+      // parent operation, we simply mark the control flow results as having
+      // reached the pessimistic state.
+      if (Region *region = it.getSuccessor())
+        markEntryBlockExecutable(region, /*markPessimisticFixpoint=*/true);
+      else
+        markAllPessimisticFixpointAndVisitUsers(it.getSuccessorInputs());
     }
-
-    // Otherwise, propagate the operand lattice states to the successors.
-    OperandRange operands = op->getOperands();
-    return visitRegionSuccessors(parentOp, regionSuccessors,
-                                 [&](Optional<unsigned>) { return operands; });
   }
 
   // Try to resolve to a specific set of successors with the current optimistic
diff --git a/mlir/lib/Dialect/SCF/SCF.cpp b/mlir/lib/Dialect/SCF/SCF.cpp
--- a/mlir/lib/Dialect/SCF/SCF.cpp
+++ b/mlir/lib/Dialect/SCF/SCF.cpp
@@ -162,6 +162,16 @@
   results.add<SingleBlockExecuteInliner>(context);
 }
 
+//===----------------------------------------------------------------------===//
+// ConditionOp
+//===----------------------------------------------------------------------===//
+
+MutableOperandRange
+ConditionOp::getMutableSuccessorOperands(Optional<unsigned> index) {
+  // Pass all operands except the condition to the successor region.
+  return argsMutable();
+}
+
 //===----------------------------------------------------------------------===//
 // ForOp
 //===----------------------------------------------------------------------===//
@@ -2100,18 +2110,6 @@
   if (!beforeTerminator)
     return failure();
 
-  TypeRange trailingTerminatorOperands = beforeTerminator.args().getTypes();
-  if (failed(verifyTypeRangesMatch(op, trailingTerminatorOperands,
-                                   op.after().getArgumentTypes(),
-                                   "trailing operands of the 'before' block "
-                                   "terminator and 'after' region arguments")))
-    return failure();
-
-  if (failed(verifyTypeRangesMatch(
-          op, trailingTerminatorOperands, op.getResultTypes(),
-          "trailing operands of the 'before' block terminator and op results")))
-    return failure();
-
   auto afterTerminator = verifyAndGetTerminator<scf::YieldOp>(
       op, op.after(),
       "expects the 'after' region to terminate with 'scf.yield'");
diff --git a/mlir/lib/Interfaces/ControlFlowInterfaces.cpp b/mlir/lib/Interfaces/ControlFlowInterfaces.cpp
--- a/mlir/lib/Interfaces/ControlFlowInterfaces.cpp
+++ b/mlir/lib/Interfaces/ControlFlowInterfaces.cpp
@@ -176,25 +176,27 @@
   for (unsigned regionNo : llvm::seq(0U, op->getNumRegions())) {
     Region &region = op->getRegion(regionNo);
 
-    // Since the interface cannot distinguish between different ReturnLike
-    // ops within the region branching to different successors, all ReturnLike
-    // ops in this region should have the same operand types. We will then use
-    // one of them as the representative for type matching.
+    // Since there can be multiple `ReturnLike` terminators or others
+    // implementing the `RegionBranchTerminatorOpInterface`, all should have the
+    // same operand types when passing them to the same region.
 
-    Operation *regionReturn = nullptr;
+    Optional<OperandRange> regionReturnOperands;
     for (Block &block : region) {
       Operation *terminator = block.getTerminator();
-      if (!terminator->hasTrait<OpTrait::ReturnLike>())
+      auto terminatorOperands =
+          getRegionBranchSuccessorOperands(terminator, regionNo);
+      if (!terminatorOperands)
         continue;
 
-      if (!regionReturn) {
-        regionReturn = terminator;
+      if (!regionReturnOperands) {
+        regionReturnOperands = terminatorOperands;
         continue;
       }
 
       // Found more than one ReturnLike terminator. Make sure the operand types
       // match with the first one.
-      if (regionReturn->getOperandTypes() != terminator->getOperandTypes())
+      if (regionReturnOperands.getValue().getTypes() !=
+          terminatorOperands.getValue().getTypes())
         return op->emitOpError("Region #")
                << regionNo
                << " operands mismatch between return-like terminators";
@@ -204,11 +206,11 @@
         [&](Optional<unsigned> regionNo) -> Optional<TypeRange> {
       // If there is no return-like terminator, the op itself should verify
       // type consistency.
-      if (!regionReturn)
+      if (!regionReturnOperands)
         return llvm::None;
 
-      // All successors get the same set of operands.
-      return TypeRange(regionReturn->getOperands().getTypes());
+      // All successors get the same set of operand types.
+      return TypeRange(regionReturnOperands.getValue().getTypes());
     };
 
     if (failed(verifyTypesAlongAllEdges(op, regionNo, inputTypesFromRegion)))
@@ -217,3 +219,45 @@
 
   return success();
 }
+
+//===----------------------------------------------------------------------===//
+// RegionBranchTerminatorOpInterface
+//===----------------------------------------------------------------------===//
+
+/// Returns true if the given operation is either annotated with the
+/// `ReturnLike` trait or implements the `RegionBranchTerminatorOpInterface`.
+bool mlir::isRegionReturnLike(Operation *operation) {
+  return dyn_cast<RegionBranchTerminatorOpInterface>(operation) ||
+         (operation && operation->hasTrait<OpTrait::ReturnLike>());
+}
+
+/// Returns the mutable operands that are passed to the region with the given
+/// `regionIndex`. If the operation does not implement the
+/// `RegionBranchTerminatorOpInterface` and is not marked as `ReturnLike`, the
+/// result will be `llvm::None`. In all other cases, the resulting
+/// `OperandRange` represents all operands that are passed to the specified
+/// successor region. If `regionIndex` is `llvm::None`, all operands that are
+/// passed to the parent operation will be returned.
+Optional<MutableOperandRange>
+mlir::getMutableRegionBranchSuccessorOperands(Operation *operation,
+                                              Optional<unsigned> regionIndex) {
+  // Try to query a RegionBranchTerminatorOpInterface to determine
+  // all successor operands that will be passed to the successor
+  // input arguments.
+  if (auto regionTerminatorInterface =
+          dyn_cast<RegionBranchTerminatorOpInterface>(operation))
+    return regionTerminatorInterface.getMutableSuccessorOperands(regionIndex);
+  if (operation->hasTrait<OpTrait::ReturnLike>())
+    return MutableOperandRange(operation);
+  return llvm::None;
+}
+
+/// Returns the read only operands that are passed to the region with the given
+/// `regionIndex`. See `getMutableRegionBranchSuccessorOperands` for more
+/// information.
+Optional<OperandRange>
+mlir::getRegionBranchSuccessorOperands(Operation *operation,
+                                       Optional<unsigned> regionIndex) {
+  auto range = getMutableRegionBranchSuccessorOperands(operation, regionIndex);
+  return range ? Optional<OperandRange>(*range) : llvm::None;
+}
diff --git a/mlir/lib/Transforms/BufferDeallocation.cpp b/mlir/lib/Transforms/BufferDeallocation.cpp
--- a/mlir/lib/Transforms/BufferDeallocation.cpp
+++ b/mlir/lib/Transforms/BufferDeallocation.cpp
@@ -68,8 +68,8 @@
 static void walkReturnOperations(Region *region, const FuncT &func) {
   for (Block &block : *region)
     for (Operation &operation : block) {
-      // Skip non-return-like terminators.
-      if (operation.hasTrait<OpTrait::ReturnLike>())
+      // Skip non region-return-like terminators.
+      if (isRegionReturnLike(&operation))
         func(&operation);
     }
 }
@@ -390,12 +390,15 @@
       // new buffer allocations. Thereby, the appropriate terminator operand
       // will be adjusted to point to the newly allocated buffer instead.
       walkReturnOperations(&region, [&](Operation *terminator) {
+        // Get the actual mutable operands for this terminator op.
+        auto terminatorOperands = *getMutableRegionBranchSuccessorOperands(
+            terminator, region.getRegionNumber());
         // Extract the source value from the current terminator.
-        Value sourceValue = terminator->getOperand(operandIndex);
+        Value sourceValue = ((OperandRange)terminatorOperands)[operandIndex];
         // Create a new clone at the current location of the terminator.
         Value clone = introduceCloneBuffers(sourceValue, terminator);
         // Wire clone and terminator operand.
-        terminator->setOperand(operandIndex, clone);
+        terminatorOperands.slice(operandIndex, 1).assign(clone);
       });
     }
   }
diff --git a/mlir/lib/Transforms/BufferOptimizations.cpp b/mlir/lib/Transforms/BufferOptimizations.cpp
--- a/mlir/lib/Transforms/BufferOptimizations.cpp
+++ b/mlir/lib/Transforms/BufferOptimizations.cpp
@@ -64,8 +64,7 @@
       // If there is at least one alias that leaves the parent region, we know
       // that this alias escapes the whole region and hence the associated
       // allocation leaves allocation scope.
-      if (use->hasTrait<OpTrait::ReturnLike>() &&
-          use->getParentRegion() == parentRegion)
+      if (isRegionReturnLike(use) && use->getParentRegion() == parentRegion)
         return true;
     }
   }
diff --git a/mlir/test/Dialect/SCF/invalid.mlir b/mlir/test/Dialect/SCF/invalid.mlir
--- a/mlir/test/Dialect/SCF/invalid.mlir
+++ b/mlir/test/Dialect/SCF/invalid.mlir
@@ -462,7 +462,7 @@
 
 func @while_cross_region_type_mismatch() {
   %true = constant true
-  // expected-error@+1 {{expects the same number of trailing operands of the 'before' block terminator and 'after' region arguments}}
+  // expected-error@+1 {{'scf.while' op  region control flow edge from Region #0 to Region #1: source has 0 operands, but target successor needs 1}}
   scf.while : () -> () {
     scf.condition(%true)
   } do {
@@ -475,8 +475,7 @@
 
 func @while_cross_region_type_mismatch() {
   %true = constant true
-  // expected-error@+2 {{expects the same types for trailing operands of the 'before' block terminator and 'after' region arguments}}
-  // expected-note@+1 {{for argument 0, found 'i1' and 'i32}}
+  // expected-error@+1 {{'scf.while' op  along control flow edge from Region #0 to Region #1: source type #0 'i1' should match input type #0 'i32'}}
   scf.while : () -> () {
     scf.condition(%true) %true : i1
   } do {
@@ -489,7 +488,7 @@
 
 func @while_result_type_mismatch() {
   %true = constant true
-  // expected-error@+1 {{expects the same number of trailing operands of the 'before' block terminator and op results}}
+  // expected-error@+1 {{'scf.while' op  region control flow edge from Region #0 to parent results: source has 1 operands, but target successor needs 0}}
   scf.while : () -> () {
     scf.condition(%true) %true : i1
   } do {
diff --git a/mlir/test/Transforms/sccp-structured.mlir b/mlir/test/Transforms/sccp-structured.mlir
--- a/mlir/test/Transforms/sccp-structured.mlir
+++ b/mlir/test/Transforms/sccp-structured.mlir
@@ -131,14 +131,13 @@
   return %result : i32
 }
 
-/// Test that we can properly visit region successors when the terminator is not
-/// return-like.
-
-// CHECK-LABEL: func @overdefined_non_returnlike(
-func @overdefined_non_returnlike(%arg1 : i32) {
-  // CHECK: scf.while (%[[ARG:.*]] = %[[INPUT:.*]])
-  // CHECK-NEXT: %[[COND:.*]] = cmpi slt, %[[ARG]], %{{.*}} : i32
-  // CHECK-NEXT: scf.condition(%[[COND]]) %[[ARG]] : i32
+/// Test that we can properly visit region successors when the terminator
+/// implements the RegionBranchTerminatorOpInterface.
+
+// CHECK-LABEL: func @loop_region_branch_terminator_op(
+func @loop_region_branch_terminator_op(%arg1 : i32) {
+  // CHECK:      %c2_i32 = constant 2 : i32
+  // CHECK-NEXT: return
 
   %c2_i32 = constant 2 : i32
    %0 = scf.while (%arg2 = %c2_i32) : (i32) -> (i32) {