Generalize traversal continuation to have a break value.

Change-Id: I3a5d8abd255147a6c68d8eb2c13f849687e17015
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
index ed6767a..977a836 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
@@ -30,28 +30,28 @@
 
   public abstract int getMemberCount();
 
-  public TraversalContinuation visitFields(
-      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
+  public TraversalContinuation<?> visitFields(
+      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation<?>> visitor) {
     return visitFields(visitor, classReference, 1);
   }
 
-  public TraversalContinuation visitMethods(
-      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
+  public TraversalContinuation<?> visitMethods(
+      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation<?>> visitor) {
     return visitMethods(visitor, classReference, 1);
   }
 
-  protected abstract TraversalContinuation visitFields(
-      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+  protected abstract TraversalContinuation<?> visitFields(
+      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation<?>> visitor,
       ClassReference holder,
       int minApiClass);
 
-  protected abstract TraversalContinuation visitMethods(
-      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+  protected abstract TraversalContinuation<?> visitMethods(
+      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation<?>> visitor,
       ClassReference holder,
       int minApiClass);
 
-  protected TraversalContinuation visitField(
-      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+  protected TraversalContinuation<?> visitField(
+      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation<?>> visitor,
       ClassReference holder,
       int minApiClass,
       int minApiField,
@@ -62,8 +62,8 @@
         getAndroidApiLevel(Integer.max(minApiClass, minApiField)));
   }
 
-  protected TraversalContinuation visitMethod(
-      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+  protected TraversalContinuation<?> visitMethod(
+      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation<?>> visitor,
       ClassReference holder,
       int minApiClass,
       int minApiMethod,
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index f32be7e..4ecd71c 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -4,8 +4,8 @@
 
 package com.android.tools.r8.graph;
 
-import static com.android.tools.r8.utils.TraversalContinuation.BREAK;
-import static com.android.tools.r8.utils.TraversalContinuation.CONTINUE;
+import static com.android.tools.r8.utils.TraversalContinuation.doBreak;
+import static com.android.tools.r8.utils.TraversalContinuation.doContinue;
 
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.MethodResolutionResult.ArrayCloneMethodResult;
@@ -21,7 +21,6 @@
 import com.android.tools.r8.shaking.MissingClasses;
 import com.android.tools.r8.synthesis.CommittedItems;
 import com.android.tools.r8.synthesis.SyntheticItems;
-import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.TraversalContinuation;
@@ -159,19 +158,19 @@
   }
 
   /** Primitive traversal over all (non-interface) superclasses of a given type. */
-  public TraversalContinuation traverseSuperClasses(
-      DexClass clazz, TriFunction<DexType, DexClass, DexClass, TraversalContinuation> fn) {
+  public <T> TraversalContinuation<T> traverseSuperClasses(
+      DexClass clazz, TriFunction<DexType, DexClass, DexClass, TraversalContinuation<T>> fn) {
     DexClass currentClass = clazz;
     while (currentClass != null && currentClass.getSuperType() != null) {
       DexClass superclass = definitionFor(currentClass.getSuperType());
-      TraversalContinuation stepResult =
+      TraversalContinuation<T> stepResult =
           fn.apply(currentClass.getSuperType(), superclass, currentClass);
       if (stepResult.shouldBreak()) {
         return stepResult;
       }
       currentClass = superclass;
     }
-    return CONTINUE;
+    return doContinue();
   }
 
   /**
@@ -181,8 +180,8 @@
    * given type is *not* visited. The function indicates if traversal should continue or break. The
    * result of the traversal is BREAK iff the function returned BREAK.
    */
-  public TraversalContinuation traverseSuperTypes(
-      final DexClass clazz, TriFunction<DexType, DexClass, Boolean, TraversalContinuation> fn) {
+  public TraversalContinuation<?> traverseSuperTypes(
+      final DexClass clazz, TriFunction<DexType, DexClass, Boolean, TraversalContinuation<?>> fn) {
     // We do an initial zero-allocation pass over the class super chain as it does not require a
     // worklist/seen-set. Only if the traversal is not aborted and there actually are interfaces,
     // do we continue traversal over the interface types. This is assuming that the second pass
@@ -195,7 +194,7 @@
         if (currentClass.superType == null) {
           break;
         }
-        TraversalContinuation stepResult = fn.apply(currentClass.superType, currentClass, false);
+        TraversalContinuation<?> stepResult = fn.apply(currentClass.superType, currentClass, false);
         if (stepResult.shouldBreak()) {
           return stepResult;
         }
@@ -203,7 +202,7 @@
       }
     }
     if (interfaceCount == 0) {
-      return CONTINUE;
+      return doContinue();
     }
     // Interfaces exist, create a worklist and seen set to ensure single visits.
     Set<DexType> seen = Sets.newIdentityHashSet();
@@ -214,7 +213,7 @@
       while (currentClass != null) {
         for (DexType iface : currentClass.interfaces.values) {
           if (seen.add(iface)) {
-            TraversalContinuation stepResult = fn.apply(iface, currentClass, true);
+            TraversalContinuation<?> stepResult = fn.apply(iface, currentClass, true);
             if (stepResult.shouldBreak()) {
               return stepResult;
             }
@@ -234,7 +233,7 @@
       if (definition != null) {
         for (DexType iface : definition.interfaces.values) {
           if (seen.add(iface)) {
-            TraversalContinuation stepResult = fn.apply(iface, definition, true);
+            TraversalContinuation<?> stepResult = fn.apply(iface, definition, true);
             if (stepResult.shouldBreak()) {
               return stepResult;
             }
@@ -243,7 +242,7 @@
         }
       }
     }
-    return CONTINUE;
+    return doContinue();
   }
 
   /**
@@ -256,7 +255,7 @@
         clazz,
         (superType, subclass, isInterface) -> {
           fn.accept(superType, subclass, isInterface);
-          return CONTINUE;
+          return doContinue();
         });
   }
 
@@ -292,7 +291,8 @@
     }
     // TODO(b/123506120): Report missing types when the predicate is inconclusive.
     return traverseSuperTypes(
-            clazz, (superType, subclass, isInterface) -> superType == supertype ? BREAK : CONTINUE)
+            clazz,
+            (superType, subclass, isInterface) -> superType == supertype ? doBreak() : doContinue())
         .shouldBreak();
   }
 
@@ -327,23 +327,22 @@
     if (superclass.getType() == dexItemFactory().objectType) {
       return true;
     }
-    BooleanBox result = new BooleanBox();
-    traverseSuperClasses(
-        subclass,
-        (currentType, currentClass, immediateSubclass) -> {
-          if (currentType == superclass.getType()) {
-            result.set();
-            return BREAK;
-          }
-          if (currentClass == null) {
-            return BREAK;
-          }
-          if (superclass.isProgramClass() && !currentClass.isProgramClass()) {
-            return BREAK;
-          }
-          return CONTINUE;
-        });
-    return result.isTrue();
+    TraversalContinuation<Boolean> result =
+        traverseSuperClasses(
+            subclass,
+            (currentType, currentClass, immediateSubclass) -> {
+              if (currentType == superclass.getType()) {
+                return doBreak(true);
+              }
+              if (currentClass == null) {
+                return doBreak(false);
+              }
+              if (superclass.isProgramClass() && !currentClass.isProgramClass()) {
+                return doBreak(false);
+              }
+              return doContinue();
+            });
+    return result.isBreak() && result.asBreak().getValue();
   }
 
   public boolean inSameHierarchy(DexType type, DexType other) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 31e7030..a0535bc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -356,13 +356,14 @@
     staticFields(predicate).forEach(consumer);
   }
 
-  public TraversalContinuation traverseFields(Function<DexEncodedField, TraversalContinuation> fn) {
+  public TraversalContinuation<?> traverseFields(
+      Function<DexEncodedField, TraversalContinuation<?>> fn) {
     for (DexEncodedField field : fields()) {
       if (fn.apply(field).shouldBreak()) {
-        return TraversalContinuation.BREAK;
+        return TraversalContinuation.doBreak();
       }
     }
-    return TraversalContinuation.CONTINUE;
+    return TraversalContinuation.doContinue();
   }
 
   public List<DexEncodedField> staticFields() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 3077717..e0d4545 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -327,38 +327,38 @@
     return null;
   }
 
-  public TraversalContinuation traverseProgramMembers(
-      Function<ProgramMember<?, ?>, TraversalContinuation> fn) {
-    TraversalContinuation continuation = traverseProgramFields(fn);
+  public TraversalContinuation<?> traverseProgramMembers(
+      Function<ProgramMember<?, ?>, TraversalContinuation<?>> fn) {
+    TraversalContinuation<?> continuation = traverseProgramFields(fn);
     if (continuation.shouldContinue()) {
       return traverseProgramMethods(fn);
     }
-    return TraversalContinuation.BREAK;
+    return TraversalContinuation.doBreak();
   }
 
-  public TraversalContinuation traverseProgramFields(
-      Function<? super ProgramField, TraversalContinuation> fn) {
+  public TraversalContinuation<?> traverseProgramFields(
+      Function<? super ProgramField, TraversalContinuation<?>> fn) {
     return traverseFields(field -> fn.apply(new ProgramField(this, field)));
   }
 
-  public TraversalContinuation traverseProgramMethods(
-      Function<? super ProgramMethod, TraversalContinuation> fn) {
+  public TraversalContinuation<?> traverseProgramMethods(
+      Function<? super ProgramMethod, TraversalContinuation<?>> fn) {
     return getMethodCollection().traverse(method -> fn.apply(new ProgramMethod(this, method)));
   }
 
-  public TraversalContinuation traverseProgramInstanceInitializers(
-      Function<ProgramMethod, TraversalContinuation> fn) {
+  public TraversalContinuation<?> traverseProgramInstanceInitializers(
+      Function<ProgramMethod, TraversalContinuation<?>> fn) {
     return traverseProgramMethods(fn, DexEncodedMethod::isInstanceInitializer);
   }
 
-  public TraversalContinuation traverseProgramMethods(
-      Function<ProgramMethod, TraversalContinuation> fn, Predicate<DexEncodedMethod> predicate) {
+  public TraversalContinuation<?> traverseProgramMethods(
+      Function<ProgramMethod, TraversalContinuation<?>> fn, Predicate<DexEncodedMethod> predicate) {
     return getMethodCollection()
         .traverse(
             method ->
                 predicate.test(method)
                     ? fn.apply(new ProgramMethod(this, method))
-                    : TraversalContinuation.CONTINUE);
+                    : TraversalContinuation.doContinue());
   }
 
   public Kind getOriginKind() {
diff --git a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
index 545645e..d848b49 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
@@ -56,20 +56,20 @@
   }
 
   @Override
-  TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn) {
+  TraversalContinuation<?> traverse(Function<DexEncodedMethod, TraversalContinuation<?>> fn) {
     for (DexEncodedMethod method : directMethods) {
-      TraversalContinuation stepResult = fn.apply(method);
+      TraversalContinuation<?> stepResult = fn.apply(method);
       if (stepResult.shouldBreak()) {
         return stepResult;
       }
     }
     for (DexEncodedMethod method : virtualMethods) {
-      TraversalContinuation stepResult = fn.apply(method);
+      TraversalContinuation<?> stepResult = fn.apply(method);
       if (stepResult.shouldBreak()) {
         return stepResult;
       }
     }
-    return TraversalContinuation.CONTINUE;
+    return TraversalContinuation.doContinue();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index 4a403c4..45b0902 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -90,7 +90,8 @@
     return backing.size();
   }
 
-  public TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn) {
+  public TraversalContinuation<?> traverse(
+      Function<DexEncodedMethod, TraversalContinuation<?>> fn) {
     return backing.traverse(fn);
   }
 
@@ -340,8 +341,8 @@
     return traverse(
             method ->
                 method.hasAnyAnnotations()
-                    ? TraversalContinuation.BREAK
-                    : TraversalContinuation.CONTINUE)
+                    ? TraversalContinuation.doBreak()
+                    : TraversalContinuation.doContinue())
         .shouldBreak();
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
index 90fbf1f..0d91ae4 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
@@ -36,7 +36,8 @@
 
   // Traversal methods.
 
-  abstract TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn);
+  abstract TraversalContinuation<?> traverse(
+      Function<DexEncodedMethod, TraversalContinuation<?>> fn);
 
   void forEachMethod(Consumer<DexEncodedMethod> fn) {
     forEachMethod(fn, alwaysTrue());
@@ -48,7 +49,7 @@
           if (predicate.test(method)) {
             fn.accept(method);
           }
-          return TraversalContinuation.CONTINUE;
+          return TraversalContinuation.doContinue();
         });
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollectionConcurrencyChecked.java b/src/main/java/com/android/tools/r8/graph/MethodCollectionConcurrencyChecked.java
index 66be012..f7717d0 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollectionConcurrencyChecked.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollectionConcurrencyChecked.java
@@ -85,9 +85,10 @@
   }
 
   @Override
-  public TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn) {
+  public TraversalContinuation<?> traverse(
+      Function<DexEncodedMethod, TraversalContinuation<?>> fn) {
     assert assertReadEntry();
-    TraversalContinuation result = super.traverse(fn);
+    TraversalContinuation<?> result = super.traverse(fn);
     assert assertReadExit();
     return result;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
index d42f6eb..1437ac1 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
@@ -88,14 +88,14 @@
   }
 
   @Override
-  TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn) {
+  TraversalContinuation<?> traverse(Function<DexEncodedMethod, TraversalContinuation<?>> fn) {
     for (Entry<DexMethodSignature, DexEncodedMethod> entry : methodMap.entrySet()) {
-      TraversalContinuation result = fn.apply(entry.getValue());
+      TraversalContinuation<?> result = fn.apply(entry.getValue());
       if (result.shouldBreak()) {
         return result;
       }
     }
-    return TraversalContinuation.CONTINUE;
+    return TraversalContinuation.doContinue();
   }
 
   @Override
@@ -128,9 +128,9 @@
         method -> {
           if (predicate.test(method)) {
             found.set(method);
-            return TraversalContinuation.BREAK;
+            return TraversalContinuation.doBreak();
           }
-          return TraversalContinuation.CONTINUE;
+          return TraversalContinuation.doContinue();
         });
     return found.get();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index bcad963..f1a3bda 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -174,19 +174,19 @@
         type,
         clazz -> {
           onClass.accept(clazz);
-          return TraversalContinuation.CONTINUE;
+          return TraversalContinuation.doContinue();
         },
         lambda -> {
           onLambda.accept(lambda);
-          return TraversalContinuation.CONTINUE;
+          return TraversalContinuation.doContinue();
         },
         appInfo);
   }
 
-  public TraversalContinuation traverseInstantiatedSubtypes(
+  public TraversalContinuation<?> traverseInstantiatedSubtypes(
       DexType type,
-      Function<DexProgramClass, TraversalContinuation> onClass,
-      Function<LambdaDescriptor, TraversalContinuation> onLambda,
+      Function<DexProgramClass, TraversalContinuation<?>> onClass,
+      Function<LambdaDescriptor, TraversalContinuation<?>> onLambda,
       AppInfo appInfo) {
     WorkList<DexClass> worklist = WorkList.newIdentityWorkList();
     if (type == appInfo.dexItemFactory().objectType) {
@@ -208,7 +208,7 @@
         for (LambdaDescriptor lambda :
             instantiatedLambdas.getOrDefault(type, Collections.emptyList())) {
           if (onLambda.apply(lambda).shouldBreak()) {
-            return TraversalContinuation.BREAK;
+            return TraversalContinuation.doBreak();
           }
         }
       } else {
@@ -223,7 +223,7 @@
         if (isInstantiatedDirectly(programClass)
             || isInterfaceWithUnknownSubtypeHierarchy(programClass)) {
           if (onClass.apply(programClass).shouldBreak()) {
-            return TraversalContinuation.BREAK;
+            return TraversalContinuation.doBreak();
           }
         }
       }
@@ -231,11 +231,11 @@
       for (LambdaDescriptor lambda :
           instantiatedLambdas.getOrDefault(clazz.type, Collections.emptyList())) {
         if (onLambda.apply(lambda).shouldBreak()) {
-          return TraversalContinuation.BREAK;
+          return TraversalContinuation.doBreak();
         }
       }
     }
-    return TraversalContinuation.CONTINUE;
+    return TraversalContinuation.doContinue();
   }
 
   public Set<DexType> getInstantiatedLambdaInterfaces() {
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 1ce97e6..919d940 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -15,7 +15,7 @@
   private final AppView<?> appView;
   private final T context;
 
-  private TraversalContinuation continuation = TraversalContinuation.CONTINUE;
+  private TraversalContinuation<?> continuation = TraversalContinuation.doContinue();
 
   public enum MethodHandleUse {
     ARGUMENT_TO_LAMBDA_METAFACTORY,
@@ -37,7 +37,7 @@
 
   public void doBreak() {
     assert continuation.shouldContinue();
-    continuation = TraversalContinuation.BREAK;
+    continuation = TraversalContinuation.doBreak();
   }
 
   public GraphLens getCodeLens() {
@@ -54,7 +54,7 @@
     return context.asMethod();
   }
 
-  public TraversalContinuation getTraversalContinuation() {
+  public TraversalContinuation<?> getTraversalContinuation() {
     return continuation;
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 70d534e..c0cb840 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -326,7 +326,7 @@
               (superType, subclass, isInterface) -> {
                 assert superType != interfaceClass.getType()
                     : "Interface " + interfaceClass.getTypeName() + " inherits from itself";
-                return TraversalContinuation.CONTINUE;
+                return TraversalContinuation.doContinue();
               });
     }
     return true;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
index e8eb14e..79d2a51 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
@@ -208,9 +208,9 @@
             (supertype, superclass, immediateSubclass) -> {
               if (superclass != null && superclass.isProgramClass()) {
                 superclasses.add(superclass.asProgramClass());
-                return TraversalContinuation.CONTINUE;
+                return TraversalContinuation.doContinue();
               }
-              return TraversalContinuation.BREAK;
+              return TraversalContinuation.doBreak();
             });
 
     // Run the tracer from the class initializers of the superclasses.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index 9336e2b..1573dde 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -65,16 +65,16 @@
     // Check that all accesses from [clazz] to classes or members from the current package of
     // [clazz] will continue to work. This is guaranteed if the methods of [clazz] do not access
     // any private or protected classes or members from the current package of [clazz].
-    TraversalContinuation result =
+    TraversalContinuation<?> result =
         clazz.traverseProgramMethods(
             method -> {
               boolean foundIllegalAccess =
                   method.registerCodeReferencesWithResult(
                       new IllegalAccessDetector(appView, method));
               if (foundIllegalAccess) {
-                return TraversalContinuation.BREAK;
+                return TraversalContinuation.doBreak();
               }
-              return TraversalContinuation.CONTINUE;
+              return TraversalContinuation.doContinue();
             });
     return result.shouldBreak();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
index ddbe46b..572b153 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
@@ -7,8 +7,8 @@
 import static com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query.DIRECTLY;
 import static com.android.tools.r8.ir.optimize.SimpleDominatingEffectAnalysis.InstructionEffect.NO_EFFECT;
 import static com.android.tools.r8.ir.optimize.SimpleDominatingEffectAnalysis.InstructionEffect.OTHER_EFFECT;
-import static com.android.tools.r8.utils.TraversalContinuation.BREAK;
-import static com.android.tools.r8.utils.TraversalContinuation.CONTINUE;
+import static com.android.tools.r8.utils.TraversalContinuation.doBreak;
+import static com.android.tools.r8.utils.TraversalContinuation.doContinue;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -249,7 +249,7 @@
     new StatefulDepthFirstSearchWorkList<BasicBlock, ResultStateWithPartialBlocks>() {
 
       @Override
-      protected TraversalContinuation process(
+      protected TraversalContinuation<?> process(
           DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks> node,
           Function<BasicBlock, DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks>>
               childNodeConsumer) {
@@ -257,7 +257,7 @@
         for (Instruction instruction : node.getNode().getInstructions()) {
           if (visitedInstructions.getAndIncrement() > analysis.maxNumberOfInstructions()) {
             builder.fail();
-            return BREAK;
+            return doBreak();
           }
           effect = analysis.analyze(instruction);
           if (!effect.isNoEffect()) {
@@ -276,16 +276,16 @@
               // If we see a block where the children have not been processed we cannot guarantee
               // all paths having the effect since - ex. we could have a non-terminating loop.
               builder.fail();
-              return BREAK;
+              return doBreak();
             }
           }
         }
         node.setState(new ResultStateWithPartialBlocks(effect.toResultState(), ImmutableList.of()));
-        return CONTINUE;
+        return doContinue();
       }
 
       @Override
-      protected TraversalContinuation joiner(
+      protected TraversalContinuation<?> joiner(
           DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks> node,
           List<DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks>> childNodes) {
         ResultStateWithPartialBlocks resultState = node.getState();
@@ -300,7 +300,7 @@
           builder.setResult(resultState.state);
           builder.setFailingBlocksForPartialResults(resultState.failingBlocks);
         }
-        return CONTINUE;
+        return doContinue();
       }
     }.run(code.entryBlock());
 
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index 339f93d..618e1f5 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -180,7 +180,7 @@
                   reservedNamingState.add(reservationState);
                 }
               }
-              return TraversalContinuation.CONTINUE;
+              return TraversalContinuation.doContinue();
             });
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index d601873..7081da1 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1126,9 +1126,9 @@
         .traverseProgramMembers(
             member -> {
               if (keepInfo.getInfo(member).isRepackagingAllowed(member, options())) {
-                return TraversalContinuation.CONTINUE;
+                return TraversalContinuation.doContinue();
               }
-              return TraversalContinuation.BREAK;
+              return TraversalContinuation.doBreak();
             })
         .shouldContinue();
   }
@@ -1574,20 +1574,20 @@
             type,
             clazz -> {
               if (objectAllocationInfoCollection.isInterfaceWithUnknownSubtypeHierarchy(clazz)) {
-                return TraversalContinuation.BREAK;
+                return TraversalContinuation.doBreak();
               } else {
                 SingleResolutionResult resolution =
                     resolveMethodOn(clazz, dexItemFactory().objectMembers.finalize)
                         .asSingleResolution();
                 if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
-                  return TraversalContinuation.BREAK;
+                  return TraversalContinuation.doBreak();
                 }
               }
-              return TraversalContinuation.CONTINUE;
+              return TraversalContinuation.doContinue();
             },
             lambda -> {
               // Lambda classes do not have finalizers.
-              return TraversalContinuation.CONTINUE;
+              return TraversalContinuation.doContinue();
             },
             this)
         .shouldBreak();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index 8ffaa8e..ffc2149 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -114,18 +114,18 @@
         });
   }
 
-  public abstract TraversalContinuation traverseTypeMatchers(
-      Function<ProguardTypeMatcher, TraversalContinuation> fn);
+  public abstract TraversalContinuation<?> traverseTypeMatchers(
+      Function<ProguardTypeMatcher, TraversalContinuation<?>> fn);
 
-  public final TraversalContinuation traverseTypeMatchers(
-      Function<ProguardTypeMatcher, TraversalContinuation> fn,
+  public final TraversalContinuation<?> traverseTypeMatchers(
+      Function<ProguardTypeMatcher, TraversalContinuation<?>> fn,
       Predicate<ProguardTypeMatcher> predicate) {
     return traverseTypeMatchers(
         matcher -> {
           if (predicate.test(matcher)) {
             return fn.apply(matcher);
           }
-          return TraversalContinuation.CONTINUE;
+          return TraversalContinuation.doContinue();
         });
   }
 
@@ -168,9 +168,9 @@
     }
 
     @Override
-    public TraversalContinuation traverseTypeMatchers(
-        Function<ProguardTypeMatcher, TraversalContinuation> fn) {
-      return TraversalContinuation.CONTINUE;
+    public TraversalContinuation<?> traverseTypeMatchers(
+        Function<ProguardTypeMatcher, TraversalContinuation<?>> fn) {
+      return TraversalContinuation.doContinue();
     }
   }
 
@@ -236,8 +236,8 @@
     }
 
     @Override
-    public TraversalContinuation traverseTypeMatchers(
-        Function<ProguardTypeMatcher, TraversalContinuation> fn) {
+    public TraversalContinuation<?> traverseTypeMatchers(
+        Function<ProguardTypeMatcher, TraversalContinuation<?>> fn) {
       return fn.apply(className);
     }
   }
@@ -320,14 +320,14 @@
     }
 
     @Override
-    public TraversalContinuation traverseTypeMatchers(
-        Function<ProguardTypeMatcher, TraversalContinuation> fn) {
+    public TraversalContinuation<?> traverseTypeMatchers(
+        Function<ProguardTypeMatcher, TraversalContinuation<?>> fn) {
       for (ProguardTypeMatcher matcher : classNames) {
         if (fn.apply(matcher).shouldBreak()) {
-          return TraversalContinuation.BREAK;
+          return TraversalContinuation.doBreak();
         }
       }
-      return TraversalContinuation.CONTINUE;
+      return TraversalContinuation.doContinue();
     }
   }
 
@@ -416,14 +416,14 @@
     }
 
     @Override
-    public TraversalContinuation traverseTypeMatchers(
-        Function<ProguardTypeMatcher, TraversalContinuation> fn) {
+    public TraversalContinuation<?> traverseTypeMatchers(
+        Function<ProguardTypeMatcher, TraversalContinuation<?>> fn) {
       for (ProguardTypeMatcher matcher : classNames.keySet()) {
         if (fn.apply(matcher).shouldBreak()) {
-          return TraversalContinuation.BREAK;
+          return TraversalContinuation.doBreak();
         }
       }
-      return TraversalContinuation.CONTINUE;
+      return TraversalContinuation.doContinue();
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 95ec8c3..524fe08 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -1403,7 +1403,7 @@
           dependentMinimumKeepInfo.getUnconditionalMinimumKeepInfoOrDefault(
               MinimumKeepInfoCollection.empty());
       for (DexProgramClass clazz : classesWithCheckDiscardedMembers) {
-        TraversalContinuation continueIfAllMembersMarkedAsCheckDiscarded =
+        TraversalContinuation<?> continueIfAllMembersMarkedAsCheckDiscarded =
             clazz.traverseProgramMembers(
                 member ->
                     TraversalContinuation.continueIf(
diff --git a/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java b/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
index df9803a..33b7d4e 100644
--- a/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
+++ b/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
@@ -38,9 +38,9 @@
                 (superType, subclass, ignore) -> {
                   if (seen.add(superType)) {
                     cache.remove(superType);
-                    return TraversalContinuation.CONTINUE;
+                    return TraversalContinuation.doContinue();
                   } else {
-                    return TraversalContinuation.BREAK;
+                    return TraversalContinuation.doBreak();
                   }
                 }),
         lambda -> {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 14a84d5..6cc5f84 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -417,7 +417,7 @@
     // If there is a constructor in the target, make sure that all source constructors can be
     // inlined.
     if (!Iterables.isEmpty(targetClass.programInstanceInitializers())) {
-      TraversalContinuation result =
+      TraversalContinuation<?> result =
           sourceClass.traverseProgramInstanceInitializers(
               method -> {
                 AbortReason reason = disallowInlining(method, targetClass);
@@ -426,9 +426,9 @@
                   if (Log.ENABLED) {
                     reason.printLogMessageForClass(sourceClass);
                   }
-                  return TraversalContinuation.BREAK;
+                  return TraversalContinuation.doBreak();
                 }
-                return TraversalContinuation.CONTINUE;
+                return TraversalContinuation.doContinue();
               });
       if (result.shouldBreak()) {
         return false;
@@ -454,7 +454,7 @@
     // If there is an invoke-special to a default interface method and we are not merging into an
     // interface, then abort, since invoke-special to a virtual class method requires desugaring.
     if (sourceClass.isInterface() && !targetClass.isInterface()) {
-      TraversalContinuation result =
+      TraversalContinuation<?> result =
           sourceClass.traverseProgramMethods(
               method -> {
                 boolean foundInvokeSpecialToDefaultLibraryMethod =
@@ -586,16 +586,16 @@
     // Check that all accesses from [source] to classes or members from the current package of
     // [source] will continue to work. This is guaranteed if the methods of [source] do not access
     // any private or protected classes or members from the current package of [source].
-    TraversalContinuation result =
+    TraversalContinuation<?> result =
         source.traverseProgramMethods(
             method -> {
               boolean foundIllegalAccess =
                   method.registerCodeReferencesWithResult(
                       new IllegalAccessDetector(appView, method));
               if (foundIllegalAccess) {
-                return TraversalContinuation.BREAK;
+                return TraversalContinuation.doBreak();
               }
-              return TraversalContinuation.CONTINUE;
+              return TraversalContinuation.doContinue();
             });
     return result.shouldBreak();
   }
diff --git a/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java b/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java
index c82f332..6b5df9b 100644
--- a/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java
+++ b/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java
@@ -107,10 +107,10 @@
   abstract T createDfsNode(N node);
 
   /** The initial processing of a node during forward search */
-  abstract TraversalContinuation internalOnVisit(T node);
+  abstract TraversalContinuation<?> internalOnVisit(T node);
 
   /** The joining of state during backtracking of the algorithm. */
-  abstract TraversalContinuation internalOnJoin(T node);
+  abstract TraversalContinuation<?> internalOnJoin(T node);
 
   final T internalEnqueueNode(N value) {
     T dfsNode = createDfsNode(value);
@@ -121,13 +121,13 @@
   }
 
   @SafeVarargs
-  public final TraversalContinuation run(N... roots) {
+  public final TraversalContinuation<?> run(N... roots) {
     return run(Arrays.asList(roots));
   }
 
-  public final TraversalContinuation run(Collection<N> roots) {
+  public final TraversalContinuation<?> run(Collection<N> roots) {
     roots.forEach(this::internalEnqueueNode);
-    TraversalContinuation continuation = TraversalContinuation.CONTINUE;
+    TraversalContinuation<?> continuation = TraversalContinuation.doContinue();
     while (!workList.isEmpty()) {
       T node = workList.removeLast();
       if (node.isFinished()) {
@@ -161,7 +161,7 @@
      *     before but not finished there is a cycle.
      * @return A value describing if the DFS algorithm should continue to run.
      */
-    protected abstract TraversalContinuation process(
+    protected abstract TraversalContinuation<?> process(
         DFSNode<N> node, Function<N, DFSNode<N>> childNodeConsumer);
 
     @Override
@@ -170,13 +170,13 @@
     }
 
     @Override
-    TraversalContinuation internalOnVisit(DFSNodeImpl<N> node) {
+    TraversalContinuation<?> internalOnVisit(DFSNodeImpl<N> node) {
       return process(node, this::internalEnqueueNode);
     }
 
     @Override
-    protected TraversalContinuation internalOnJoin(DFSNodeImpl<N> node) {
-      return TraversalContinuation.CONTINUE;
+    protected TraversalContinuation<?> internalOnJoin(DFSNodeImpl<N> node) {
+      return TraversalContinuation.doContinue();
     }
   }
 
@@ -195,7 +195,7 @@
      *     before but not finished there is a cycle.
      * @return A value describing if the DFS algorithm should continue to run.
      */
-    protected abstract TraversalContinuation process(
+    protected abstract TraversalContinuation<?> process(
         DFSNodeWithState<N, S> node, Function<N, DFSNodeWithState<N, S>> childNodeConsumer);
 
     /**
@@ -205,7 +205,7 @@
      * @param childStates The already computed child states.
      * @return A value describing if the DFS algorithm should continue to run.
      */
-    protected abstract TraversalContinuation joiner(
+    protected abstract TraversalContinuation<?> joiner(
         DFSNodeWithState<N, S> node, List<DFSNodeWithState<N, S>> childStates);
 
     @Override
@@ -214,7 +214,7 @@
     }
 
     @Override
-    TraversalContinuation internalOnVisit(DFSNodeWithStateImpl<N, S> node) {
+    TraversalContinuation<?> internalOnVisit(DFSNodeWithStateImpl<N, S> node) {
       List<DFSNodeWithState<N, S>> childStates = new ArrayList<>();
       List<DFSNodeWithState<N, S>> removedChildStates = childStateMap.put(node, childStates);
       assert removedChildStates == null;
@@ -228,7 +228,7 @@
     }
 
     @Override
-    protected TraversalContinuation internalOnJoin(DFSNodeWithStateImpl<N, S> node) {
+    protected TraversalContinuation<?> internalOnJoin(DFSNodeWithStateImpl<N, S> node) {
       return joiner(
           node,
           childStateMap.computeIfAbsent(
diff --git a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
index 0e9d873..5bd242f 100644
--- a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
+++ b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
@@ -3,25 +3,87 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.errors.Unreachable;
+
 /** Two value continuation value to indicate the continuation of a loop/traversal. */
 /* This class is used for building up api class member traversals. */
-public enum TraversalContinuation {
-  CONTINUE,
-  BREAK;
+public abstract class TraversalContinuation<T> {
 
-  public static TraversalContinuation breakIf(boolean condition) {
+  public boolean isBreak() {
+    return !isContinue();
+  }
+
+  public boolean isContinue() {
+    return false;
+  }
+
+  public Break<T> asBreak() {
+    return null;
+  }
+
+  public static final class Continue<T> extends TraversalContinuation<T> {
+    private static final TraversalContinuation<?> CONTINUE = new Continue<Object>();
+
+    private Continue() {}
+
+    @Override
+    public boolean isContinue() {
+      return true;
+    }
+  }
+
+  public static class Break<T> extends TraversalContinuation<T> {
+    private static final TraversalContinuation<?> BREAK_NO_VALUE =
+        new Break<Object>(null) {
+          @Override
+          public Object getValue() {
+            return new Unreachable(
+                "Invalid attempt at getting a value from a no-value break state.");
+          }
+        };
+
+    private final T value;
+
+    private Break(T value) {
+      this.value = value;
+    }
+
+    public T getValue() {
+      return value;
+    }
+
+    @Override
+    public Break<T> asBreak() {
+      return this;
+    }
+  }
+
+  public static TraversalContinuation<?> breakIf(boolean condition) {
     return continueIf(!condition);
   }
 
-  public static TraversalContinuation continueIf(boolean condition) {
-    return condition ? CONTINUE : BREAK;
+  public static TraversalContinuation<?> continueIf(boolean condition) {
+    return condition ? doContinue() : doBreak();
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> TraversalContinuation<T> doContinue() {
+    return (TraversalContinuation<T>) Continue.CONTINUE;
+  }
+
+  public static TraversalContinuation<?> doBreak() {
+    return Break.BREAK_NO_VALUE;
+  }
+
+  public static <T> TraversalContinuation<T> doBreak(T value) {
+    return new Break<>(value);
   }
 
   public final boolean shouldBreak() {
-    return this == BREAK;
+    return isBreak();
   }
 
   public final boolean shouldContinue() {
-    return this == CONTINUE;
+    return isContinue();
   }
 }